Skip to main content

ezu_graph/
build.rs

1//! Drive [`GraphBuilder`] from a parsed [`spec::Document`] using a
2//! [`NodeRegistry`].
3
4use ezu_style as spec;
5
6use crate::graph::{BuildError, Graph, GraphBuilder};
7use crate::registry::{FactoryCtx, FactoryError, NodeRegistry};
8
9#[derive(Debug, thiserror::Error)]
10pub enum BuildGraphError {
11    #[error("unknown op `{op}` on node `{node}`")]
12    UnknownOp { node: String, op: String },
13
14    #[error("factory error on node `{node}`: {source}")]
15    Factory {
16        node: String,
17        #[source]
18        source: FactoryError,
19    },
20
21    #[error(transparent)]
22    Graph(#[from] BuildError),
23}
24
25/// Build a typed [`Graph`] from a parsed document and a registry of
26/// node factories.
27pub fn build_graph(
28    doc: &spec::Document,
29    registry: &NodeRegistry,
30) -> Result<Graph, BuildGraphError> {
31    let ctx = FactoryCtx {
32        params: &doc.params,
33        sources: &doc.sources,
34    };
35
36    let mut gb = GraphBuilder::new();
37    let mut pending: Vec<(String, Vec<crate::registry::Connection>)> = Vec::new();
38
39    for (id, spec) in &doc.nodes {
40        let factory = registry
41            .get(&spec.op)
42            .ok_or_else(|| BuildGraphError::UnknownOp {
43                node: id.clone(),
44                op: spec.op.clone(),
45            })?;
46
47        let built = factory
48            .build(&spec.fields, &ctx)
49            .map_err(|e| BuildGraphError::Factory {
50                node: id.clone(),
51                source: e,
52            })?;
53
54        gb.add_node(id.clone(), built.node);
55        pending.push((id.clone(), built.connections));
56    }
57
58    for (dst, conns) in pending {
59        for c in conns {
60            gb.connect(c.src, dst.clone(), c.port);
61        }
62    }
63
64    gb.set_output(doc.output.as_str().to_string());
65    Ok(gb.build()?)
66}