Skip to main content

bb_ops/syscalls/
mod.rs

1//! Framework syscall components - each sub-directory hosts one or
2//! more registerable ops. Self-registration is via
3//! `inventory::submit!`; `Engine::register_all_framework_syscalls`
4//! walks the registry at Node::ensure_ready.
5
6pub mod clock_rng;
7pub mod composite;
8pub mod coordination;
9pub mod gates;
10pub mod lifecycle;
11pub mod peers;
12pub mod structural;
13pub mod sync;
14pub mod telemetry;
15pub mod triggers;
16
17use bb_runtime::bus::{OpError, OpErrorKind};
18use bb_runtime::slot_value::SlotValue;
19use bb_runtime::syscall::values::BytesValue;
20
21/// Read the first input as `BytesValue`. Distinguishes "no input
22/// present" (returns `Ok(None)` — legitimate Trigger-only firing)
23/// from "input present but not `BytesValue`" (returns `Err` with
24/// `OpErrorKind::TypeMismatch` — adversarial / mis-wired graph).
25///
26/// Use for syscalls that accept an optional payload alongside a
27/// trigger: `Hold.Stash`, `Serialize.Enqueue`, `Record`, `AppEmit`.
28pub(crate) fn first_input_optional_bytes(
29    op_name: &str,
30    inputs: &[(&str, &dyn SlotValue)],
31) -> Result<Option<Vec<u8>>, OpError> {
32    let Some((slot_name, value)) = inputs.first() else {
33        return Ok(None);
34    };
35    let Some(bytes) = value.as_any().downcast_ref::<BytesValue>() else {
36        return Err(OpError {
37            kind: OpErrorKind::TypeMismatch,
38            reason: "expected_bytes",
39            detail: format!("{op_name}: input '{slot_name}' is not BytesValue"),
40        });
41    };
42    Ok(Some(bytes.0.clone()))
43}