nodex_core/model/
graph.rs1use indexmap::IndexMap;
2use serde::ser::SerializeStruct;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6use super::edge::Edge;
7use super::node::Node;
8
9pub struct Graph {
12 nodes: IndexMap<String, Node>,
13 edges: Vec<Edge>,
14 incoming: BTreeMap<String, Vec<usize>>,
15 outgoing: BTreeMap<String, Vec<usize>>,
16}
17
18impl Graph {
19 pub fn new(nodes: IndexMap<String, Node>, edges: Vec<Edge>) -> Self {
21 let (incoming, outgoing) = build_indices(&edges);
22 Self {
23 nodes,
24 edges,
25 incoming,
26 outgoing,
27 }
28 }
29
30 pub fn node(&self, id: &str) -> Option<&Node> {
31 self.nodes.get(id)
32 }
33
34 pub fn nodes(&self) -> &IndexMap<String, Node> {
35 &self.nodes
36 }
37
38 pub fn edges(&self) -> &[Edge] {
39 &self.edges
40 }
41
42 pub fn incoming_indices(&self, id: &str) -> &[usize] {
44 self.incoming
45 .get(id)
46 .map(|v| v.as_slice())
47 .unwrap_or_default()
48 }
49
50 fn outgoing_indices(&self, id: &str) -> &[usize] {
51 self.outgoing
52 .get(id)
53 .map(|v| v.as_slice())
54 .unwrap_or_default()
55 }
56
57 pub fn incoming_edges(&self, id: &str) -> Vec<&Edge> {
59 self.incoming_indices(id)
60 .iter()
61 .filter_map(|&idx| self.edges.get(idx))
62 .collect()
63 }
64
65 pub fn outgoing_edges(&self, id: &str) -> Vec<&Edge> {
67 self.outgoing_indices(id)
68 .iter()
69 .filter_map(|&idx| self.edges.get(idx))
70 .collect()
71 }
72
73 pub fn node_count(&self) -> usize {
74 self.nodes.len()
75 }
76
77 pub fn edge_count(&self) -> usize {
78 self.edges.len()
79 }
80}
81
82fn build_indices(edges: &[Edge]) -> (BTreeMap<String, Vec<usize>>, BTreeMap<String, Vec<usize>>) {
83 let mut incoming: BTreeMap<String, Vec<usize>> = BTreeMap::new();
84 let mut outgoing: BTreeMap<String, Vec<usize>> = BTreeMap::new();
85
86 for (idx, edge) in edges.iter().enumerate() {
87 outgoing.entry(edge.source.clone()).or_default().push(idx);
88 if let Some(target_id) = edge.target.id() {
89 incoming.entry(target_id.to_string()).or_default().push(idx);
90 }
91 }
92
93 (incoming, outgoing)
94}
95
96pub const SCHEMA_VERSION: u32 = 1;
102
103impl Serialize for Graph {
106 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107 let mut s = serializer.serialize_struct("Graph", 3)?;
108 s.serialize_field("schema_version", &SCHEMA_VERSION)?;
109 s.serialize_field("nodes", &self.nodes)?;
110 s.serialize_field("edges", &self.edges)?;
111 s.end()
112 }
113}
114
115impl<'de> Deserialize<'de> for Graph {
124 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
125 #[derive(Deserialize)]
126 struct Raw {
127 #[serde(default)]
128 schema_version: u32,
129 nodes: IndexMap<String, Node>,
130 edges: Vec<Edge>,
131 }
132
133 let raw = Raw::deserialize(deserializer)?;
134 if raw.schema_version > SCHEMA_VERSION {
135 return Err(serde::de::Error::custom(format!(
136 "graph.json schema_version {} is newer than this binary supports ({}); \
137 run `nodex build --full` to regenerate",
138 raw.schema_version, SCHEMA_VERSION
139 )));
140 }
141 Ok(Graph::new(raw.nodes, raw.edges))
142 }
143}
144
145impl std::fmt::Debug for Graph {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 f.debug_struct("Graph")
148 .field("nodes", &self.nodes.len())
149 .field("edges", &self.edges.len())
150 .finish()
151 }
152}