Skip to main content

celox/component/
injected.rs

1use std::sync::Arc;
2
3use crate::HashMap;
4
5use veryl_metadata::ComponentManifest;
6
7/// A value crossing the boundary of an in-process component callback.
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum InjectedValue {
10    Bits {
11        words: Vec<u64>,
12        mask_xz: Vec<u64>,
13        width: u32,
14    },
15    String(String),
16    Unit,
17}
18
19/// The lifecycle operation requested from an injected component.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum InjectedHook {
22    Create,
23    Init,
24    Reset,
25    Clock,
26    Finish,
27    Method {
28        name: String,
29        args: Vec<InjectedValue>,
30    },
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct InjectedNamedValue {
35    pub name: String,
36    pub value: InjectedValue,
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct InjectedPort {
41    pub name: String,
42    pub direction: String,
43    pub role: Option<String>,
44    pub width: u32,
45}
46
47/// One synchronous call into an injected component implementation.
48#[derive(Clone, Debug)]
49pub struct InjectedCall {
50    pub instance: String,
51    pub hook: InjectedHook,
52    pub inputs: Vec<InjectedNamedValue>,
53    pub params: Vec<InjectedNamedValue>,
54    pub ports: Vec<InjectedPort>,
55    pub cycle: u64,
56    pub time: u64,
57    pub seed: u64,
58    pub fired_clock: Option<String>,
59    pub four_state: bool,
60}
61
62/// Effects returned by an injected callback. Outputs are staged using the
63/// same component/NBA path as compiled native and Wasm components.
64#[derive(Clone, Debug, Default)]
65pub struct InjectedResult {
66    pub outputs: Vec<InjectedNamedValue>,
67    pub return_value: Option<InjectedValue>,
68    pub failures: Vec<String>,
69    pub logs: Vec<String>,
70    pub finish: bool,
71}
72
73/// Synchronous component implementation supplied by an embedding runtime.
74pub trait InjectedComponentHandler: Send + Sync + 'static {
75    fn call(&self, call: InjectedCall) -> Result<InjectedResult, String>;
76}
77
78#[derive(Clone)]
79pub(crate) struct InjectedComponentDefinition {
80    pub manifest: ComponentManifest,
81    pub kind: u32,
82    pub handler: Arc<dyn InjectedComponentHandler>,
83}
84
85/// Component definitions injected by an embedding API for one simulator.
86#[derive(Clone, Default)]
87pub struct InjectedComponents {
88    pub(crate) definitions: HashMap<String, InjectedComponentDefinition>,
89}
90
91impl InjectedComponents {
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    pub fn insert(
97        &mut self,
98        name: impl Into<String>,
99        manifest_json: &str,
100        handler: Arc<dyn InjectedComponentHandler>,
101    ) -> Result<(), String> {
102        let name = name.into();
103        if name.is_empty() || name.contains("::") {
104            return Err("injected component names must be non-empty identifiers".into());
105        }
106        let manifest = ComponentManifest::parse(manifest_json)
107            .ok_or_else(|| format!("manifest of injected component `{name}` cannot be parsed"))?;
108        let kind = match manifest.kind.as_deref() {
109            Some("clocked") => veryl_component_sys::VRL_KIND_CLOCKED,
110            Some("method_only") => veryl_component_sys::VRL_KIND_METHOD_ONLY,
111            Some(kind) => return Err(format!("unsupported component kind `{kind}`")),
112            None => {
113                return Err(format!(
114                    "manifest of injected component `{name}` has no kind"
115                ));
116            }
117        };
118        if self.definitions.contains_key(&name) {
119            return Err(format!(
120                "injected component `{name}` is defined more than once"
121            ));
122        }
123        self.definitions.insert(
124            name,
125            InjectedComponentDefinition {
126                manifest,
127                kind,
128                handler,
129            },
130        );
131        Ok(())
132    }
133
134    pub(crate) fn get(&self, name: &str) -> Option<&InjectedComponentDefinition> {
135        self.definitions.get(name)
136    }
137
138    pub(crate) fn manifests(&self) -> Vec<(String, ComponentManifest)> {
139        self.definitions
140            .iter()
141            .map(|(name, definition)| (name.clone(), definition.manifest.clone()))
142            .collect()
143    }
144}