From 21f0c705fee1b0803591a1b3fcc36216b3515be6 Mon Sep 17 00:00:00 2001 From: Paul Z Date: Mon, 5 Jun 2023 22:17:22 +0200 Subject: [PATCH] rename to rebacs, proto rework and implemented missing functions --- Cargo.toml | 2 +- build.rs | 2 +- proto/rebacs.proto | 95 ++++++++++++ proto/themis.proto | 67 --------- src/grpc_service.rs | 358 ++++++++++++++++++++++---------------------- src/main.rs | 4 +- src/rebacs_proto.rs | 1 + src/relation_set.rs | 138 ++++++++++++++++- src/themis_proto.rs | 1 - 9 files changed, 413 insertions(+), 255 deletions(-) create mode 100644 proto/rebacs.proto delete mode 100644 proto/themis.proto create mode 100644 src/rebacs_proto.rs delete mode 100644 src/themis_proto.rs diff --git a/Cargo.toml b/Cargo.toml index 3871cd6..db4070d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "themis" +name = "rebacs" version = "0.1.0" edition = "2021" diff --git a/build.rs b/build.rs index f7de9b7..a15eb9a 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ fn main() { tonic_build::configure() .build_server(true) - .compile(&["proto/themis.proto"], &["proto"]) + .compile(&["proto/rebacs.proto"], &["proto"]) .unwrap(); } diff --git a/proto/rebacs.proto b/proto/rebacs.proto new file mode 100644 index 0000000..8322645 --- /dev/null +++ b/proto/rebacs.proto @@ -0,0 +1,95 @@ +syntax = "proto3"; +package eu.zettoit.rebacs; + +service RelationService { + rpc Create(RelationCreateReq) returns (RelationCreateRes); + rpc Delete(RelationDeleteReq) returns (RelationDeleteRes); + rpc Exists(RelationExistsReq) returns (RelationExistsRes); +} + +service QueryService { + // check if one object or objectset is related to another by a relation + rpc IsRelatedTo(QueryIsRelatedToReq) returns (QueryIsRelatedToRes); + + // get all objects that are related to one object by a relation + rpc GetRelated(QueryGetRelatedReq) returns (QueryGetRelatedRes); + + // get all objects that the given object has a relation with + rpc GetRelations(QueryGetRelationsReq) returns (QueryGetRelationsRes); +} + +message RelationCreateReq{ + ObjectOrSet src = 1; + Object dst = 2; + string relation = 3; +} +message RelationCreateRes{} + +message RelationDeleteReq{ + ObjectOrSet src = 1; + Object dst = 3; + string relation = 4; +} +message RelationDeleteRes{} + +message RelationExistsReq{ + ObjectOrSet src = 1; + Object dst = 2; + string relation = 3; +} +message RelationExistsRes{ + bool exists = 1; +} + +message QueryIsRelatedToReq{ + ObjectOrSet src = 1; + Object dst = 2; + string relation = 3; +} +message QueryIsRelatedToRes{ + bool related = 1; +} + +message QueryGetRelatedReq{ + Object dst = 1; + optional string relation = 2; + optional string namespace = 3; + optional uint32 depth = 4; +} +message QueryGetRelatedRes{ + repeated QueryGetRelatedItem objects = 1; +} +message QueryGetRelatedItem{ + string relation = 1; + Object src = 2; +} + +message QueryGetRelationsReq{ + Object src = 1; + optional string relation = 2; + optional string namespace = 3; + optional uint32 depth = 4; +} +message QueryGetRelationsRes{ + repeated QueryGetRelationsItem related = 1; +} +message QueryGetRelationsItem{ + string relation = 1; + Object dst = 2; +} + +message Object{ + string namespace = 1; + string id = 2; +} +message Set{ + string namespace = 1; + string id = 2; + string relation = 3; +} + +message ObjectOrSet { + string namespace = 1; + string id = 2; + optional string relation = 3; +} diff --git a/proto/themis.proto b/proto/themis.proto deleted file mode 100644 index ae2046d..0000000 --- a/proto/themis.proto +++ /dev/null @@ -1,67 +0,0 @@ -syntax = "proto3"; -package eu.zettoit.themis; - -service RelationService { - rpc Create(Relation) returns (Empty); - rpc Delete(Relation) returns (Empty); - rpc Exists(Relation) returns (ExistsResponse); -} - -service QueryService { - // check if one object or objectset is related to another by a relation - rpc IsRelatedTo(Relation) returns (IsRelatedToResponse); - - // get all objects that are related to one object by a relation - rpc GetRelatedTo(Set) returns (GetRelatedToResponse); - - // get all objects that the given object has a relation with - rpc GetRelations(GetRelationsRequest) returns (GetRelationsResponse); -} - -message ExistsResponse { - bool exists = 1; -} - -message IsRelatedToResponse{ - bool related = 1; -} - -message GetRelatedToResponse{ - repeated Object objects = 1; -} - -message GetRelationsRequest{ - Object object = 1; - string relation = 2; -} -message GetRelationsResponse{ - repeated Object objects = 1; -} - -message Object{ - string namespace = 1; - string id = 2; -} -message Set{ - string namespace = 1; - string id = 2; - string relation = 3; -} - -message ObjectOrSet { - oneof object_or_set{ - Object object = 1; - Set set = 2; - }; -} - -message Relation{ - oneof src{ - Object src_obj = 1; - Set src_set = 2; - }; - Object dst = 3; - string relation = 4; -} - -message Empty{} diff --git a/src/grpc_service.rs b/src/grpc_service.rs index abc53a0..5d33e74 100644 --- a/src/grpc_service.rs +++ b/src/grpc_service.rs @@ -8,12 +8,14 @@ use tokio::sync::Mutex; use tonic::metadata::MetadataMap; use tonic::{Request, Response, Status}; -use crate::relation_set::{ObjectOrSet, RelationSet}; -use crate::themis_proto::{ - query_service_server::QueryService, relation::Src, relation_service_server::RelationService, - Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse, - IsRelatedToResponse, Relation, Set, +use crate::rebacs_proto::{ + query_service_server::QueryService, relation_service_server::RelationService, Object, + QueryGetRelatedItem, QueryGetRelatedReq, QueryGetRelatedRes, QueryGetRelationsItem, + QueryGetRelationsReq, QueryGetRelationsRes, QueryIsRelatedToReq, QueryIsRelatedToRes, + RelationCreateReq, RelationCreateRes, RelationDeleteReq, RelationDeleteRes, RelationExistsReq, + RelationExistsRes, }; +use crate::relation_set::{ObjectOrSet, RelationSet}; #[derive(Clone)] pub struct GraphService { @@ -22,9 +24,15 @@ pub struct GraphService { pub save_trigger: Sender<()>, } +const API_KEY_NS: &str = "rebacs_key"; +const NAMESPACE_NS: &str = "rebacs_ns"; + #[tonic::async_trait] impl RelationService for GraphService { - async fn create(&self, request: Request) -> Result, Status> { + async fn create( + &self, + request: Request, + ) -> Result, Status> { let mut graph = self.graph.lock().await; let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; @@ -52,9 +60,9 @@ impl RelationService for GraphService { } if !graph.has_recursive( - ("themis_key", &*api_key), + (API_KEY_NS, &*api_key), "write", - ("themis_ns", &*req_dst.namespace), + (NAMESPACE_NS, &*req_dst.namespace), u32::MAX, ) { return Err(Status::permission_denied( @@ -62,32 +70,21 @@ impl RelationService for GraphService { ))?; } - let src: Result = 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()) + if req_src.namespace.is_empty() { + return Err(Status::invalid_argument("src.namespace must be set")); + } + if req_src.id.is_empty() { + return Err(Status::invalid_argument("src.id must be set")); + } + let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { + if req_src_relation.is_empty() { + return Err(Status::invalid_argument("src.relation must be set")); } - 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()) - } + (&*req_src.namespace, &*req_src.id, req_src_relation).into() + } else { + (&*req_src.namespace, &*req_src.id).into() }; - let src = src?; graph.insert( src.clone(), @@ -99,9 +96,12 @@ impl RelationService for GraphService { self.save_trigger.send(()).await.unwrap(); - Ok(Response::new(Empty {})) + Ok(Response::new(RelationCreateRes {})) } - async fn delete(&self, request: Request) -> Result, Status> { + async fn delete( + &self, + request: Request, + ) -> Result, Status> { let mut graph = self.graph.lock().await; let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; @@ -129,41 +129,31 @@ impl RelationService for GraphService { } if !graph.has_recursive( - ("themis_key", &*api_key), + (API_KEY_NS, &*api_key), "write", - ("themis_ns", &*req_dst.namespace), + (NAMESPACE_NS, &*req_dst.namespace), u32::MAX, ) { return Err(Status::permission_denied( "missing dst.namespace write permissions", ))?; } - let src: Result = 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()) + if req_src.namespace.is_empty() { + return Err(Status::invalid_argument("src.namespace must be set")); + } + if req_src.id.is_empty() { + return Err(Status::invalid_argument("src.id must be set")); + } + let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { + if req_src_relation.is_empty() { + return Err(Status::invalid_argument("src.relation must be set")); } - 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()) - } + (&*req_src.namespace, &*req_src.id, req_src_relation).into() + } else { + (&*req_src.namespace, &*req_src.id).into() }; - let src = src?; graph.remove(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id)); @@ -171,9 +161,12 @@ impl RelationService for GraphService { self.save_trigger.send(()).await.unwrap(); - Ok(Response::new(Empty {})) + Ok(Response::new(RelationDeleteRes {})) } - async fn exists(&self, request: Request) -> Result, Status> { + async fn exists( + &self, + request: Request, + ) -> Result, Status> { let graph = self.graph.lock().await; let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; @@ -201,45 +194,34 @@ impl RelationService for GraphService { } if !graph.has_recursive( - ("themis_key", &*api_key), + (API_KEY_NS, &*api_key), "read", - ("themis_ns", &*req_dst.namespace), + (NAMESPACE_NS, &*req_dst.namespace), u32::MAX, ) { return Err(Status::permission_denied( "missing dst.namespace write permissions", ))?; } - let src: Result = 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()) + if req_src.namespace.is_empty() { + return Err(Status::invalid_argument("src.namespace must be set")); + } + if req_src.id.is_empty() { + return Err(Status::invalid_argument("src.id must be set")); + } + let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { + if req_src_relation.is_empty() { + return Err(Status::invalid_argument("src.relation must be set")); } - 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()) - } + (&*req_src.namespace, &*req_src.id, req_src_relation).into() + } else { + (&*req_src.namespace, &*req_src.id).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(RelationExistsRes { exists })) } } @@ -247,8 +229,8 @@ impl RelationService for GraphService { impl QueryService for GraphService { async fn is_related_to( &self, - request: Request, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { let graph = self.graph.lock().await; let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; @@ -276,9 +258,9 @@ impl QueryService for GraphService { } if !graph.has_recursive( - ("themis_key", &*api_key), + (API_KEY_NS, &*api_key), "read", - ("themis_ns", &*req_dst.namespace), + (NAMESPACE_NS, &*req_dst.namespace), u32::MAX, ) { return Err(Status::permission_denied( @@ -286,32 +268,21 @@ impl QueryService for GraphService { ))?; } - let src: Result = 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()) + if req_src.namespace.is_empty() { + return Err(Status::invalid_argument("src.namespace must be set")); + } + if req_src.id.is_empty() { + return Err(Status::invalid_argument("src.id must be set")); + } + let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { + if req_src_relation.is_empty() { + return Err(Status::invalid_argument("src.relation must be set")); } - 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()) - } + (&*req_src.namespace, &*req_src.id, req_src_relation).into() + } else { + (&*req_src.namespace, &*req_src.id).into() }; - let src = src?; let related = graph.has_recursive( src, @@ -320,90 +291,113 @@ impl QueryService for GraphService { u32::MAX, ); - Ok(Response::new(IsRelatedToResponse { related })) + Ok(Response::new(QueryIsRelatedToRes { related })) } - async fn get_related_to( + async fn get_related( &self, - request: Request, - ) -> Result, Status> { - //let graph = self.graph.lock().await; + request: Request, + ) -> Result, Status> { + let graph = self.graph.lock().await; - //authenticate( - // request.metadata(), - // &graph, - // &self.api_keys, - // &request.get_ref().namespace, - // "read", - //) - //.await?; + let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; - //let obj = graph - // .get_node(&request.get_ref().namespace, &request.get_ref().id) - // .ok_or(Status::not_found("object not found"))?; + 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; - //let rel = graph::Relation::new(&request.get_ref().relation); + 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")); + } - //Ok(Response::new(GetRelatedToResponse { - // objects: graph - // .related_to(obj, rel) - // .into_iter() - // .map(|x| { - // let obj = graph.object_from_ref(&x); - // Object { - // namespace: obj.namespace.to_string(), - // id: obj.id, - // } - // }) - // .collect::>(), - //})) - todo!() + let req_namespace = &request.get_ref().namespace; + let req_depth = &request.get_ref().depth; + + if !graph.has_recursive( + (API_KEY_NS, &*api_key), + "read", + (NAMESPACE_NS, &*req_dst.namespace), + u32::MAX, + ) { + return Err(Status::permission_denied( + "missing dst.namespace read permissions", + ))?; + } + + let dst = (req_dst.namespace.as_ref(), req_dst.id.as_ref()); + + let objects = graph + .related_to( + dst, + req_rel.as_deref(), + req_namespace.as_deref(), + req_depth.unwrap_or(u32::MAX), + ) + .into_iter() + .map(|x| QueryGetRelatedItem { + src: Some(Object { + namespace: x.1.namespace.to_string(), + id: x.1.id.to_string(), + }), + relation: x.0 .0.to_string(), + }) + .collect::<_>(); + + Ok(Response::new(QueryGetRelatedRes { objects })) } async fn get_relations( &self, - request: Request, - ) -> Result, Status> { - //let graph = self.graph.lock().await; + request: Request, + ) -> Result, Status> { + let graph = self.graph.lock().await; - //if request.get_ref().relation.is_empty() { - // return Err(Status::invalid_argument("relation must be set")); - //} + let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; - //let obj = request - // .get_ref() - // .object - // .as_ref() - // .ok_or(Status::invalid_argument("object must be set"))?; + let req_src = request + .get_ref() + .src + .as_ref() + .ok_or(Status::invalid_argument("src must be set"))?; + let src = (&*req_src.namespace, &*req_src.id); - //authenticate( - // request.metadata(), - // &graph, - // &self.api_keys, - // &obj.namespace, - // "read", - //) - //.await?; + let req_rel = &request.get_ref().relation; + let req_namespace = &request.get_ref().namespace; + let req_depth = &request.get_ref().depth; - //let obj = graph - // .get_node(&obj.namespace, &obj.id) - // .ok_or(Status::not_found("object not found"))?; + if !graph.has_recursive( + (API_KEY_NS, &*api_key), + "read", + (NAMESPACE_NS, &*req_src.namespace), + u32::MAX, + ) { + return Err(Status::permission_denied( + "missing src.namespace read permissions", + ))?; + } - //Ok(Response::new(GetRelationsResponse { - // objects: graph - // .relations(ObjectRelation( - // obj, - // graph::Relation::new(&request.get_ref().relation), - // )) - // .into_iter() - // .map(|x| { - // let obj = graph.object_from_ref(&x); - // Object { - // namespace: obj.namespace.to_string(), - // id: obj.id, - // } - // }) - // .collect::>(), - //})) - todo!() + let related = graph + .relations( + src, + req_rel.as_deref(), + req_namespace.as_deref(), + req_depth.unwrap_or(u32::MAX), + ) + .into_iter() + .map(|x| QueryGetRelationsItem { + dst: Some(Object { + namespace: x.1.namespace.to_string(), + id: x.1.id.to_string(), + }), + relation: x.0 .0.to_string(), + }) + .collect::<_>(); + + Ok(Response::new(QueryGetRelationsRes { related })) } } diff --git a/src/main.rs b/src/main.rs index ac3f79a..c15850d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,10 @@ use tokio::{ use tonic::transport::Server; pub mod grpc_service; +pub mod rebacs_proto; pub mod relation_set; -pub mod themis_proto; -use crate::themis_proto::{ +use crate::rebacs_proto::{ query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer, }; diff --git a/src/rebacs_proto.rs b/src/rebacs_proto.rs new file mode 100644 index 0000000..5c61ec7 --- /dev/null +++ b/src/rebacs_proto.rs @@ -0,0 +1 @@ +tonic::include_proto!("eu.zettoit.rebacs"); diff --git a/src/relation_set.rs b/src/relation_set.rs index d1c5fb5..98d2ec8 100644 --- a/src/relation_set.rs +++ b/src/relation_set.rs @@ -195,6 +195,9 @@ impl RelationSet { while let Some(distanced) = q.pop() { let node_dist = distanced.distance() + 1; + if node_dist > limit { + break; + } let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone())); for (nrel, ndst) in self .src_to_dst @@ -207,7 +210,7 @@ impl RelationSet { return true; } if let Some(existing_node_dist) = dist.get(&*distanced) { - if *existing_node_dist <= node_dist || node_dist >= limit { + if *existing_node_dist <= node_dist { continue; } } @@ -218,6 +221,139 @@ impl RelationSet { false } + pub fn related_to( + &self, + dst: impl Into, + rel: Option>, + namespace: Option<&str>, + limit: u32, + ) -> Vec<(Relation, Object)> { + let rel = rel.map(|x| x.into()); + let dst = dst.into(); + + let mut related: Vec<(Relation, Object)> = vec![]; + + let mut dist: HashMap<(Arc, Arc), u32> = HashMap::new(); + let mut q: BinaryHeap, Arc)>> = BinaryHeap::new(); + + for (nrel, ndst) in self + .dst_to_src + .get(&dst) + .iter() + .flat_map(|x| x.iter()) + .flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone()))) + { + match &*ndst { + ObjectOrSet::Object(obj) => { + if (rel.is_none() || rel.as_ref() == Some(&nrel)) + && (namespace.is_none() || namespace == Some(&obj.namespace)) + { + related.push(((*nrel).clone(), obj.clone())); + } + } + ObjectOrSet::Set((obj, rel)) => { + let obj = Arc::new(obj.clone()); + let rel = Arc::new(rel.clone()); + dist.insert((obj.clone(), rel.clone()), 1); + q.push(Distanced::one((obj, rel))); + } + } + } + + while let Some(distanced) = q.pop() { + let node_dist = distanced.distance() + 1; + if node_dist > limit { + break; + } + + for ndst in self + .dst_to_src + .get(&distanced.0) + .and_then(|x| x.get(&distanced.1)) + .iter() + .flat_map(|x| x.iter()) + { + match &**ndst { + ObjectOrSet::Object(obj) => { + if (rel.is_none() || rel.as_ref() == Some(&distanced.1)) + && (namespace.is_none() || namespace == Some(&obj.namespace)) + { + related.push(((*distanced.1).clone(), obj.clone())); + } + } + ObjectOrSet::Set((obj, rel)) => { + let obj = Arc::new(obj.clone()); + let rel = Arc::new(rel.clone()); + dist.insert((obj.clone(), rel.clone()), node_dist); + q.push(Distanced::one((obj, rel))); + } + } + } + } + + related + } + + pub fn relations( + &self, + src: impl Into, + rel: Option>, + namespace: Option<&str>, + limit: u32, + ) -> Vec<(Relation, Object)> { + let rel = rel.map(|x| x.into()); + let src = src.into(); + + let mut related: Vec<(Relation, Object)> = vec![]; + + let mut dist: HashMap, u32> = HashMap::new(); + let mut q: BinaryHeap>> = 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 (rel.is_none() || rel.as_ref() == Some(&nrel)) + && (namespace.is_none() || namespace == Some(&ndst.namespace)) + { + related.push(((*nrel).clone(), (*ndst).clone())); + } + let obj = Arc::new(ObjectOrSet::Set(((*ndst).clone(), (*nrel).clone()))); + dist.insert(obj.clone(), 1); + q.push(Distanced::one(obj)); + } + + while let Some(distanced) = q.pop() { + let node_dist = distanced.distance() + 1; + if node_dist > limit { + break; + } + + for (nrel, ndsts) in self + .src_to_dst + .get(&*distanced) + .iter() + .flat_map(|x| x.iter()) + { + for ndst in ndsts { + if (rel.is_none() || rel.as_ref() == Some(nrel)) + && (namespace.is_none() || namespace == Some(&ndst.namespace)) + { + related.push(((**nrel).clone(), (**ndst).clone())); + } + let obj = Arc::new(ObjectOrSet::Set(((**ndst).clone(), (**nrel).clone()))); + dist.insert(obj.clone(), node_dist); + q.push(Distanced::one(obj)); + } + } + } + + related + } + 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()) diff --git a/src/themis_proto.rs b/src/themis_proto.rs deleted file mode 100644 index 57cdbe0..0000000 --- a/src/themis_proto.rs +++ /dev/null @@ -1 +0,0 @@ -tonic::include_proto!("eu.zettoit.themis");