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;