Skip to main content

weavatrix_graph/graph/
builder.rs

1use super::core::Graph;
2use super::index::canonicalize_edges;
3use super::validate::{validate_edge, validate_node};
4use crate::{Edge, GraphError, Node, NodeId, Result};
5use crate::{ToString, Vec};
6#[cfg(not(feature = "std"))]
7use alloc::collections::BTreeMap as NodeMap;
8#[cfg(feature = "std")]
9use std::collections::HashMap as NodeMap;
10
11#[derive(Debug, Default)]
12pub struct GraphBuilder {
13    nodes: NodeMap<NodeId, Node>,
14    edges: Vec<Edge>,
15}
16
17#[cfg(feature = "std")]
18fn node_map_with_capacity(capacity: usize) -> NodeMap<NodeId, Node> {
19    NodeMap::with_capacity(capacity)
20}
21
22#[cfg(not(feature = "std"))]
23fn node_map_with_capacity(_: usize) -> NodeMap<NodeId, Node> {
24    NodeMap::new()
25}
26
27impl GraphBuilder {
28    #[must_use]
29    pub fn new() -> Self {
30        Self {
31            nodes: NodeMap::new(),
32            edges: Vec::new(),
33        }
34    }
35
36    /// Creates a builder with storage sized for the expected graph.
37    #[must_use]
38    pub fn with_capacity(nodes: usize, edges: usize) -> Self {
39        Self {
40            nodes: node_map_with_capacity(nodes),
41            edges: Vec::with_capacity(edges),
42        }
43    }
44
45    /// Adds a node idempotently.
46    ///
47    /// # Errors
48    ///
49    /// Returns an error when the same identifier already has a different
50    /// definition or the node contains an invalid source span.
51    pub fn add_node(&mut self, node: Node) -> Result<&mut Self> {
52        validate_node(&node)?;
53        if let Some(existing) = self.nodes.get(&node.id) {
54            if existing == &node {
55                return Ok(self);
56            }
57            return Err(GraphError::ConflictingNode {
58                id: node.id.to_string(),
59            });
60        }
61        self.nodes.insert(node.id.clone(), node);
62        Ok(self)
63    }
64
65    /// Adds an edge idempotently. Endpoint existence is validated by `build`,
66    /// so callers may insert edges before nodes.
67    ///
68    /// # Errors
69    ///
70    /// Returns an error when provenance or its source span is invalid.
71    pub fn add_edge(&mut self, edge: Edge) -> Result<&mut Self> {
72        validate_edge(&edge)?;
73        self.edges.push(edge);
74        Ok(self)
75    }
76
77    /// Validates all endpoints and returns an immutable graph.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error when an edge references a missing source or target.
82    pub fn build(self) -> Result<Graph> {
83        let mut nodes = self.nodes.into_values().collect::<Vec<_>>();
84        nodes.sort_unstable_by(|left, right| left.id.cmp(&right.id));
85        let (edges, topology) = canonicalize_edges(&nodes, self.edges)?;
86        Ok(Graph::from_indexed_parts(nodes, edges, topology))
87    }
88}