Skip to main content

grust_core/
lib.rs

1use std::{
2    collections::{BTreeMap, BTreeSet},
3    fmt,
4};
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9pub type Result<T> = std::result::Result<T, GrustError>;
10pub type Props = BTreeMap<String, Value>;
11
12#[derive(Debug, thiserror::Error)]
13pub enum GrustError {
14    #[error("backend error: {0}")]
15    Backend(String),
16    #[error("schema error: {0}")]
17    Schema(String),
18    #[error("unsupported graph feature: {0}")]
19    Unsupported(String),
20    #[error("serialization error: {0}")]
21    Serialization(String),
22}
23
24#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
25pub struct NodeId(String);
26
27impl NodeId {
28    pub fn new(value: impl Into<String>) -> Self {
29        Self(value.into())
30    }
31
32    pub fn as_str(&self) -> &str {
33        &self.0
34    }
35
36    pub fn into_string(self) -> String {
37        self.0
38    }
39}
40
41impl fmt::Display for NodeId {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        f.write_str(&self.0)
44    }
45}
46
47impl From<String> for NodeId {
48    fn from(value: String) -> Self {
49        Self::new(value)
50    }
51}
52
53impl From<&str> for NodeId {
54    fn from(value: &str) -> Self {
55        Self::new(value)
56    }
57}
58
59impl From<&String> for NodeId {
60    fn from(value: &String) -> Self {
61        Self::new(value.clone())
62    }
63}
64
65impl From<&NodeId> for NodeId {
66    fn from(value: &NodeId) -> Self {
67        value.clone()
68    }
69}
70
71#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
72pub struct EdgeId(String);
73
74impl EdgeId {
75    pub fn new(value: impl Into<String>) -> Self {
76        Self(value.into())
77    }
78
79    pub fn as_str(&self) -> &str {
80        &self.0
81    }
82
83    pub fn into_string(self) -> String {
84        self.0
85    }
86}
87
88impl fmt::Display for EdgeId {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.write_str(&self.0)
91    }
92}
93
94impl From<String> for EdgeId {
95    fn from(value: String) -> Self {
96        Self::new(value)
97    }
98}
99
100impl From<&str> for EdgeId {
101    fn from(value: &str) -> Self {
102        Self::new(value)
103    }
104}
105
106impl From<&String> for EdgeId {
107    fn from(value: &String) -> Self {
108        Self::new(value.clone())
109    }
110}
111
112impl From<&EdgeId> for EdgeId {
113    fn from(value: &EdgeId) -> Self {
114        value.clone()
115    }
116}
117
118#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
119pub struct Label(String);
120
121impl Label {
122    pub fn new(value: impl Into<String>) -> Self {
123        Self(value.into())
124    }
125
126    pub fn as_str(&self) -> &str {
127        &self.0
128    }
129
130    pub fn into_string(self) -> String {
131        self.0
132    }
133}
134
135impl fmt::Display for Label {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.write_str(&self.0)
138    }
139}
140
141impl From<String> for Label {
142    fn from(value: String) -> Self {
143        Self::new(value)
144    }
145}
146
147impl From<&str> for Label {
148    fn from(value: &str) -> Self {
149        Self::new(value)
150    }
151}
152
153impl From<&String> for Label {
154    fn from(value: &String) -> Self {
155        Self::new(value.clone())
156    }
157}
158
159impl From<&Label> for Label {
160    fn from(value: &Label) -> Self {
161        value.clone()
162    }
163}
164
165#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
166#[serde(tag = "type", content = "value", rename_all = "snake_case")]
167pub enum Value {
168    Null,
169    Bool(bool),
170    Int(i64),
171    Float(f64),
172    String(String),
173    StringArray(Vec<String>),
174    Json(serde_json::Value),
175}
176
177impl Value {
178    pub fn as_str(&self) -> Option<&str> {
179        match self {
180            Self::String(value) => Some(value),
181            _ => None,
182        }
183    }
184
185    pub fn as_string_array(&self) -> Option<&[String]> {
186        match self {
187            Self::StringArray(values) => Some(values),
188            _ => None,
189        }
190    }
191}
192
193impl From<String> for Value {
194    fn from(value: String) -> Self {
195        Self::String(value)
196    }
197}
198
199impl From<&str> for Value {
200    fn from(value: &str) -> Self {
201        Self::String(value.to_string())
202    }
203}
204
205impl From<&String> for Value {
206    fn from(value: &String) -> Self {
207        Self::String(value.clone())
208    }
209}
210
211impl From<Vec<String>> for Value {
212    fn from(value: Vec<String>) -> Self {
213        Self::StringArray(value)
214    }
215}
216
217impl From<bool> for Value {
218    fn from(value: bool) -> Self {
219        Self::Bool(value)
220    }
221}
222
223impl From<i64> for Value {
224    fn from(value: i64) -> Self {
225        Self::Int(value)
226    }
227}
228
229impl From<i32> for Value {
230    fn from(value: i32) -> Self {
231        Self::Int(i64::from(value))
232    }
233}
234
235impl From<usize> for Value {
236    fn from(value: usize) -> Self {
237        Self::Int(value as i64)
238    }
239}
240
241impl From<f64> for Value {
242    fn from(value: f64) -> Self {
243        Self::Float(value)
244    }
245}
246
247impl From<serde_json::Value> for Value {
248    fn from(value: serde_json::Value) -> Self {
249        match value {
250            serde_json::Value::Null => Self::Null,
251            serde_json::Value::Bool(value) => Self::Bool(value),
252            serde_json::Value::Number(value) => {
253                if let Some(value) = value.as_i64() {
254                    Self::Int(value)
255                } else if let Some(value) = value.as_f64() {
256                    Self::Float(value)
257                } else {
258                    Self::Json(serde_json::Value::Number(value))
259                }
260            }
261            serde_json::Value::String(value) => Self::String(value),
262            serde_json::Value::Array(values) => {
263                let strings = values
264                    .iter()
265                    .filter_map(|value| value.as_str().map(ToString::to_string))
266                    .collect::<Vec<_>>();
267                if strings.len() == values.len() {
268                    Self::StringArray(strings)
269                } else {
270                    Self::Json(serde_json::Value::Array(values))
271                }
272            }
273            serde_json::Value::Object(value) => Self::Json(serde_json::Value::Object(value)),
274        }
275    }
276}
277
278#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
279pub struct Node {
280    pub id: NodeId,
281    pub label: Label,
282    pub props: Props,
283}
284
285impl Node {
286    pub fn new(label: impl Into<Label>, id: impl Into<NodeId>, props: impl Into<Props>) -> Self {
287        let id = id.into();
288        let mut props = props.into();
289        props
290            .entry("id".to_string())
291            .or_insert_with(|| Value::from(id.as_str()));
292        Self {
293            id,
294            label: label.into(),
295            props,
296        }
297    }
298}
299
300#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
301pub struct Edge {
302    pub id: Option<EdgeId>,
303    pub from: NodeId,
304    pub to: NodeId,
305    pub label: Label,
306    pub props: Props,
307}
308
309impl Edge {
310    pub fn new(
311        label: impl Into<Label>,
312        from: impl Into<NodeId>,
313        to: impl Into<NodeId>,
314        props: impl Into<Props>,
315    ) -> Self {
316        Self {
317            id: None,
318            from: from.into(),
319            to: to.into(),
320            label: label.into(),
321            props: props.into(),
322        }
323    }
324
325    pub fn with_id(mut self, id: impl Into<EdgeId>) -> Self {
326        self.id = Some(id.into());
327        self
328    }
329}
330
331#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
332pub struct Graph {
333    pub nodes: Vec<Node>,
334    pub edges: Vec<Edge>,
335}
336
337impl Graph {
338    pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self {
339        Self { nodes, edges }
340    }
341
342    pub fn from_yaml(yaml: &str) -> Result<Self> {
343        yaml::graph_from_yaml(yaml)
344    }
345
346    pub fn to_yaml(&self) -> Result<String> {
347        yaml::graph_to_yaml(self)
348    }
349
350    pub fn from_json(json: &str) -> Result<Self> {
351        json::graph_from_json(json)
352    }
353
354    pub fn to_json(&self) -> Result<String> {
355        json::graph_to_json(self)
356    }
357
358    pub fn from_xml(xml: &str) -> Result<Self> {
359        xml::graph_from_xml(xml)
360    }
361
362    pub fn to_xml(&self) -> Result<String> {
363        xml::graph_to_xml(self)
364    }
365
366    pub fn builder() -> GraphBuilder {
367        GraphBuilder::new()
368    }
369}
370
371mod graph_doc {
372    use std::collections::{BTreeMap, BTreeSet};
373
374    use serde::{Deserialize, Serialize};
375
376    use crate::{Edge, EdgeId, Graph, GrustError, Label, Node, NodeId, Props, Value};
377
378    #[derive(Debug, Serialize, Deserialize)]
379    pub(super) struct GraphDoc {
380        #[serde(default)]
381        pub(super) nodes: Vec<NodeDoc>,
382        #[serde(default)]
383        pub(super) edges: Vec<EdgeDoc>,
384    }
385
386    #[derive(Debug, Serialize, Deserialize)]
387    pub(super) struct NodeDoc {
388        pub(super) id: NodeId,
389        pub(super) label: Label,
390        #[serde(default, deserialize_with = "deserialize_props")]
391        pub(super) props: Props,
392    }
393
394    #[derive(Debug, Serialize, Deserialize)]
395    pub(super) struct NodeDocOut {
396        pub(super) id: NodeId,
397        pub(super) label: Label,
398        #[serde(default)]
399        pub(super) props: Props,
400    }
401
402    #[derive(Debug, Serialize, Deserialize)]
403    pub(super) struct EdgeDoc {
404        #[serde(default)]
405        pub(super) id: Option<EdgeId>,
406        pub(super) label: Label,
407        pub(super) from: NodeId,
408        pub(super) to: NodeId,
409        #[serde(default, deserialize_with = "deserialize_props")]
410        pub(super) props: Props,
411    }
412
413    #[derive(Debug, Serialize, Deserialize)]
414    pub(super) struct EdgeDocOut {
415        #[serde(default)]
416        pub(super) id: Option<EdgeId>,
417        pub(super) label: Label,
418        pub(super) from: NodeId,
419        pub(super) to: NodeId,
420        #[serde(default)]
421        pub(super) props: Props,
422    }
423
424    pub(super) fn graph_from_doc(doc: GraphDoc) -> super::Result<Graph> {
425        let mut ids = BTreeSet::new();
426        for node in &doc.nodes {
427            if !ids.insert(node.id.clone()) {
428                return Err(GrustError::Schema(format!(
429                    "duplicate node id '{}'",
430                    node.id
431                )));
432            }
433        }
434
435        let mut edges = Vec::with_capacity(doc.edges.len());
436        for edge in doc.edges {
437            if !ids.contains(&edge.from) {
438                return Err(GrustError::Schema(format!(
439                    "edge '{}' references unknown from node '{}'",
440                    edge.label, edge.from
441                )));
442            }
443            if !ids.contains(&edge.to) {
444                return Err(GrustError::Schema(format!(
445                    "edge '{}' references unknown to node '{}'",
446                    edge.label, edge.to
447                )));
448            }
449
450            let mut graph_edge = Edge::new(edge.label, edge.from, edge.to, edge.props);
451            graph_edge.id = edge.id;
452            edges.push(graph_edge);
453        }
454
455        let nodes = doc
456            .nodes
457            .into_iter()
458            .map(|node| Node::new(node.label, node.id, node.props))
459            .collect();
460
461        Ok(Graph::new(nodes, edges))
462    }
463
464    pub(super) fn graph_to_doc(graph: &Graph) -> GraphDocOut {
465        GraphDocOut {
466            nodes: graph
467                .nodes
468                .iter()
469                .map(|node| NodeDocOut {
470                    id: node.id.clone(),
471                    label: node.label.clone(),
472                    props: without_generated_id(&node.props, &node.id),
473                })
474                .collect(),
475            edges: graph
476                .edges
477                .iter()
478                .map(|edge| EdgeDocOut {
479                    id: edge.id.clone(),
480                    label: edge.label.clone(),
481                    from: edge.from.clone(),
482                    to: edge.to.clone(),
483                    props: edge.props.clone(),
484                })
485                .collect(),
486        }
487    }
488
489    fn without_generated_id(props: &Props, id: &NodeId) -> Props {
490        let mut props = props.clone();
491        if props.get("id") == Some(&Value::from(id.as_str())) {
492            props.remove("id");
493        }
494        props
495    }
496
497    fn deserialize_props<'de, D>(deserializer: D) -> std::result::Result<Props, D::Error>
498    where
499        D: serde::Deserializer<'de>,
500    {
501        let raw = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
502        raw.into_iter()
503            .map(|(key, value)| {
504                value_from_json(value)
505                    .map(|value| (key, value))
506                    .map_err(serde::de::Error::custom)
507            })
508            .collect()
509    }
510
511    fn value_from_json(value: serde_json::Value) -> std::result::Result<Value, String> {
512        if let serde_json::Value::Object(mapping) = &value
513            && mapping.contains_key("type")
514            && mapping.contains_key("value")
515        {
516            return serde_json::from_value(value)
517                .map_err(|err| format!("invalid tagged Grust value: {err}"));
518        }
519
520        Ok(Value::from(value))
521    }
522
523    #[derive(Debug, Serialize, Deserialize)]
524    pub(super) struct GraphDocOut {
525        pub(super) nodes: Vec<NodeDocOut>,
526        pub(super) edges: Vec<EdgeDocOut>,
527    }
528}
529
530mod yaml {
531    use crate::{Graph, GrustError};
532
533    pub(super) fn graph_from_yaml(yaml: &str) -> super::Result<Graph> {
534        let doc: super::graph_doc::GraphDoc = serde_yaml::from_str(yaml)
535            .map_err(|err| GrustError::Serialization(format!("YAML parse error: {err}")))?;
536        super::graph_doc::graph_from_doc(doc)
537    }
538
539    pub(super) fn graph_to_yaml(graph: &Graph) -> super::Result<String> {
540        serde_yaml::to_string(&super::graph_doc::graph_to_doc(graph))
541            .map_err(|err| GrustError::Serialization(format!("YAML serialization error: {err}")))
542    }
543}
544
545mod json {
546    use crate::{Graph, GrustError};
547
548    pub(super) fn graph_from_json(json: &str) -> super::Result<Graph> {
549        let doc: super::graph_doc::GraphDoc = serde_json::from_str(json)
550            .map_err(|err| GrustError::Serialization(format!("JSON parse error: {err}")))?;
551        super::graph_doc::graph_from_doc(doc)
552    }
553
554    pub(super) fn graph_to_json(graph: &Graph) -> super::Result<String> {
555        serde_json::to_string_pretty(&super::graph_doc::graph_to_doc(graph))
556            .map_err(|err| GrustError::Serialization(format!("JSON serialization error: {err}")))
557    }
558}
559
560mod xml {
561    use serde::{Deserialize, Serialize};
562
563    use crate::{Edge, EdgeId, Graph, GrustError, Label, Node, NodeId, Props, Value};
564
565    #[derive(Debug, Serialize, Deserialize)]
566    #[serde(rename = "graph")]
567    struct GraphXml {
568        #[serde(default)]
569        nodes: NodesXml,
570        #[serde(default)]
571        edges: EdgesXml,
572    }
573
574    #[derive(Debug, Default, Serialize, Deserialize)]
575    struct NodesXml {
576        #[serde(rename = "node", default)]
577        items: Vec<NodeXml>,
578    }
579
580    #[derive(Debug, Default, Serialize, Deserialize)]
581    struct EdgesXml {
582        #[serde(rename = "edge", default)]
583        items: Vec<EdgeXml>,
584    }
585
586    #[derive(Debug, Serialize, Deserialize)]
587    struct NodeXml {
588        id: NodeId,
589        label: Label,
590        #[serde(default)]
591        props: PropsXml,
592    }
593
594    #[derive(Debug, Serialize, Deserialize)]
595    struct EdgeXml {
596        #[serde(default, skip_serializing_if = "Option::is_none")]
597        id: Option<EdgeId>,
598        label: Label,
599        from: NodeId,
600        to: NodeId,
601        #[serde(default)]
602        props: PropsXml,
603    }
604
605    #[derive(Debug, Default, Serialize, Deserialize)]
606    struct PropsXml {
607        #[serde(rename = "prop", default)]
608        items: Vec<PropXml>,
609    }
610
611    #[derive(Debug, Serialize, Deserialize)]
612    struct PropXml {
613        key: String,
614        value: Value,
615    }
616
617    pub(super) fn graph_from_xml(xml: &str) -> super::Result<Graph> {
618        let doc: GraphXml = quick_xml::de::from_str(xml)
619            .map_err(|err| GrustError::Serialization(format!("XML parse error: {err}")))?;
620        super::graph_doc::graph_from_doc(doc.into())
621    }
622
623    pub(super) fn graph_to_xml(graph: &Graph) -> super::Result<String> {
624        quick_xml::se::to_string(&GraphXml::from(graph))
625            .map_err(|err| GrustError::Serialization(format!("XML serialization error: {err}")))
626    }
627
628    impl From<GraphXml> for super::graph_doc::GraphDoc {
629        fn from(value: GraphXml) -> Self {
630            Self {
631                nodes: value.nodes.items.into_iter().map(Into::into).collect(),
632                edges: value.edges.items.into_iter().map(Into::into).collect(),
633            }
634        }
635    }
636
637    impl From<NodeXml> for super::graph_doc::NodeDoc {
638        fn from(value: NodeXml) -> Self {
639            Self {
640                id: value.id,
641                label: value.label,
642                props: value.props.into(),
643            }
644        }
645    }
646
647    impl From<EdgeXml> for super::graph_doc::EdgeDoc {
648        fn from(value: EdgeXml) -> Self {
649            Self {
650                id: value.id,
651                label: value.label,
652                from: value.from,
653                to: value.to,
654                props: value.props.into(),
655            }
656        }
657    }
658
659    impl From<PropsXml> for Props {
660        fn from(value: PropsXml) -> Self {
661            value
662                .items
663                .into_iter()
664                .map(|prop| (prop.key, prop.value))
665                .collect()
666        }
667    }
668
669    impl From<&Graph> for GraphXml {
670        fn from(graph: &Graph) -> Self {
671            Self {
672                nodes: NodesXml {
673                    items: graph.nodes.iter().map(NodeXml::from).collect(),
674                },
675                edges: EdgesXml {
676                    items: graph.edges.iter().map(EdgeXml::from).collect(),
677                },
678            }
679        }
680    }
681
682    impl From<&Node> for NodeXml {
683        fn from(node: &Node) -> Self {
684            let props = super::graph_doc::graph_to_doc(&Graph::new(vec![node.clone()], Vec::new()))
685                .nodes
686                .into_iter()
687                .next()
688                .expect("node exists")
689                .props;
690            Self {
691                id: node.id.clone(),
692                label: node.label.clone(),
693                props: props.into(),
694            }
695        }
696    }
697
698    impl From<&Edge> for EdgeXml {
699        fn from(edge: &Edge) -> Self {
700            Self {
701                id: edge.id.clone(),
702                label: edge.label.clone(),
703                from: edge.from.clone(),
704                to: edge.to.clone(),
705                props: edge.props.clone().into(),
706            }
707        }
708    }
709
710    impl From<Props> for PropsXml {
711        fn from(value: Props) -> Self {
712            Self {
713                items: value
714                    .into_iter()
715                    .map(|(key, value)| PropXml { key, value })
716                    .collect(),
717            }
718        }
719    }
720}
721
722#[derive(Clone, Debug, Eq, PartialEq)]
723pub enum EdgePolicy {
724    AllowDuplicates,
725    DedupeByFromLabelTo,
726}
727
728impl Default for EdgePolicy {
729    fn default() -> Self {
730        Self::DedupeByFromLabelTo
731    }
732}
733
734#[derive(Clone, Debug, Default)]
735pub struct GraphBuilder {
736    nodes: BTreeMap<NodeId, Node>,
737    edges: Vec<Edge>,
738    edge_keys: BTreeSet<(NodeId, Label, NodeId)>,
739    edge_policy: EdgePolicy,
740}
741
742impl GraphBuilder {
743    pub fn new() -> Self {
744        Self::default()
745    }
746
747    pub fn edge_policy(mut self, edge_policy: EdgePolicy) -> Self {
748        self.edge_policy = edge_policy;
749        self
750    }
751
752    pub fn node<'a>(
753        &'a mut self,
754        label: impl Into<Label>,
755        id: impl Into<NodeId>,
756    ) -> NodeBuilder<'a> {
757        NodeBuilder {
758            builder: self,
759            label: label.into(),
760            id: id.into(),
761            props: Props::new(),
762        }
763    }
764
765    pub fn edge<'a>(
766        &'a mut self,
767        label: impl Into<Label>,
768        from: impl Into<NodeId>,
769        to: impl Into<NodeId>,
770    ) -> EdgeBuilder<'a> {
771        EdgeBuilder {
772            builder: self,
773            id: None,
774            label: label.into(),
775            from: from.into(),
776            to: to.into(),
777            props: Props::new(),
778        }
779    }
780
781    pub fn add_node(&mut self, node: Node) -> NodeId {
782        let id = node.id.clone();
783        self.nodes
784            .entry(id.clone())
785            .and_modify(|existing| {
786                if existing.label == node.label {
787                    existing.props.extend(node.props.clone());
788                }
789            })
790            .or_insert(node);
791        id
792    }
793
794    pub fn add_edge(&mut self, edge: Edge) -> Option<EdgeId> {
795        let id = edge.id.clone();
796        match self.edge_policy {
797            EdgePolicy::AllowDuplicates => self.edges.push(edge),
798            EdgePolicy::DedupeByFromLabelTo => {
799                let key = (edge.from.clone(), edge.label.clone(), edge.to.clone());
800                if self.edge_keys.insert(key) {
801                    self.edges.push(edge);
802                }
803            }
804        }
805        id
806    }
807
808    pub fn build(self) -> Graph {
809        Graph {
810            nodes: self.nodes.into_values().collect(),
811            edges: self.edges,
812        }
813    }
814}
815
816pub struct NodeBuilder<'a> {
817    builder: &'a mut GraphBuilder,
818    label: Label,
819    id: NodeId,
820    props: Props,
821}
822
823impl<'a> NodeBuilder<'a> {
824    pub fn prop(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
825        self.props.insert(key.into(), value.into());
826        self
827    }
828
829    pub fn props(mut self, props: Props) -> Self {
830        self.props.extend(props);
831        self
832    }
833
834    pub fn finish(self) -> NodeId {
835        let node = Node::new(self.label, self.id, self.props);
836        self.builder.add_node(node)
837    }
838}
839
840pub struct EdgeBuilder<'a> {
841    builder: &'a mut GraphBuilder,
842    id: Option<EdgeId>,
843    label: Label,
844    from: NodeId,
845    to: NodeId,
846    props: Props,
847}
848
849impl<'a> EdgeBuilder<'a> {
850    pub fn id(mut self, id: impl Into<EdgeId>) -> Self {
851        self.id = Some(id.into());
852        self
853    }
854
855    pub fn prop(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
856        self.props.insert(key.into(), value.into());
857        self
858    }
859
860    pub fn props(mut self, props: Props) -> Self {
861        self.props.extend(props);
862        self
863    }
864
865    pub fn finish(self) -> Option<EdgeId> {
866        let mut edge = Edge::new(self.label, self.from, self.to, self.props);
867        edge.id = self.id;
868        self.builder.add_edge(edge)
869    }
870}
871
872#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
873pub struct GraphSchema {
874    pub nodes: Vec<NodeType>,
875    pub edges: Vec<EdgeType>,
876}
877
878#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
879pub struct NodeType {
880    pub label: Label,
881    pub fields: Vec<Field>,
882}
883
884#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
885pub struct EdgeType {
886    pub label: Label,
887    pub from: Vec<Label>,
888    pub to: Vec<Label>,
889    pub fields: Vec<Field>,
890    pub directed: bool,
891    pub uniqueness: EdgeUniqueness,
892}
893
894#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
895pub struct Field {
896    pub name: String,
897    pub ty: FieldType,
898    pub required: bool,
899}
900
901#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
902pub enum FieldType {
903    String,
904    Int,
905    Float,
906    Bool,
907    DateTime,
908    StringArray,
909    Json,
910}
911
912#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
913pub enum EdgeUniqueness {
914    None,
915    FromTo,
916    FromLabelTo,
917}
918
919#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
920pub struct Traversal {
921    pub start: Start,
922    pub steps: Vec<Step>,
923    pub limit: Option<u32>,
924}
925
926impl Traversal {
927    pub fn from_node(id: impl Into<NodeId>) -> Self {
928        Self {
929            start: Start::Node(id.into()),
930            steps: Vec::new(),
931            limit: None,
932        }
933    }
934
935    pub fn out(mut self, edge: impl Into<Label>) -> Self {
936        self.steps.push(Step {
937            direction: Direction::Out,
938            edge: Some(edge.into()),
939            node: None,
940        });
941        self
942    }
943
944    pub fn in_(mut self, edge: impl Into<Label>) -> Self {
945        self.steps.push(Step {
946            direction: Direction::In,
947            edge: Some(edge.into()),
948            node: None,
949        });
950        self
951    }
952
953    pub fn both(mut self, edge: impl Into<Label>) -> Self {
954        self.steps.push(Step {
955            direction: Direction::Both,
956            edge: Some(edge.into()),
957            node: None,
958        });
959        self
960    }
961
962    pub fn to(mut self, node: impl Into<Label>) -> Self {
963        if let Some(step) = self.steps.last_mut() {
964            step.node = Some(node.into());
965        }
966        self
967    }
968
969    pub fn limit(mut self, limit: u32) -> Self {
970        self.limit = Some(limit);
971        self
972    }
973}
974
975#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
976pub enum Start {
977    Node(NodeId),
978    NodesByLabel(Label),
979    NodesByProperty {
980        label: Label,
981        key: String,
982        value: Value,
983    },
984}
985
986#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
987pub struct Step {
988    pub direction: Direction,
989    pub edge: Option<Label>,
990    pub node: Option<Label>,
991}
992
993#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
994pub enum Direction {
995    Out,
996    In,
997    Both,
998}
999
1000#[derive(Clone, Debug, Default, PartialEq)]
1001pub struct EdgeQuery {
1002    pub from: Option<NodeId>,
1003    pub to: Option<NodeId>,
1004    pub label: Option<Label>,
1005}
1006
1007#[derive(Clone, Debug, Default, Eq, PartialEq)]
1008pub struct LoadReport {
1009    pub nodes: usize,
1010    pub edges: usize,
1011}
1012
1013#[async_trait]
1014pub trait GraphStore: Send + Sync {
1015    async fn apply_schema(&self, _schema: &GraphSchema) -> Result<()> {
1016        Ok(())
1017    }
1018
1019    async fn put_node(&self, node: &Node) -> Result<NodeId>;
1020    async fn put_edge(&self, edge: &Edge) -> Result<Option<EdgeId>>;
1021
1022    async fn put_graph(&self, graph: &Graph) -> Result<LoadReport> {
1023        let mut report = LoadReport::default();
1024        for node in &graph.nodes {
1025            self.put_node(node).await?;
1026            report.nodes += 1;
1027        }
1028        for edge in &graph.edges {
1029            self.put_edge(edge).await?;
1030            report.edges += 1;
1031        }
1032        Ok(report)
1033    }
1034
1035    async fn get_node(&self, id: &NodeId) -> Result<Option<Node>>;
1036    async fn get_edges(&self, query: EdgeQuery) -> Result<Vec<Edge>>;
1037    async fn traverse(&self, traversal: Traversal) -> Result<Vec<Node>>;
1038}
1039
1040#[async_trait]
1041pub trait GraphAdminStore: GraphStore {
1042    async fn bootstrap(&self) -> Result<()> {
1043        Ok(())
1044    }
1045
1046    async fn clear(&self) -> Result<()>;
1047}
1048
1049pub mod prelude {
1050    pub use crate::{
1051        Direction, Edge, EdgeId, EdgePolicy, EdgeQuery, EdgeType, Field, FieldType, Graph,
1052        GraphAdminStore, GraphBuilder, GraphSchema, GraphStore, GrustError, Label, LoadReport,
1053        Node, NodeId, NodeType, Props, Result, Start, Step, Traversal, Value,
1054    };
1055}
1056
1057#[cfg(test)]
1058mod tests;