rename to rebacs, proto rework and implemented missing functions

This commit is contained in:
Paul Zinselmeyer 2023-06-05 22:17:22 +02:00
parent 0b1af2771c
commit 21f0c705fe
9 changed files with 413 additions and 255 deletions

View file

@ -1,5 +1,5 @@
[package] [package]
name = "themis" name = "rebacs"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View file

@ -1,6 +1,6 @@
fn main() { fn main() {
tonic_build::configure() tonic_build::configure()
.build_server(true) .build_server(true)
.compile(&["proto/themis.proto"], &["proto"]) .compile(&["proto/rebacs.proto"], &["proto"])
.unwrap(); .unwrap();
} }

95
proto/rebacs.proto Normal file
View file

@ -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;
}

View file

@ -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{}

View file

@ -8,12 +8,14 @@ use tokio::sync::Mutex;
use tonic::metadata::MetadataMap; use tonic::metadata::MetadataMap;
use tonic::{Request, Response, Status}; use tonic::{Request, Response, Status};
use crate::relation_set::{ObjectOrSet, RelationSet}; use crate::rebacs_proto::{
use crate::themis_proto::{ query_service_server::QueryService, relation_service_server::RelationService, Object,
query_service_server::QueryService, relation::Src, relation_service_server::RelationService, QueryGetRelatedItem, QueryGetRelatedReq, QueryGetRelatedRes, QueryGetRelationsItem,
Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse, QueryGetRelationsReq, QueryGetRelationsRes, QueryIsRelatedToReq, QueryIsRelatedToRes,
IsRelatedToResponse, Relation, Set, RelationCreateReq, RelationCreateRes, RelationDeleteReq, RelationDeleteRes, RelationExistsReq,
RelationExistsRes,
}; };
use crate::relation_set::{ObjectOrSet, RelationSet};
#[derive(Clone)] #[derive(Clone)]
pub struct GraphService { pub struct GraphService {
@ -22,9 +24,15 @@ pub struct GraphService {
pub save_trigger: Sender<()>, pub save_trigger: Sender<()>,
} }
const API_KEY_NS: &str = "rebacs_key";
const NAMESPACE_NS: &str = "rebacs_ns";
#[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<RelationCreateReq>,
) -> Result<Response<RelationCreateRes>, Status> {
let mut graph = self.graph.lock().await; let mut graph = self.graph.lock().await;
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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( if !graph.has_recursive(
("themis_key", &*api_key), (API_KEY_NS, &*api_key),
"write", "write",
("themis_ns", &*req_dst.namespace), (NAMESPACE_NS, &*req_dst.namespace),
u32::MAX, u32::MAX,
) { ) {
return Err(Status::permission_denied( return Err(Status::permission_denied(
@ -62,32 +70,21 @@ impl RelationService for GraphService {
))?; ))?;
} }
let src: Result<ObjectOrSet, Status> = match req_src { if req_src.namespace.is_empty() {
Src::SrcObj(obj) => { return Err(Status::invalid_argument("src.namespace must be set"));
if obj.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"));
if obj.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"));
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()) (&*req_src.namespace, &*req_src.id, req_src_relation).into()
} } else {
(&*req_src.namespace, &*req_src.id).into()
}; };
let src = src?;
graph.insert( graph.insert(
src.clone(), src.clone(),
@ -99,9 +96,12 @@ impl RelationService for GraphService {
self.save_trigger.send(()).await.unwrap(); self.save_trigger.send(()).await.unwrap();
Ok(Response::new(Empty {})) Ok(Response::new(RelationCreateRes {}))
} }
async fn delete(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> { async fn delete(
&self,
request: Request<RelationDeleteReq>,
) -> Result<Response<RelationDeleteRes>, Status> {
let mut graph = self.graph.lock().await; let mut graph = self.graph.lock().await;
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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( if !graph.has_recursive(
("themis_key", &*api_key), (API_KEY_NS, &*api_key),
"write", "write",
("themis_ns", &*req_dst.namespace), (NAMESPACE_NS, &*req_dst.namespace),
u32::MAX, u32::MAX,
) { ) {
return Err(Status::permission_denied( return Err(Status::permission_denied(
"missing dst.namespace write permissions", "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()) 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)); 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(); self.save_trigger.send(()).await.unwrap();
Ok(Response::new(Empty {})) Ok(Response::new(RelationDeleteRes {}))
} }
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> { async fn exists(
&self,
request: Request<RelationExistsReq>,
) -> Result<Response<RelationExistsRes>, Status> {
let graph = self.graph.lock().await; let graph = self.graph.lock().await;
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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( if !graph.has_recursive(
("themis_key", &*api_key), (API_KEY_NS, &*api_key),
"read", "read",
("themis_ns", &*req_dst.namespace), (NAMESPACE_NS, &*req_dst.namespace),
u32::MAX, u32::MAX,
) { ) {
return Err(Status::permission_denied( return Err(Status::permission_denied(
"missing dst.namespace write permissions", "missing dst.namespace write permissions",
))?; ))?;
} }
let src: Result<ObjectOrSet, Status> = match req_src { if req_src.namespace.is_empty() {
Src::SrcObj(obj) => { return Err(Status::invalid_argument("src.namespace must be set"));
if obj.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"));
if obj.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"));
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()) (&*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)); 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 { impl QueryService for GraphService {
async fn is_related_to( async fn is_related_to(
&self, &self,
request: Request<Relation>, request: Request<QueryIsRelatedToReq>,
) -> Result<Response<IsRelatedToResponse>, Status> { ) -> Result<Response<QueryIsRelatedToRes>, Status> {
let graph = self.graph.lock().await; let graph = self.graph.lock().await;
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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( if !graph.has_recursive(
("themis_key", &*api_key), (API_KEY_NS, &*api_key),
"read", "read",
("themis_ns", &*req_dst.namespace), (NAMESPACE_NS, &*req_dst.namespace),
u32::MAX, u32::MAX,
) { ) {
return Err(Status::permission_denied( return Err(Status::permission_denied(
@ -286,32 +268,21 @@ impl QueryService for GraphService {
))?; ))?;
} }
let src: Result<ObjectOrSet, Status> = match req_src { if req_src.namespace.is_empty() {
Src::SrcObj(obj) => { return Err(Status::invalid_argument("src.namespace must be set"));
if obj.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"));
if obj.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"));
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()) (&*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( let related = graph.has_recursive(
src, src,
@ -320,90 +291,113 @@ impl QueryService for GraphService {
u32::MAX, u32::MAX,
); );
Ok(Response::new(IsRelatedToResponse { related })) Ok(Response::new(QueryIsRelatedToRes { related }))
} }
async fn get_related_to( async fn get_related(
&self, &self,
request: Request<Set>, request: Request<QueryGetRelatedReq>,
) -> Result<Response<GetRelatedToResponse>, Status> { ) -> Result<Response<QueryGetRelatedRes>, Status> {
//let graph = self.graph.lock().await; let graph = self.graph.lock().await;
//authenticate( let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
// request.metadata(),
// &graph,
// &self.api_keys,
// &request.get_ref().namespace,
// "read",
//)
//.await?;
//let obj = graph let req_dst = request
// .get_node(&request.get_ref().namespace, &request.get_ref().id) .get_ref()
// .ok_or(Status::not_found("object not found"))?; .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 { let req_namespace = &request.get_ref().namespace;
// objects: graph let req_depth = &request.get_ref().depth;
// .related_to(obj, rel)
// .into_iter() if !graph.has_recursive(
// .map(|x| { (API_KEY_NS, &*api_key),
// let obj = graph.object_from_ref(&x); "read",
// Object { (NAMESPACE_NS, &*req_dst.namespace),
// namespace: obj.namespace.to_string(), u32::MAX,
// id: obj.id, ) {
// } return Err(Status::permission_denied(
// }) "missing dst.namespace read permissions",
// .collect::<Vec<_>>(), ))?;
//})) }
todo!()
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( async fn get_relations(
&self, &self,
request: Request<GetRelationsRequest>, request: Request<QueryGetRelationsReq>,
) -> Result<Response<GetRelationsResponse>, Status> { ) -> Result<Response<QueryGetRelationsRes>, Status> {
//let graph = self.graph.lock().await; let graph = self.graph.lock().await;
//if request.get_ref().relation.is_empty() { let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
// return Err(Status::invalid_argument("relation must be set"));
//}
//let obj = request let req_src = request
// .get_ref() .get_ref()
// .object .src
// .as_ref() .as_ref()
// .ok_or(Status::invalid_argument("object must be set"))?; .ok_or(Status::invalid_argument("src must be set"))?;
let src = (&*req_src.namespace, &*req_src.id);
//authenticate( let req_rel = &request.get_ref().relation;
// request.metadata(), let req_namespace = &request.get_ref().namespace;
// &graph, let req_depth = &request.get_ref().depth;
// &self.api_keys,
// &obj.namespace,
// "read",
//)
//.await?;
//let obj = graph if !graph.has_recursive(
// .get_node(&obj.namespace, &obj.id) (API_KEY_NS, &*api_key),
// .ok_or(Status::not_found("object not found"))?; "read",
(NAMESPACE_NS, &*req_src.namespace),
u32::MAX,
) {
return Err(Status::permission_denied(
"missing src.namespace read permissions",
))?;
}
//Ok(Response::new(GetRelationsResponse { let related = graph
// objects: graph .relations(
// .relations(ObjectRelation( src,
// obj, req_rel.as_deref(),
// graph::Relation::new(&request.get_ref().relation), req_namespace.as_deref(),
// )) req_depth.unwrap_or(u32::MAX),
// .into_iter() )
// .map(|x| { .into_iter()
// let obj = graph.object_from_ref(&x); .map(|x| QueryGetRelationsItem {
// Object { dst: Some(Object {
// namespace: obj.namespace.to_string(), namespace: x.1.namespace.to_string(),
// id: obj.id, id: x.1.id.to_string(),
// } }),
// }) relation: x.0 .0.to_string(),
// .collect::<Vec<_>>(), })
//})) .collect::<_>();
todo!()
Ok(Response::new(QueryGetRelationsRes { related }))
} }
} }

View file

@ -14,10 +14,10 @@ use tokio::{
use tonic::transport::Server; use tonic::transport::Server;
pub mod grpc_service; pub mod grpc_service;
pub mod rebacs_proto;
pub mod relation_set; pub mod relation_set;
pub mod themis_proto;
use crate::themis_proto::{ use crate::rebacs_proto::{
query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer, query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer,
}; };

1
src/rebacs_proto.rs Normal file
View file

@ -0,0 +1 @@
tonic::include_proto!("eu.zettoit.rebacs");

View file

@ -195,6 +195,9 @@ impl RelationSet {
while let Some(distanced) = q.pop() { while let Some(distanced) = q.pop() {
let node_dist = distanced.distance() + 1; let node_dist = distanced.distance() + 1;
if node_dist > limit {
break;
}
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone())); let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
for (nrel, ndst) in self for (nrel, ndst) in self
.src_to_dst .src_to_dst
@ -207,7 +210,7 @@ impl RelationSet {
return true; return true;
} }
if let Some(existing_node_dist) = dist.get(&*distanced) { 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; continue;
} }
} }
@ -218,6 +221,139 @@ impl RelationSet {
false false
} }
pub fn related_to(
&self,
dst: impl Into<D>,
rel: Option<impl Into<R>>,
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<Object>, Arc<Relation>), u32> = HashMap::new();
let mut q: BinaryHeap<Distanced<(Arc<Object>, Arc<Relation>)>> = 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<S>,
rel: Option<impl Into<R>>,
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<Arc<ObjectOrSet>, u32> = HashMap::new();
let mut q: BinaryHeap<Distanced<Arc<ObjectOrSet>>> = 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) { pub async fn to_file(&self, file: &mut File) {
for (dst, rels_srcs) in self.dst_to_src.iter() { for (dst, rels_srcs) in self.dst_to_src.iter() {
file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes()) file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes())

View file

@ -1 +0,0 @@
tonic::include_proto!("eu.zettoit.themis");