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