mirror of
https://github.com/pfzetto/rebacs
synced 2024-11-23 19:52:50 +01:00
new backend
This commit is contained in:
parent
dbd581cece
commit
1b53e041e3
7 changed files with 1073 additions and 381 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -145,6 +145,15 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "castaway"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
|
@ -157,6 +166,19 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "compact_str"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bff0805f79ecb1b35163f3957a6934ea8d04fcd36ef98b52e7316f63e72e73d1"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"cfg-if",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
|
@ -902,6 +924,12 @@ version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -989,6 +1017,12 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -1043,6 +1077,7 @@ dependencies = [
|
||||||
name = "themis"
|
name = "themis"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"compact_str",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"hex",
|
"hex",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -15,6 +15,7 @@ tonic = { version="0.9.2", features=["tls"] }
|
||||||
prost = "0.11.9"
|
prost = "0.11.9"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.6"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
compact_str = "0.7.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.9.2"
|
tonic-build = "0.9.2"
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package eu.zettoit.themis;
|
package eu.zettoit.themis;
|
||||||
|
|
||||||
service ObjectService{
|
|
||||||
rpc Create(Object) returns (Empty);
|
|
||||||
rpc Delete(Object) returns (Empty);
|
|
||||||
rpc Exists(Object) returns (ExistsResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
service RelationService {
|
service RelationService {
|
||||||
rpc Create(Relation) returns (Empty);
|
rpc Create(Relation) returns (Empty);
|
||||||
rpc Delete(Relation) returns (Empty);
|
rpc Delete(Relation) returns (Empty);
|
||||||
|
|
387
src/graph.rs
387
src/graph.rs
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
collections::{
|
collections::{
|
||||||
hash_map::{Iter, IterMut},
|
hash_map::{Iter, IterMut},
|
||||||
BinaryHeap, HashMap, HashSet,
|
BinaryHeap, HashMap, HashSet,
|
||||||
|
@ -33,12 +34,12 @@ pub struct ObjectRef(pub u32);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)]
|
||||||
pub enum ObjectOrSet {
|
pub enum ObjectOrSet {
|
||||||
Object(ObjectRef),
|
Object(Object),
|
||||||
Set((ObjectRef, Relation)),
|
Set((Object, Relation)),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Relation(String);
|
pub struct Relation(pub String);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Hash, Serialize, Deserialize, Debug)]
|
#[derive(PartialEq, Eq, Clone, Hash, Serialize, Deserialize, Debug)]
|
||||||
pub struct ObjectRelation(pub ObjectRef, pub Relation);
|
pub struct ObjectRelation(pub ObjectRef, pub Relation);
|
||||||
|
@ -53,7 +54,7 @@ impl Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectOrSet {
|
impl ObjectOrSet {
|
||||||
pub fn object_ref(&self) -> &ObjectRef {
|
pub fn object(&self) -> &Object {
|
||||||
match self {
|
match self {
|
||||||
ObjectOrSet::Object(obj) => obj,
|
ObjectOrSet::Object(obj) => obj,
|
||||||
ObjectOrSet::Set((obj, _)) => obj,
|
ObjectOrSet::Set((obj, _)) => obj,
|
||||||
|
@ -67,14 +68,14 @@ impl ObjectOrSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ObjectRef> for ObjectOrSet {
|
impl From<Object> for ObjectOrSet {
|
||||||
fn from(value: ObjectRef) -> Self {
|
fn from(value: Object) -> Self {
|
||||||
Self::Object(value)
|
Self::Object(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(ObjectRef, &str)> for ObjectOrSet {
|
impl From<(Object, &str)> for ObjectOrSet {
|
||||||
fn from(value: (ObjectRef, &str)) -> Self {
|
fn from(value: (Object, &str)) -> Self {
|
||||||
Self::Set((value.0, Relation::new(value.1)))
|
Self::Set((value.0, Relation::new(value.1)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +103,27 @@ impl From<(ObjectRef, &str)> for ObjectRelation {
|
||||||
Self(value.0, Relation::new(value.1))
|
Self(value.0, Relation::new(value.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<(&str, &str)> for Object {
|
||||||
|
fn from((namespace, id): (&str, &str)) -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: namespace.to_string(),
|
||||||
|
id: id.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(&String, &String)> for Object {
|
||||||
|
fn from((namespace, id): (&String, &String)) -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: namespace.to_string(),
|
||||||
|
id: id.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(String, String)> for Object {
|
||||||
|
fn from((namespace, id): (String, String)) -> Self {
|
||||||
|
Self { namespace, id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Graph {
|
impl Graph {
|
||||||
pub fn get_node(&self, namespace: &str, id: &str) -> Option<ObjectRef> {
|
pub fn get_node(&self, namespace: &str, id: &str) -> Option<ObjectRef> {
|
||||||
|
@ -110,29 +132,86 @@ impl Graph {
|
||||||
pub fn object_from_ref(&self, obj: &ObjectRef) -> Object {
|
pub fn object_from_ref(&self, obj: &ObjectRef) -> Object {
|
||||||
self.nodes.get_by_b(obj).unwrap().clone()
|
self.nodes.get_by_b(obj).unwrap().clone()
|
||||||
}
|
}
|
||||||
pub fn add_node(&mut self, node: Object) -> ObjectRef {
|
pub fn get_or_add_node(&mut self, namespace: &str, id: &str) -> ObjectRef {
|
||||||
|
if let Some(node) = self.get_node(namespace, id) {
|
||||||
|
node
|
||||||
|
} else {
|
||||||
|
self.add_node((namespace, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_node(&mut self, node: impl Into<Object>) -> ObjectRef {
|
||||||
let obj_ref = ObjectRef(self.counter);
|
let obj_ref = ObjectRef(self.counter);
|
||||||
self.nodes.insert(node, obj_ref);
|
self.nodes.insert(node.into(), obj_ref);
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
obj_ref
|
obj_ref
|
||||||
}
|
}
|
||||||
pub fn remove_node(&mut self, node: Object) {
|
pub fn remove_node(&mut self, node: impl Into<Object>) {
|
||||||
|
let node = node.into();
|
||||||
let index = self.nodes.remove_by_a(&node);
|
let index = self.nodes.remove_by_a(&node);
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
self.edges.remove_by_c(&index);
|
self.edges.remove_by_c(&index);
|
||||||
self.edges.get_by_a(&ObjectOrSet::Object(*index));
|
//self.edges.get_by_a(&ObjectOrSet::Object(*index));
|
||||||
//TODO: remove edges with ObjectOrSet::Set
|
//TODO: remove edges with ObjectOrSet::Set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn remove_node_by_ref(&mut self, node: impl Into<ObjectRef>) {
|
||||||
|
let node = node.into();
|
||||||
|
let index = self.nodes.remove_by_b(&node);
|
||||||
|
if index.is_some() {
|
||||||
|
self.edges.remove_by_c(&node);
|
||||||
|
|
||||||
|
//let edges = self
|
||||||
|
// .edges
|
||||||
|
// .left_to_right
|
||||||
|
// .keys()
|
||||||
|
// .filter(|x| *x.object_ref() == node)
|
||||||
|
// .map(|x| (**x).clone())
|
||||||
|
// .collect::<Vec<ObjectOrSet>>();
|
||||||
|
//for edge in edges {
|
||||||
|
// self.edges.remove_by_a(&edge);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_relation(&self, src: ObjectOrSet, dst: ObjectRelation) -> bool {
|
pub fn has_relation(&self, src: ObjectOrSet, dst: ObjectRelation) -> bool {
|
||||||
self.edges.has(&src, &dst.1, &dst.0)
|
self.edges.has(&src, &dst.1, &dst.0)
|
||||||
}
|
}
|
||||||
pub fn add_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) {
|
pub fn add_relation(&mut self, src: impl Into<ObjectOrSet>, dst: impl Into<ObjectRelation>) {
|
||||||
self.edges.insert(src, dst.1, dst.0);
|
let dst = dst.into();
|
||||||
|
self.edges.insert(src.into(), dst.1, dst.0);
|
||||||
}
|
}
|
||||||
pub fn remove_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) {
|
pub fn remove_relation(&mut self, src: impl Into<ObjectOrSet>, dst: impl Into<ObjectRelation>) {
|
||||||
|
let dst = dst.into();
|
||||||
|
self.edges.remove(&src.into(), &dst.1, &dst.0);
|
||||||
|
}
|
||||||
|
pub fn remove_relation_and_residual_node(
|
||||||
|
&mut self,
|
||||||
|
src: impl Into<ObjectOrSet>,
|
||||||
|
dst: impl Into<ObjectRelation>,
|
||||||
|
) {
|
||||||
|
let src = src.into();
|
||||||
|
let dst = dst.into();
|
||||||
self.edges.remove(&src, &dst.1, &dst.0);
|
self.edges.remove(&src, &dst.1, &dst.0);
|
||||||
|
|
||||||
|
//if self.edges.get_by_c(src.object_ref()).is_empty()
|
||||||
|
// && !self
|
||||||
|
// .edges
|
||||||
|
// .left_to_right
|
||||||
|
// .keys()
|
||||||
|
// .any(|x| x.object_ref() == src.object_ref())
|
||||||
|
//{
|
||||||
|
// self.remove_node_by_ref(*src.object_ref());
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if self.edges.get_by_c(&dst.0).is_empty()
|
||||||
|
// && !self
|
||||||
|
// .edges
|
||||||
|
// .left_to_right
|
||||||
|
// .keys()
|
||||||
|
// .any(|x| *x.object_ref() == dst.0)
|
||||||
|
//{
|
||||||
|
// self.remove_node_by_ref(dst.0);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_related_to(
|
pub fn is_related_to(
|
||||||
|
@ -158,87 +237,88 @@ impl Graph {
|
||||||
q.push(ObjectRelationDist(1, neighbor.clone()));
|
q.push(ObjectRelationDist(1, neighbor.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(ObjectRelationDist(node_dist, node)) = q.pop() {
|
//while let Some(ObjectRelationDist(node_dist, node)) = q.pop() {
|
||||||
let node_dist = node_dist + 1;
|
// let node_dist = node_dist + 1;
|
||||||
let node = ObjectOrSet::Set((node.0, node.1));
|
// let node = ObjectOrSet::Set((node.0, node.1));
|
||||||
for neighbor in self
|
// for neighbor in self
|
||||||
.edges
|
// .edges
|
||||||
.get_by_a(&node)
|
// .get_by_a(&node)
|
||||||
.iter()
|
// .iter()
|
||||||
.flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone())))
|
// .flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone())))
|
||||||
{
|
// {
|
||||||
if neighbor == dst {
|
// if neighbor == dst {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
if let Some(existing_node_dist) = dist.get(&neighbor) {
|
// if let Some(existing_node_dist) = dist.get(&neighbor) {
|
||||||
if *existing_node_dist < node_dist {
|
// if *existing_node_dist < node_dist {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
dist.insert(neighbor.clone(), node_dist);
|
// dist.insert(neighbor.clone(), node_dist);
|
||||||
q.push(ObjectRelationDist(node_dist, neighbor.clone()));
|
// q.push(ObjectRelationDist(node_dist, neighbor.clone()));
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
pub fn related_to(&self, dst: ObjectRef, relation: Relation) -> HashSet<ObjectRef> {
|
pub fn related_to(&self, dst: ObjectRef, relation: Relation) -> HashSet<ObjectRef> {
|
||||||
let mut relation_sets = vec![];
|
//let mut relation_sets = vec![];
|
||||||
let mut relations: HashSet<ObjectRef> = HashSet::new();
|
//let mut relations: HashSet<ObjectRef> = HashSet::new();
|
||||||
for obj in self.edges.get_by_cb(&dst, &relation) {
|
//for obj in self.edges.get_by_cb(&dst, &relation) {
|
||||||
match obj {
|
// match obj {
|
||||||
ObjectOrSet::Object(obj) => {
|
// ObjectOrSet::Object(obj) => {
|
||||||
relations.insert(*obj);
|
// relations.insert(*obj);
|
||||||
}
|
// }
|
||||||
ObjectOrSet::Set(set) => relation_sets.push(set),
|
// ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
while let Some(set) = relation_sets.pop() {
|
//while let Some(set) = relation_sets.pop() {
|
||||||
for obj in self.edges.get_by_cb(&set.0, &set.1) {
|
// for obj in self.edges.get_by_cb(&set.0, &set.1) {
|
||||||
match obj {
|
// match obj {
|
||||||
ObjectOrSet::Object(obj) => {
|
// ObjectOrSet::Object(obj) => {
|
||||||
relations.insert(*obj);
|
// relations.insert(*obj);
|
||||||
}
|
// }
|
||||||
ObjectOrSet::Set(set) => relation_sets.push(set),
|
// ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
relations
|
//relations
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
pub fn relations(&self, src: impl Into<ObjectRelation>) -> HashSet<ObjectRef> {
|
pub fn relations(&self, src: impl Into<ObjectRelation>) -> HashSet<ObjectRef> {
|
||||||
let src: ObjectRelation = src.into();
|
//let src: ObjectRelation = src.into();
|
||||||
|
|
||||||
let mut visited = HashSet::new();
|
//let mut visited = HashSet::new();
|
||||||
let mut relation_sets = vec![];
|
//let mut relation_sets = vec![];
|
||||||
let mut relations = HashSet::new();
|
//let mut relations = HashSet::new();
|
||||||
|
|
||||||
for (rel, neighbors) in self.edges.get_by_a(&ObjectOrSet::Object(src.0)) {
|
//for (rel, neighbors) in self.edges.get_by_a(&ObjectOrSet::Object(src.0)) {
|
||||||
for neighbor in neighbors {
|
// for neighbor in neighbors {
|
||||||
if *rel == src.1 {
|
// if *rel == src.1 {
|
||||||
relations.insert(*neighbor);
|
// relations.insert(*neighbor);
|
||||||
}
|
// }
|
||||||
relation_sets.push((rel, neighbor));
|
// relation_sets.push((rel, neighbor));
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
while let Some((rel, obj_ref)) = relation_sets.pop() {
|
//while let Some((rel, obj_ref)) = relation_sets.pop() {
|
||||||
if !visited.contains(&(rel, obj_ref)) {
|
// if !visited.contains(&(rel, obj_ref)) {
|
||||||
for (rel, neighbors) in self
|
// for (rel, neighbors) in self
|
||||||
.edges
|
// .edges
|
||||||
.get_by_a(&ObjectOrSet::Set((*obj_ref, (*rel).clone())))
|
// .get_by_a(&ObjectOrSet::Set((*obj_ref, (*rel).clone())))
|
||||||
{
|
// {
|
||||||
for neighbor in neighbors {
|
// for neighbor in neighbors {
|
||||||
if *rel == src.1 {
|
// if *rel == src.1 {
|
||||||
relations.insert(*neighbor);
|
// relations.insert(*neighbor);
|
||||||
}
|
// }
|
||||||
relation_sets.push((rel, neighbor));
|
// relation_sets.push((rel, neighbor));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
visited.insert((rel, obj_ref));
|
// visited.insert((rel, obj_ref));
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//relations
|
||||||
relations
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn to_file(&self, file: &mut File) {
|
pub async fn to_file(&self, file: &mut File) {
|
||||||
|
@ -247,33 +327,33 @@ impl Graph {
|
||||||
file.write_all(format!("[{}:{}]\n", &obj.namespace, &obj.id).as_bytes())
|
file.write_all(format!("[{}:{}]\n", &obj.namespace, &obj.id).as_bytes())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for (rel, arr) in self.edges.get_by_c(obj_ref.as_ref()) {
|
//for (rel, arr) in self.edges.get_by_c(obj_ref.as_ref()) {
|
||||||
let arr = arr
|
// let arr = arr
|
||||||
.iter()
|
// .iter()
|
||||||
.filter_map(|x| {
|
// .filter_map(|x| {
|
||||||
let rel_obj_ref = x.object_ref();
|
// let rel_obj_ref = x.object_ref();
|
||||||
self.nodes.get_by_b(rel_obj_ref).map(|rel_obj| {
|
// self.nodes.get_by_b(rel_obj_ref).map(|rel_obj| {
|
||||||
let (namespace, id) = (&rel_obj.namespace, &rel_obj.id);
|
// let (namespace, id) = (&rel_obj.namespace, &rel_obj.id);
|
||||||
|
|
||||||
if *namespace == obj.namespace && *id == obj.id {
|
// if *namespace == obj.namespace && *id == obj.id {
|
||||||
match x.relation() {
|
// match x.relation() {
|
||||||
None => "self".to_string(),
|
// None => "self".to_string(),
|
||||||
Some(rel) => format!("self#{}", &rel.0),
|
// Some(rel) => format!("self#{}", &rel.0),
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
match x.relation() {
|
// match x.relation() {
|
||||||
None => format!("{}:{}", &namespace, &id),
|
// None => format!("{}:{}", &namespace, &id),
|
||||||
Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0),
|
// Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
.reduce(|acc, e| acc + ", " + &e)
|
// .reduce(|acc, e| acc + ", " + &e)
|
||||||
.unwrap_or_default();
|
// .unwrap_or_default();
|
||||||
file.write_all(format!("{} = [{}]\n", &rel.0, &arr).as_bytes())
|
// file.write_all(format!("{} = [{}]\n", &rel.0, &arr).as_bytes())
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
}
|
//}
|
||||||
file.write_all("\n".as_bytes()).await.unwrap();
|
file.write_all("\n".as_bytes()).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,19 +419,19 @@ impl Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for relation in relations {
|
//for relation in relations {
|
||||||
let src = match relation.2 {
|
// let src = match relation.2 {
|
||||||
Some(rel) => {
|
// Some(rel) => {
|
||||||
let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
// let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||||
ObjectOrSet::Set((obj, Relation::new(&rel)))
|
// ObjectOrSet::Set((obj, Relation::new(&rel)))
|
||||||
}
|
// }
|
||||||
None => {
|
// None => {
|
||||||
let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
// let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||||
ObjectOrSet::Object(obj)
|
// ObjectOrSet::Object(obj)
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
graph.add_relation(src, ObjectRelation(relation.3, Relation(relation.4)));
|
// graph.add_relation(src, ObjectRelation(relation.3, Relation(relation.4)));
|
||||||
}
|
//}
|
||||||
|
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
@ -598,3 +678,58 @@ impl<A, B, C> Default for BidThreeMap<A, B, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//impl Ord for ObjectOrSet {
|
||||||
|
// fn cmp(&self, other: &Self) -> Ordering {}
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//impl Ord for ObjectRef {}cmp
|
||||||
|
|
||||||
|
impl PartialOrd for Relation {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
self.0.partial_cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for Relation {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.0.cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for ObjectOrSet {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match (
|
||||||
|
self.object().partial_cmp(other.object()),
|
||||||
|
self.relation(),
|
||||||
|
other.relation(),
|
||||||
|
) {
|
||||||
|
(Some(Ordering::Equal), Some(self_rel), Some(other_rel)) => {
|
||||||
|
self_rel.partial_cmp(other_rel)
|
||||||
|
}
|
||||||
|
(ord, _, _) => ord,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for ObjectOrSet {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.object()
|
||||||
|
.cmp(other.object())
|
||||||
|
.then(self.relation().cmp(&other.relation()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Object {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match self.namespace.partial_cmp(&other.namespace) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => self.id.partial_cmp(&other.id),
|
||||||
|
ord => ord,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for Object {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.namespace
|
||||||
|
.cmp(&other.namespace)
|
||||||
|
.then(self.id.cmp(&other.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,125 +8,91 @@ use tokio::sync::Mutex;
|
||||||
use tonic::metadata::MetadataMap;
|
use tonic::metadata::MetadataMap;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
|
|
||||||
use crate::graph::{self, Graph, ObjectRelation};
|
use crate::relation_set::{ObjectOrSet, RelationSet};
|
||||||
use crate::themis_proto::{
|
use crate::themis_proto::{
|
||||||
object_service_server::ObjectService, query_service_server::QueryService, relation::Src,
|
query_service_server::QueryService, relation::Src, relation_service_server::RelationService,
|
||||||
relation_service_server::RelationService, Empty, ExistsResponse, GetRelatedToResponse,
|
Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse,
|
||||||
GetRelationsRequest, GetRelationsResponse, IsRelatedToResponse, Object, Relation, Set,
|
IsRelatedToResponse, Relation, Set,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GraphService {
|
pub struct GraphService {
|
||||||
pub api_keys: Arc<Mutex<HashMap<String, String>>>,
|
pub api_keys: Arc<Mutex<HashMap<String, String>>>,
|
||||||
pub graph: Arc<Mutex<Graph>>,
|
pub graph: Arc<Mutex<RelationSet>>,
|
||||||
pub save_trigger: Sender<()>,
|
pub save_trigger: Sender<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tonic::async_trait]
|
|
||||||
impl ObjectService for GraphService {
|
|
||||||
async fn create(&self, request: Request<Object>) -> Result<Response<Empty>, Status> {
|
|
||||||
let mut graph = self.graph.lock().await;
|
|
||||||
|
|
||||||
authenticate(
|
|
||||||
request.metadata(),
|
|
||||||
&graph,
|
|
||||||
&self.api_keys,
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
"write",
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
|
||||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.add_node(graph::Object::new(
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
&request.get_ref().id,
|
|
||||||
));
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"created object {}:{}",
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
&request.get_ref().id
|
|
||||||
);
|
|
||||||
|
|
||||||
self.save_trigger.send(()).await.unwrap();
|
|
||||||
|
|
||||||
Ok(Response::new(Empty {}))
|
|
||||||
}
|
|
||||||
async fn delete(&self, request: Request<Object>) -> Result<Response<Empty>, Status> {
|
|
||||||
let mut graph = self.graph.lock().await;
|
|
||||||
|
|
||||||
authenticate(
|
|
||||||
request.metadata(),
|
|
||||||
&graph,
|
|
||||||
&self.api_keys,
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
"write",
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
|
||||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.remove_node(graph::Object::new(
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
&request.get_ref().id,
|
|
||||||
));
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"removed object {}:{}",
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
&request.get_ref().id
|
|
||||||
);
|
|
||||||
|
|
||||||
self.save_trigger.send(()).await.unwrap();
|
|
||||||
|
|
||||||
Ok(Response::new(Empty {}))
|
|
||||||
}
|
|
||||||
async fn exists(&self, request: Request<Object>) -> Result<Response<ExistsResponse>, Status> {
|
|
||||||
let graph = self.graph.lock().await;
|
|
||||||
|
|
||||||
authenticate(
|
|
||||||
request.metadata(),
|
|
||||||
&graph,
|
|
||||||
&self.api_keys,
|
|
||||||
&request.get_ref().namespace,
|
|
||||||
"read",
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
|
||||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let exists = graph
|
|
||||||
.get_node(&request.get_ref().namespace, &request.get_ref().id)
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
Ok(Response::new(ExistsResponse { exists }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl RelationService for GraphService {
|
impl RelationService for GraphService {
|
||||||
async fn create(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
async fn create(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||||
let mut graph = self.graph.lock().await;
|
let mut graph = self.graph.lock().await;
|
||||||
|
|
||||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
|
|
||||||
authenticate(
|
let req_src = request
|
||||||
request.metadata(),
|
.get_ref()
|
||||||
&graph,
|
.src
|
||||||
&self.api_keys,
|
.as_ref()
|
||||||
&dst_namespace,
|
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||||
|
let req_dst = request
|
||||||
|
.get_ref()
|
||||||
|
.dst
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||||
|
let req_rel = &request.get_ref().relation;
|
||||||
|
|
||||||
|
if req_rel.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("relation must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.namespace must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !graph.has(
|
||||||
|
("themis_key", &*api_key),
|
||||||
"write",
|
"write",
|
||||||
)
|
("themis_ns", &*req_dst.namespace),
|
||||||
.await?;
|
) {
|
||||||
|
return Err(Status::permission_denied(
|
||||||
|
"missing dst.namespace write permissions",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
graph.add_relation(src, dst);
|
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||||
|
Src::SrcObj(obj) => {
|
||||||
|
if obj.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if obj.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((&*obj.namespace, &*obj.id).into())
|
||||||
|
}
|
||||||
|
Src::SrcSet(set) => {
|
||||||
|
if set.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if set.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
if set.relation.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.relation must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let src = src?;
|
||||||
|
|
||||||
|
graph.insert(
|
||||||
|
src.clone(),
|
||||||
|
req_rel.clone(),
|
||||||
|
(req_dst.namespace.clone(), req_dst.id.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
info!("created relation");
|
info!("created relation");
|
||||||
|
|
||||||
|
@ -137,20 +103,69 @@ impl RelationService for GraphService {
|
||||||
async fn delete(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
async fn delete(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||||
let mut graph = self.graph.lock().await;
|
let mut graph = self.graph.lock().await;
|
||||||
|
|
||||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
|
|
||||||
authenticate(
|
let req_src = request
|
||||||
request.metadata(),
|
.get_ref()
|
||||||
&graph,
|
.src
|
||||||
&self.api_keys,
|
.as_ref()
|
||||||
&dst_namespace,
|
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||||
|
let req_dst = request
|
||||||
|
.get_ref()
|
||||||
|
.dst
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||||
|
let req_rel = &request.get_ref().relation;
|
||||||
|
|
||||||
|
if req_rel.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("relation must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.namespace must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !graph.has(
|
||||||
|
("themis_key", &*api_key),
|
||||||
"write",
|
"write",
|
||||||
)
|
("themis_ns", &*req_dst.namespace),
|
||||||
.await?;
|
) {
|
||||||
|
return Err(Status::permission_denied(
|
||||||
|
"missing dst.namespace write permissions",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||||
|
Src::SrcObj(obj) => {
|
||||||
|
if obj.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if obj.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
graph.remove_relation(src, dst);
|
Ok((&*obj.namespace, &*obj.id).into())
|
||||||
|
}
|
||||||
|
Src::SrcSet(set) => {
|
||||||
|
if set.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if set.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
if set.relation.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.relation must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
info!("removed relation relation");
|
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let src = src?;
|
||||||
|
|
||||||
|
graph.remove(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||||
|
|
||||||
|
info!("deleted relation");
|
||||||
|
|
||||||
self.save_trigger.send(()).await.unwrap();
|
self.save_trigger.send(()).await.unwrap();
|
||||||
|
|
||||||
|
@ -159,18 +174,67 @@ impl RelationService for GraphService {
|
||||||
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> {
|
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> {
|
||||||
let graph = self.graph.lock().await;
|
let graph = self.graph.lock().await;
|
||||||
|
|
||||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
|
|
||||||
authenticate(
|
let req_src = request
|
||||||
request.metadata(),
|
.get_ref()
|
||||||
&graph,
|
.src
|
||||||
&self.api_keys,
|
.as_ref()
|
||||||
&dst_namespace,
|
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||||
|
let req_dst = request
|
||||||
|
.get_ref()
|
||||||
|
.dst
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||||
|
let req_rel = &request.get_ref().relation;
|
||||||
|
|
||||||
|
if req_rel.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("relation must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.namespace must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !graph.has(
|
||||||
|
("themis_key", &*api_key),
|
||||||
"read",
|
"read",
|
||||||
)
|
("themis_ns", &*req_dst.namespace),
|
||||||
.await?;
|
) {
|
||||||
|
return Err(Status::permission_denied(
|
||||||
|
"missing dst.namespace write permissions",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||||
|
Src::SrcObj(obj) => {
|
||||||
|
if obj.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if obj.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
let exists = graph.has_relation(src, dst);
|
Ok((&*obj.namespace, &*obj.id).into())
|
||||||
|
}
|
||||||
|
Src::SrcSet(set) => {
|
||||||
|
if set.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if set.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
if set.relation.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.relation must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let src = src?;
|
||||||
|
|
||||||
|
let exists = graph.has(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||||
|
|
||||||
Ok(Response::new(ExistsResponse { exists }))
|
Ok(Response::new(ExistsResponse { exists }))
|
||||||
}
|
}
|
||||||
|
@ -184,11 +248,73 @@ impl QueryService for GraphService {
|
||||||
) -> Result<Response<IsRelatedToResponse>, Status> {
|
) -> Result<Response<IsRelatedToResponse>, Status> {
|
||||||
let graph = self.graph.lock().await;
|
let graph = self.graph.lock().await;
|
||||||
|
|
||||||
let related = if let Ok((src, dst, _)) = transform_relation(request.get_ref(), &graph) {
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
graph.is_related_to(src, dst)
|
|
||||||
} else {
|
let req_src = request
|
||||||
false
|
.get_ref()
|
||||||
|
.src
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||||
|
let req_dst = request
|
||||||
|
.get_ref()
|
||||||
|
.dst
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||||
|
let req_rel = &request.get_ref().relation;
|
||||||
|
|
||||||
|
if req_rel.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("relation must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.namespace must be set"));
|
||||||
|
}
|
||||||
|
if req_dst.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("dst.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !graph.has(
|
||||||
|
("themis_key", &*api_key),
|
||||||
|
"read",
|
||||||
|
("themis_ns", &*req_dst.namespace),
|
||||||
|
) {
|
||||||
|
return Err(Status::permission_denied(
|
||||||
|
"missing dst.namespace write permissions",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||||
|
Src::SrcObj(obj) => {
|
||||||
|
if obj.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if obj.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((&*obj.namespace, &*obj.id).into())
|
||||||
|
}
|
||||||
|
Src::SrcSet(set) => {
|
||||||
|
if set.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if set.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
if set.relation.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.relation must be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
let src = src?;
|
||||||
|
|
||||||
|
let related = graph.has_recursive(
|
||||||
|
src,
|
||||||
|
req_rel.as_str(),
|
||||||
|
(&*req_dst.namespace, &*req_dst.id),
|
||||||
|
u32::MAX,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Response::new(IsRelatedToResponse { related }))
|
Ok(Response::new(IsRelatedToResponse { related }))
|
||||||
}
|
}
|
||||||
|
@ -196,127 +322,91 @@ impl QueryService for GraphService {
|
||||||
&self,
|
&self,
|
||||||
request: Request<Set>,
|
request: Request<Set>,
|
||||||
) -> Result<Response<GetRelatedToResponse>, Status> {
|
) -> Result<Response<GetRelatedToResponse>, Status> {
|
||||||
let graph = self.graph.lock().await;
|
//let graph = self.graph.lock().await;
|
||||||
|
|
||||||
authenticate(
|
//authenticate(
|
||||||
request.metadata(),
|
// request.metadata(),
|
||||||
&graph,
|
// &graph,
|
||||||
&self.api_keys,
|
// &self.api_keys,
|
||||||
&request.get_ref().namespace,
|
// &request.get_ref().namespace,
|
||||||
"read",
|
// "read",
|
||||||
)
|
//)
|
||||||
.await?;
|
//.await?;
|
||||||
|
|
||||||
let obj = graph
|
//let obj = graph
|
||||||
.get_node(&request.get_ref().namespace, &request.get_ref().id)
|
// .get_node(&request.get_ref().namespace, &request.get_ref().id)
|
||||||
.ok_or(Status::not_found("object not found"))?;
|
// .ok_or(Status::not_found("object not found"))?;
|
||||||
|
|
||||||
let rel = graph::Relation::new(&request.get_ref().relation);
|
//let rel = graph::Relation::new(&request.get_ref().relation);
|
||||||
|
|
||||||
Ok(Response::new(GetRelatedToResponse {
|
//Ok(Response::new(GetRelatedToResponse {
|
||||||
objects: graph
|
// objects: graph
|
||||||
.related_to(obj, rel)
|
// .related_to(obj, rel)
|
||||||
.into_iter()
|
// .into_iter()
|
||||||
.map(|x| {
|
// .map(|x| {
|
||||||
let obj = graph.object_from_ref(&x);
|
// let obj = graph.object_from_ref(&x);
|
||||||
Object {
|
// Object {
|
||||||
namespace: obj.namespace.to_string(),
|
// namespace: obj.namespace.to_string(),
|
||||||
id: obj.id,
|
// id: obj.id,
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.collect::<Vec<_>>(),
|
// .collect::<Vec<_>>(),
|
||||||
}))
|
//}))
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
async fn get_relations(
|
async fn get_relations(
|
||||||
&self,
|
&self,
|
||||||
request: Request<GetRelationsRequest>,
|
request: Request<GetRelationsRequest>,
|
||||||
) -> Result<Response<GetRelationsResponse>, Status> {
|
) -> Result<Response<GetRelationsResponse>, Status> {
|
||||||
let graph = self.graph.lock().await;
|
//let graph = self.graph.lock().await;
|
||||||
|
|
||||||
if request.get_ref().relation.is_empty() {
|
//if request.get_ref().relation.is_empty() {
|
||||||
return Err(Status::invalid_argument("relation must be set"));
|
// return Err(Status::invalid_argument("relation must be set"));
|
||||||
}
|
//}
|
||||||
|
|
||||||
let obj = request
|
//let obj = request
|
||||||
.get_ref()
|
// .get_ref()
|
||||||
.object
|
// .object
|
||||||
.as_ref()
|
// .as_ref()
|
||||||
.ok_or(Status::invalid_argument("object must be set"))?;
|
// .ok_or(Status::invalid_argument("object must be set"))?;
|
||||||
|
|
||||||
authenticate(
|
//authenticate(
|
||||||
request.metadata(),
|
// request.metadata(),
|
||||||
&graph,
|
// &graph,
|
||||||
&self.api_keys,
|
// &self.api_keys,
|
||||||
&obj.namespace,
|
// &obj.namespace,
|
||||||
"read",
|
// "read",
|
||||||
)
|
//)
|
||||||
.await?;
|
//.await?;
|
||||||
|
|
||||||
let obj = graph
|
//let obj = graph
|
||||||
.get_node(&obj.namespace, &obj.id)
|
// .get_node(&obj.namespace, &obj.id)
|
||||||
.ok_or(Status::not_found("object not found"))?;
|
// .ok_or(Status::not_found("object not found"))?;
|
||||||
|
|
||||||
Ok(Response::new(GetRelationsResponse {
|
//Ok(Response::new(GetRelationsResponse {
|
||||||
objects: graph
|
// objects: graph
|
||||||
.relations(ObjectRelation(
|
// .relations(ObjectRelation(
|
||||||
obj,
|
// obj,
|
||||||
graph::Relation::new(&request.get_ref().relation),
|
// graph::Relation::new(&request.get_ref().relation),
|
||||||
))
|
// ))
|
||||||
.into_iter()
|
// .into_iter()
|
||||||
.map(|x| {
|
// .map(|x| {
|
||||||
let obj = graph.object_from_ref(&x);
|
// let obj = graph.object_from_ref(&x);
|
||||||
Object {
|
// Object {
|
||||||
namespace: obj.namespace.to_string(),
|
// namespace: obj.namespace.to_string(),
|
||||||
id: obj.id,
|
// id: obj.id,
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.collect::<Vec<_>>(),
|
// .collect::<Vec<_>>(),
|
||||||
}))
|
//}))
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_relation(
|
async fn api_key_from_req(
|
||||||
rel: &Relation,
|
|
||||||
graph: &Graph,
|
|
||||||
) -> Result<(graph::ObjectOrSet, graph::ObjectRelation, String), Status> {
|
|
||||||
let src = match rel
|
|
||||||
.src
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Status::invalid_argument("src must be set"))?
|
|
||||||
{
|
|
||||||
Src::SrcObj(object) => graph::ObjectOrSet::Object(
|
|
||||||
graph
|
|
||||||
.get_node(&object.namespace, &object.id)
|
|
||||||
.ok_or(Status::not_found("src object could not be found"))?,
|
|
||||||
),
|
|
||||||
Src::SrcSet(set) => graph::ObjectOrSet::Set((
|
|
||||||
graph
|
|
||||||
.get_node(&set.namespace, &set.id)
|
|
||||||
.ok_or(Status::not_found("src object could not be found"))?,
|
|
||||||
graph::Relation::new(&set.relation),
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let dst = rel
|
|
||||||
.dst
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Status::invalid_argument("dst must be set"))?;
|
|
||||||
let dst_namespace = dst.namespace.to_string();
|
|
||||||
let dst = graph
|
|
||||||
.get_node(&dst.namespace, &dst.id)
|
|
||||||
.ok_or(Status::not_found("dst object could not be found"))?;
|
|
||||||
let dst = ObjectRelation(dst, graph::Relation::new(&rel.relation));
|
|
||||||
|
|
||||||
Ok((src, dst, dst_namespace))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn authenticate(
|
|
||||||
metadata: &MetadataMap,
|
metadata: &MetadataMap,
|
||||||
graph: &Graph,
|
|
||||||
api_keys: &Arc<Mutex<HashMap<String, String>>>,
|
api_keys: &Arc<Mutex<HashMap<String, String>>>,
|
||||||
namespace: &str,
|
) -> Result<String, Status> {
|
||||||
relation: &str,
|
|
||||||
) -> Result<(), Status> {
|
|
||||||
let api_key = metadata
|
let api_key = metadata
|
||||||
.get("x-api-key")
|
.get("x-api-key")
|
||||||
.map(|x| x.to_str().unwrap())
|
.map(|x| x.to_str().unwrap())
|
||||||
|
@ -329,18 +419,5 @@ async fn authenticate(
|
||||||
let api_key = api_keys
|
let api_key = api_keys
|
||||||
.get(&api_key)
|
.get(&api_key)
|
||||||
.ok_or(Status::unauthenticated("api-key invalid"))?;
|
.ok_or(Status::unauthenticated("api-key invalid"))?;
|
||||||
|
Ok(api_key.to_string())
|
||||||
let api_key = graph
|
|
||||||
.get_node("themis_key", api_key)
|
|
||||||
.ok_or(Status::unauthenticated("api-key invalid"))?;
|
|
||||||
|
|
||||||
let ns_ref = graph
|
|
||||||
.get_node("themis_ns", namespace)
|
|
||||||
.ok_or(Status::permission_denied("no permission for namespace"))?;
|
|
||||||
|
|
||||||
if !graph.is_related_to(api_key, (ns_ref, graph::Relation::new(relation))) {
|
|
||||||
Err(Status::permission_denied("no permission for namespace"))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -1,7 +1,10 @@
|
||||||
|
#![feature(btree_cursors)]
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use graph::Graph;
|
|
||||||
use grpc_service::GraphService;
|
use grpc_service::GraphService;
|
||||||
|
use relation_set::RelationSet;
|
||||||
|
//use grpc_service::GraphService;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{AsyncBufReadExt, BufReader},
|
io::{AsyncBufReadExt, BufReader},
|
||||||
|
@ -10,13 +13,12 @@ use tokio::{
|
||||||
};
|
};
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
|
|
||||||
pub mod graph;
|
|
||||||
pub mod grpc_service;
|
pub mod grpc_service;
|
||||||
|
pub mod relation_set;
|
||||||
pub mod themis_proto;
|
pub mod themis_proto;
|
||||||
|
|
||||||
use crate::themis_proto::{
|
use crate::themis_proto::{
|
||||||
object_service_server::ObjectServiceServer, query_service_server::QueryServiceServer,
|
query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer,
|
||||||
relation_service_server::RelationServiceServer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -38,9 +40,9 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph = if let Ok(mut file) = File::open("graph.dat").await {
|
let graph = if let Ok(mut file) = File::open("graph.dat").await {
|
||||||
Graph::from_file(&mut file).await
|
RelationSet::from_file(&mut file).await
|
||||||
} else {
|
} else {
|
||||||
Graph::default()
|
RelationSet::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let graph = Arc::new(Mutex::new(graph));
|
let graph = Arc::new(Mutex::new(graph));
|
||||||
|
@ -68,10 +70,9 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Server::builder()
|
Server::builder()
|
||||||
.add_service(ObjectServiceServer::new(graph_service.clone()))
|
|
||||||
.add_service(RelationServiceServer::new(graph_service.clone()))
|
.add_service(RelationServiceServer::new(graph_service.clone()))
|
||||||
.add_service(QueryServiceServer::new(graph_service))
|
.add_service(QueryServiceServer::new(graph_service))
|
||||||
.serve("0.0.0.0:50051".parse().unwrap())
|
.serve("[::]:50051".parse().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
449
src/relation_set.rs
Normal file
449
src/relation_set.rs
Normal file
|
@ -0,0 +1,449 @@
|
||||||
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::{BTreeMap, BinaryHeap, HashMap, HashSet},
|
||||||
|
ops::{Bound, Deref},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use compact_str::CompactString;
|
||||||
|
use tokio::{
|
||||||
|
fs::File,
|
||||||
|
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub struct Object {
|
||||||
|
pub namespace: CompactString,
|
||||||
|
pub id: CompactString,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub struct ObjectRef<'a> {
|
||||||
|
pub namespace: &'a str,
|
||||||
|
pub id: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
|
pub enum ObjectOrSet {
|
||||||
|
Object(Object),
|
||||||
|
Set((Object, Relation)),
|
||||||
|
}
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub struct Relation(pub CompactString);
|
||||||
|
|
||||||
|
type S = ObjectOrSet;
|
||||||
|
type R = Relation;
|
||||||
|
type D = Object;
|
||||||
|
|
||||||
|
pub struct RelationSet {
|
||||||
|
src_to_dst: BTreeMap<Arc<S>, HashMap<Arc<R>, HashSet<Arc<D>>>>,
|
||||||
|
dst_to_src: BTreeMap<Arc<D>, HashMap<Arc<R>, HashSet<Arc<S>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationSet {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
src_to_dst: BTreeMap::new(),
|
||||||
|
dst_to_src: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) {
|
||||||
|
let src = Arc::new(src.into());
|
||||||
|
let rel = Arc::new(rel.into());
|
||||||
|
let dst = Arc::new(dst.into());
|
||||||
|
|
||||||
|
if let Some(rels_dsts) = self.src_to_dst.get_mut(&src) {
|
||||||
|
if let Some(dsts) = rels_dsts.get_mut(&rel) {
|
||||||
|
dsts.insert(dst.clone());
|
||||||
|
} else {
|
||||||
|
let mut dsts = HashSet::new();
|
||||||
|
dsts.insert(dst.clone());
|
||||||
|
rels_dsts.insert(rel.clone(), dsts);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut rels_dsts = HashMap::new();
|
||||||
|
let mut dsts = HashSet::new();
|
||||||
|
dsts.insert(dst.clone());
|
||||||
|
rels_dsts.insert(rel.clone(), dsts);
|
||||||
|
self.src_to_dst.insert(src.clone(), rels_dsts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rels_srcs) = self.dst_to_src.get_mut(&dst) {
|
||||||
|
if let Some(srcs) = rels_srcs.get_mut(&rel) {
|
||||||
|
srcs.insert(src.clone());
|
||||||
|
} else {
|
||||||
|
let mut srcs = HashSet::new();
|
||||||
|
srcs.insert(src.clone());
|
||||||
|
rels_srcs.insert(rel.clone(), srcs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut rels_srcs = HashMap::new();
|
||||||
|
let mut srcs = HashSet::new();
|
||||||
|
srcs.insert(src.clone());
|
||||||
|
rels_srcs.insert(rel.clone(), srcs);
|
||||||
|
self.dst_to_src.insert(dst.clone(), rels_srcs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) {
|
||||||
|
let src = src.into();
|
||||||
|
let rel = rel.into();
|
||||||
|
let dst = dst.into();
|
||||||
|
|
||||||
|
if let Some(dsts) = self
|
||||||
|
.src_to_dst
|
||||||
|
.get_mut(&src)
|
||||||
|
.and_then(|rels_dsts| rels_dsts.get_mut(&rel))
|
||||||
|
{
|
||||||
|
dsts.remove(&dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(srcs) = self
|
||||||
|
.dst_to_src
|
||||||
|
.get_mut(&dst)
|
||||||
|
.and_then(|rels_srcs| rels_srcs.get_mut(&rel))
|
||||||
|
{
|
||||||
|
srcs.remove(&src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_by_src(&mut self, src: &S) {
|
||||||
|
for (rel, dsts) in self.src_to_dst.remove(src).iter().flat_map(|x| x.iter()) {
|
||||||
|
for dst in dsts {
|
||||||
|
if let Some(srcs) = self
|
||||||
|
.dst_to_src
|
||||||
|
.get_mut(dst)
|
||||||
|
.and_then(|rels_srcs| rels_srcs.get_mut(rel))
|
||||||
|
{
|
||||||
|
srcs.remove(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_by_dst(&mut self, dst: &D) {
|
||||||
|
for (rel, srcs) in self.dst_to_src.remove(dst).iter().flat_map(|x| x.iter()) {
|
||||||
|
for src in srcs {
|
||||||
|
if let Some(dsts) = self
|
||||||
|
.src_to_dst
|
||||||
|
.get_mut(src)
|
||||||
|
.and_then(|rels_dsts| rels_dsts.get_mut(rel))
|
||||||
|
{
|
||||||
|
dsts.remove(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has(&self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) -> bool {
|
||||||
|
let src = src.into();
|
||||||
|
let rel = rel.into();
|
||||||
|
let dst = dst.into();
|
||||||
|
|
||||||
|
self.src_to_dst
|
||||||
|
.get(&src)
|
||||||
|
.and_then(|rels_dsts| rels_dsts.get(&rel))
|
||||||
|
.and_then(|dsts| dsts.get(&dst))
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_object<'a>(&self, obj: impl Into<&'a Object>) -> bool {
|
||||||
|
let obj = obj.into();
|
||||||
|
let has_dst_obj = self.dst_to_src.contains_key(obj);
|
||||||
|
|
||||||
|
let cursor = self
|
||||||
|
.src_to_dst
|
||||||
|
.lower_bound(Bound::Included(&ObjectOrSet::Object(obj.clone())));
|
||||||
|
|
||||||
|
let has_src_obj = if let Some(key) = cursor.key() {
|
||||||
|
obj.namespace == key.object().namespace && obj.id == key.object().id
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
has_dst_obj || has_src_obj
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_recursive(
|
||||||
|
&self,
|
||||||
|
src: impl Into<S>,
|
||||||
|
rel: impl Into<R>,
|
||||||
|
dst: impl Into<D>,
|
||||||
|
limit: u32,
|
||||||
|
) -> bool {
|
||||||
|
let src = src.into();
|
||||||
|
let rel = rel.into();
|
||||||
|
let dst = dst.into();
|
||||||
|
|
||||||
|
let mut dist: HashMap<(Arc<Object>, Arc<Relation>), u32> = HashMap::new();
|
||||||
|
let mut q: BinaryHeap<Distanced<(Arc<Object>, Arc<Relation>)>> = BinaryHeap::new();
|
||||||
|
|
||||||
|
for (nrel, ndst) in self
|
||||||
|
.src_to_dst
|
||||||
|
.get(&src)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.iter())
|
||||||
|
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||||
|
{
|
||||||
|
if *nrel == rel && *ndst == dst {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dist.insert((ndst.clone(), nrel.clone()), 1);
|
||||||
|
q.push(Distanced::one((ndst, nrel)));
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(distanced) = q.pop() {
|
||||||
|
let node_dist = distanced.distance() + 1;
|
||||||
|
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
|
||||||
|
for (nrel, ndst) in self
|
||||||
|
.src_to_dst
|
||||||
|
.get(&node)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.iter())
|
||||||
|
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||||
|
{
|
||||||
|
if *nrel == rel && *ndst == dst {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if let Some(existing_node_dist) = dist.get(&*distanced) {
|
||||||
|
if *existing_node_dist <= node_dist || node_dist >= limit {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dist.insert((ndst.clone(), nrel.clone()), node_dist);
|
||||||
|
q.push(Distanced::one((ndst, nrel)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn to_file(&self, file: &mut File) {
|
||||||
|
for (dst, rels_srcs) in self.dst_to_src.iter() {
|
||||||
|
file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
for (rel, srcs) in rels_srcs.iter() {
|
||||||
|
let srcs = srcs
|
||||||
|
.iter()
|
||||||
|
.map(|src| {
|
||||||
|
let src_obj = src.object();
|
||||||
|
let src_str = if src_obj.namespace == dst.namespace && src_obj.id == dst.id
|
||||||
|
{
|
||||||
|
"self".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}:{}", src_obj.namespace, src_obj.id)
|
||||||
|
};
|
||||||
|
match &**src {
|
||||||
|
ObjectOrSet::Object(_) => src_str,
|
||||||
|
ObjectOrSet::Set(set) => {
|
||||||
|
format!("{}#{}", src_str, set.1 .0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.reduce(|acc, x| acc + ", " + &x)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
file.write_all(format!("{} = [{}]\n", &rel.0, &srcs).as_bytes())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
file.write_all("\n".as_bytes()).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn from_file(file: &mut File) -> Self {
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let mut lines = reader.lines();
|
||||||
|
let mut graph = Self::new();
|
||||||
|
let mut node: Option<(String, String)> = None;
|
||||||
|
while let Ok(Some(line)) = lines.next_line().await {
|
||||||
|
if line.starts_with('[') && line.ends_with(']') {
|
||||||
|
let line = &mut line[1..line.len() - 1].split(':');
|
||||||
|
let namespace = line.next().unwrap();
|
||||||
|
let id = line.next().unwrap();
|
||||||
|
node = Some((namespace.to_string(), id.to_string()));
|
||||||
|
} else if line.contains('=') && line.contains('[') && line.contains(']') {
|
||||||
|
if let Some(dst) = &node {
|
||||||
|
let equals_pos = line.find('=').unwrap();
|
||||||
|
let arr_start = line.find('[').unwrap();
|
||||||
|
let arr_stop = line.find(']').unwrap();
|
||||||
|
|
||||||
|
let rel = line[..equals_pos].trim();
|
||||||
|
let arr = line[arr_start + 1..arr_stop].split(", ");
|
||||||
|
|
||||||
|
for obj in arr {
|
||||||
|
let src: ObjectOrSet = if obj.contains('#') {
|
||||||
|
let sep_1 = obj.find(':');
|
||||||
|
let sep_2 = obj.find('#').unwrap();
|
||||||
|
|
||||||
|
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||||
|
(&obj[..sep_1], &obj[sep_1 + 1..sep_2])
|
||||||
|
} else {
|
||||||
|
(dst.0.as_str(), dst.1.as_str())
|
||||||
|
};
|
||||||
|
|
||||||
|
let rel = &obj[sep_2 + 1..];
|
||||||
|
|
||||||
|
(namespace, id, rel).into()
|
||||||
|
} else {
|
||||||
|
let sep_1 = obj.find(':');
|
||||||
|
|
||||||
|
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||||
|
(&obj[..sep_1], &obj[sep_1 + 1..])
|
||||||
|
} else {
|
||||||
|
(dst.0.as_str(), dst.1.as_str())
|
||||||
|
};
|
||||||
|
(namespace, id).into()
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.insert(src, rel, dst.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
struct Distanced<T> {
|
||||||
|
distance: u32,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Distanced<T> {
|
||||||
|
pub fn one(data: T) -> Self {
|
||||||
|
Self { distance: 1, data }
|
||||||
|
}
|
||||||
|
pub fn distance(&self) -> u32 {
|
||||||
|
self.distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Distanced<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialOrd for Distanced<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.distance.partial_cmp(&other.distance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Eq> Ord for Distanced<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.distance.cmp(&other.distance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Relation {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
self.0.partial_cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for Relation {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.0.cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for ObjectOrSet {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match (
|
||||||
|
self.object().partial_cmp(other.object()),
|
||||||
|
self.relation(),
|
||||||
|
other.relation(),
|
||||||
|
) {
|
||||||
|
(Some(Ordering::Equal), self_rel, other_rel) => self_rel.partial_cmp(&other_rel),
|
||||||
|
(ord, _, _) => ord,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for ObjectOrSet {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.object()
|
||||||
|
.cmp(other.object())
|
||||||
|
.then(self.relation().cmp(&other.relation()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Object {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match self.namespace.partial_cmp(&other.namespace) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => self.id.partial_cmp(&other.id),
|
||||||
|
ord => ord,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for Object {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.namespace
|
||||||
|
.cmp(&other.namespace)
|
||||||
|
.then(self.id.cmp(&other.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(&str, &str)> for ObjectOrSet {
|
||||||
|
fn from((namespace, id): (&str, &str)) -> Self {
|
||||||
|
ObjectOrSet::Object(Object {
|
||||||
|
namespace: namespace.into(),
|
||||||
|
id: id.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(&str, &str, &str)> for ObjectOrSet {
|
||||||
|
fn from((namespace, id, rel): (&str, &str, &str)) -> Self {
|
||||||
|
ObjectOrSet::Set(((namespace, id).into(), Relation(rel.into())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(&str, &str)> for Object {
|
||||||
|
fn from((namespace, id): (&str, &str)) -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: namespace.into(),
|
||||||
|
id: id.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(String, String)> for Object {
|
||||||
|
fn from((namespace, id): (String, String)) -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: namespace.into(),
|
||||||
|
id: id.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Relation {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Relation(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<String> for Relation {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Relation(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectOrSet {
|
||||||
|
pub fn object(&self) -> &Object {
|
||||||
|
match self {
|
||||||
|
ObjectOrSet::Object(obj) => obj,
|
||||||
|
ObjectOrSet::Set((obj, _)) => obj,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn relation(&self) -> Option<&Relation> {
|
||||||
|
match self {
|
||||||
|
ObjectOrSet::Object(_) => None,
|
||||||
|
ObjectOrSet::Set((_, rel)) => Some(rel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Relation {
|
||||||
|
pub fn new(relation: &str) -> Self {
|
||||||
|
Self(relation.into())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue