bb_ops/syscalls/composite/
unbundle.rs1use bb_ir::proto::onnx::NodeProto;
20use bb_runtime::atomic::DispatchResult;
21use bb_runtime::bus::{OpError, OpErrorKind};
22use bb_runtime::runtime::RuntimeResourceRef;
23use bb_runtime::slot_value::SlotValue;
24use bb_runtime::syscall::values::CompositeValue;
25
26pub const DOMAIN: &str = "ai.bytesandbrains.composite";
28pub const OP_TYPE: &str = "Unbundle";
30pub const PORT_BUNDLE: &str = "bundle";
32pub const ATTR_CHILD_COUNT: &str = "ai.bytesandbrains.composite.child_count";
34pub const ATTR_CHILD_TYPES: &str = "ai.bytesandbrains.composite.child_types";
37
38pub fn invoke(
44 node: &NodeProto,
45 inputs: &[(&str, &dyn SlotValue)],
46 _ctx: &mut RuntimeResourceRef<'_>,
47) -> Result<DispatchResult, OpError> {
48 let composite = downcast_composite(inputs)?;
49 let declared = declared_child_count(node)?;
50 if composite.children.len() != declared {
51 return Err(OpError {
52 kind: OpErrorKind::ExecutionFailed,
53 reason: "unbundle_child_count_mismatch",
54 detail: format!(
55 "composite.Unbundle: envelope carries {} children, declared {}",
56 composite.children.len(),
57 declared
58 ),
59 });
60 }
61 let mut outs: Vec<(String, Box<dyn SlotValue>)> = Vec::with_capacity(declared);
62 for (i, child) in composite.children.iter().enumerate() {
63 outs.push((format!("child_{i}"), child.clone_boxed()));
64 }
65 Ok(DispatchResult::Immediate(outs))
66}
67
68fn downcast_composite<'a>(
69 inputs: &'a [(&str, &dyn SlotValue)],
70) -> Result<&'a CompositeValue, OpError> {
71 let (_, value) = inputs
72 .iter()
73 .find(|(n, _)| *n == PORT_BUNDLE)
74 .ok_or_else(|| OpError {
75 kind: OpErrorKind::MissingSlot,
76 reason: "missing_bundle",
77 detail: "composite.Unbundle: required input `bundle` is absent".into(),
78 })?;
79 value
80 .as_any()
81 .downcast_ref::<CompositeValue>()
82 .ok_or_else(|| OpError {
83 kind: OpErrorKind::TypeMismatch,
84 reason: "expected_composite",
85 detail: "composite.Unbundle: input `bundle` is not a CompositeValue".into(),
86 })
87}
88
89fn declared_child_count(node: &NodeProto) -> Result<usize, OpError> {
90 let attr = node
91 .attribute
92 .iter()
93 .find(|a| a.name == ATTR_CHILD_COUNT)
94 .ok_or_else(|| OpError {
95 kind: OpErrorKind::MissingSlot,
96 reason: "missing_child_count",
97 detail: format!("composite.Unbundle: missing `{ATTR_CHILD_COUNT}` attribute"),
98 })?;
99 if attr.i < 1 {
100 return Err(OpError {
101 kind: OpErrorKind::ExecutionFailed,
102 reason: "child_count_below_one",
103 detail: format!(
104 "composite.Unbundle: `{ATTR_CHILD_COUNT}` must be >= 1, got {}",
105 attr.i
106 ),
107 });
108 }
109 Ok(attr.i as usize)
110}
111
112
113bb_derive::register_op! {
114 domain: "ai.bytesandbrains.composite",
115 op_type: "Unbundle",
116 invoke: invoke,
117}