bb_compiler/
resolve_component_dependencies.rs1use bb_ir::component::DependencyDecl;
20use bb_ir::keys::{stamp_dependency_metadata, CONCRETE_TYPE_KEY};
21use bb_ir::proto::onnx::ModelProto;
22use bb_ir::registry::find_concrete_component;
23
24use crate::artifact::{BindingSlot, BindingSpec};
25use crate::error::CompileError;
26
27pub(crate) fn resolve_component_dependencies(
31 spec: &BindingSpec,
32 models: &mut [ModelProto],
33) -> Result<(), CompileError> {
34 for slot in &spec.slots {
35 let concrete_type = slot.concrete_type_name.as_str();
36 if concrete_type.is_empty() {
37 continue;
40 }
41 let entry = match find_concrete_component(concrete_type) {
42 Some(e) => e,
43 None => {
44 continue;
48 }
49 };
50 verify_deps(slot, entry.dependencies, spec)?;
51 }
52
53 stamp_dep_metadata_across_models(spec, models);
54 Ok(())
55}
56
57fn verify_deps(
58 component_slot: &BindingSlot,
59 deps: &[DependencyDecl],
60 spec: &BindingSpec,
61) -> Result<(), CompileError> {
62 for dep in deps {
63 let target = spec
64 .get(dep.slot)
65 .ok_or_else(|| CompileError::UnboundDependency {
66 component: component_slot.concrete_type_name.clone(),
67 bound_at_slot: component_slot.slot.clone(),
68 required_role: dep.role.to_string(),
69 required_slot: dep.slot.to_string(),
70 })?;
71 if !role_matches(&target.role, dep.role) {
72 return Err(CompileError::DependencyRoleMismatch {
73 component: component_slot.concrete_type_name.clone(),
74 bound_at_slot: component_slot.slot.clone(),
75 required_role: dep.role.to_string(),
76 required_slot: dep.slot.to_string(),
77 provided_role: target.role.clone(),
78 });
79 }
80 }
81 Ok(())
82}
83
84fn role_matches(provided: &str, required: &str) -> bool {
89 normalize_role(provided) == normalize_role(required)
90}
91
92fn normalize_role(role: &str) -> &str {
93 role.strip_suffix("Runtime").unwrap_or(role)
94}
95
96fn stamp_dep_metadata_across_models(spec: &BindingSpec, models: &mut [ModelProto]) {
97 for model in models {
98 if let Some(graph) = model.graph.as_mut() {
102 for node in &mut graph.node {
103 stamp_for_node(spec, node);
104 }
105 }
106 for function in &mut model.functions {
107 for node in &mut function.node {
108 stamp_for_node(spec, node);
109 }
110 }
111 }
112}
113
114fn stamp_for_node(spec: &BindingSpec, node: &mut bb_ir::proto::onnx::NodeProto) {
115 let Some(concrete_type) = node
116 .metadata_props
117 .iter()
118 .find(|e| e.key == CONCRETE_TYPE_KEY)
119 .map(|e| e.value.clone())
120 else {
121 return;
122 };
123 let Some(_bound_slot) = spec
124 .slots
125 .iter()
126 .find(|s| s.concrete_type_name == concrete_type)
127 else {
128 return;
129 };
130 let Some(entry) = find_concrete_component(&concrete_type) else {
131 return;
132 };
133 if entry.dependencies.is_empty() {
134 return;
135 }
136 stamp_dependency_metadata(node, entry.dependencies);
137}
138