1use 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
25pub 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}