new backend

This commit is contained in:
Paul Zinselmeyer 2023-05-01 02:08:22 +02:00
parent dbd581cece
commit 1b53e041e3
Signed by: pfzetto
GPG key ID: 4EEF46A5B276E648
7 changed files with 1073 additions and 381 deletions

35
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

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

View file

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

View file

@ -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(())
}
} }

View file

@ -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
View 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())
}
}