Skip to main content

sim_lib_bridge/
runtime.rs

1use std::sync::Arc;
2
3use sim_codec_bridge::{BridgeBook, BridgeFramePayload, expr_to_packet, packet_to_expr};
4use sim_kernel::{
5    AbiVersion, Args, Callable, Cx, Error, Export, Lib, LibManifest, LibTarget, Linker, LoadCx,
6    Object, ObjectCompat, Result, Symbol, Value, Version,
7};
8use sim_shape::{AnyShape, ListShape, Shape, shape_value};
9
10use crate::{ask_packet, bridge_brief, bridge_tx, receipt_packet_for_report, rx_check};
11
12/// Loadable BRIDGE runtime library.
13pub struct BridgeLib;
14
15impl Lib for BridgeLib {
16    fn manifest(&self) -> LibManifest {
17        LibManifest {
18            id: manifest_name(),
19            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
20            abi: AbiVersion { major: 0, minor: 1 },
21            target: LibTarget::HostRegistered,
22            requires: Vec::new(),
23            capabilities: Vec::new(),
24            exports: bridge_exports(),
25        }
26    }
27
28    fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
29        for kind in BridgeFunctionKind::ALL {
30            let function = BridgeFunction::value(kind);
31            linker.function_value(function.symbol(), cx.factory().opaque(function)?)?;
32        }
33        Ok(())
34    }
35}
36
37/// Installs the BRIDGE runtime library into a context.
38pub fn install_bridge_lib(cx: &mut Cx) -> Result<()> {
39    cx.load_lib(&BridgeLib).map(|_| ())
40}
41
42/// Manifest symbol for the BRIDGE runtime library.
43pub fn manifest_name() -> Symbol {
44    Symbol::qualified("sim", "bridge")
45}
46
47/// Runtime symbol for `bridge/tx`.
48pub fn bridge_tx_symbol() -> Symbol {
49    Symbol::qualified("bridge", "tx")
50}
51
52/// Runtime symbol for `bridge/rx`.
53pub fn bridge_rx_symbol() -> Symbol {
54    Symbol::qualified("bridge", "rx")
55}
56
57/// Runtime symbol for `bridge/report`.
58pub fn bridge_report_symbol() -> Symbol {
59    Symbol::qualified("bridge", "report")
60}
61
62/// Runtime symbol for `bridge/brief`.
63pub fn bridge_brief_symbol() -> Symbol {
64    Symbol::qualified("bridge", "brief")
65}
66
67/// Runtime symbol for `bridge/ask`.
68pub fn bridge_ask_symbol() -> Symbol {
69    Symbol::qualified("bridge", "ask")
70}
71
72fn bridge_exports() -> Vec<Export> {
73    BridgeFunctionKind::ALL
74        .iter()
75        .map(|kind| Export::Function {
76            symbol: kind.symbol(),
77            function_id: None,
78        })
79        .collect()
80}
81
82/// Runtime callable kind for BRIDGE exports.
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum BridgeFunctionKind {
85    /// Build a checked eval request from a packet.
86    Tx,
87    /// Decode and check a model response packet.
88    Rx,
89    /// Produce a receive-check report for a packet.
90    Report,
91    /// Produce a receipt packet for a report.
92    Receipt,
93    /// Build a BRIEF request packet from one typed frame.
94    Brief,
95    /// Build an ASK request packet from one typed call.
96    Ask,
97}
98
99impl BridgeFunctionKind {
100    /// All exported function kinds.
101    pub const ALL: [Self; 6] = [
102        Self::Tx,
103        Self::Rx,
104        Self::Report,
105        Self::Receipt,
106        Self::Brief,
107        Self::Ask,
108    ];
109
110    /// Runtime symbol for this kind.
111    pub fn symbol(self) -> Symbol {
112        match self {
113            Self::Tx => bridge_tx_symbol(),
114            Self::Rx => bridge_rx_symbol(),
115            Self::Report => bridge_report_symbol(),
116            Self::Receipt => crate::receipt_symbol(),
117            Self::Brief => bridge_brief_symbol(),
118            Self::Ask => bridge_ask_symbol(),
119        }
120    }
121}
122
123/// Runtime callable implementing one BRIDGE export.
124#[derive(Clone)]
125pub struct BridgeFunction {
126    kind: BridgeFunctionKind,
127}
128
129impl BridgeFunction {
130    /// Builds a function object for `kind`.
131    pub fn new(kind: BridgeFunctionKind) -> Self {
132        Self { kind }
133    }
134
135    /// Returns this function's runtime symbol.
136    pub fn symbol(&self) -> Symbol {
137        self.kind.symbol()
138    }
139
140    /// Builds a shared function object for `kind`.
141    pub fn value(kind: BridgeFunctionKind) -> Arc<Self> {
142        Arc::new(Self::new(kind))
143    }
144}
145
146impl Object for BridgeFunction {
147    fn display(&self, _cx: &mut Cx) -> Result<String> {
148        Ok(format!("#<function {}>", self.symbol()))
149    }
150
151    fn as_any(&self) -> &dyn std::any::Any {
152        self
153    }
154}
155
156impl ObjectCompat for BridgeFunction {
157    fn as_callable(&self) -> Option<&dyn Callable> {
158        Some(self)
159    }
160}
161
162impl Callable for BridgeFunction {
163    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
164        match self.kind {
165            BridgeFunctionKind::Tx => call_tx(cx, args),
166            BridgeFunctionKind::Rx => call_rx(cx, args),
167            BridgeFunctionKind::Report => call_report(cx, args),
168            BridgeFunctionKind::Receipt => call_receipt(cx, args),
169            BridgeFunctionKind::Brief => call_brief(cx, args),
170            BridgeFunctionKind::Ask => call_ask(cx, args),
171        }
172    }
173
174    fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
175        let shape: Arc<dyn Shape> = match self.kind {
176            BridgeFunctionKind::Tx | BridgeFunctionKind::Report => {
177                Arc::new(ListShape::new(vec![Arc::new(AnyShape)]))
178            }
179            BridgeFunctionKind::Rx => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
180            BridgeFunctionKind::Receipt => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
181            BridgeFunctionKind::Brief => Arc::new(ListShape::new(vec![
182                Arc::new(AnyShape),
183                Arc::new(AnyShape),
184                Arc::new(AnyShape),
185            ])),
186            BridgeFunctionKind::Ask => Arc::new(ListShape::new(vec![
187                Arc::new(AnyShape),
188                Arc::new(AnyShape),
189                Arc::new(AnyShape),
190                Arc::new(AnyShape),
191            ])),
192        };
193        Ok(Some(shape_value(
194            Symbol::qualified(self.symbol().to_string(), "args"),
195            shape,
196        )))
197    }
198
199    fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
200        Ok(Some(shape_value(
201            Symbol::qualified(self.symbol().to_string(), "result"),
202            Arc::new(AnyShape),
203        )))
204    }
205}
206
207fn call_tx(cx: &mut Cx, args: Args) -> Result<Value> {
208    let packet = packet_arg(cx, args, "bridge/tx expects one packet expression")?;
209    let request = bridge_tx(cx, &BridgeBook::standard(), &packet)?;
210    cx.factory().opaque(Arc::new(request))
211}
212
213fn call_rx(cx: &mut Cx, args: Args) -> Result<Value> {
214    let response = one_expr_arg(cx, args, "bridge/rx expects one model response expression")?;
215    let (packet, report) = crate::bridge_rx(cx, &BridgeBook::standard(), response, None)?;
216    cx.factory().expr(sim_kernel::Expr::Map(vec![
217        sim_value::build::entry("packet", packet_to_expr(&packet)),
218        sim_value::build::entry("report", report.to_expr()),
219    ]))
220}
221
222fn call_report(cx: &mut Cx, args: Args) -> Result<Value> {
223    let packet = packet_arg(cx, args, "bridge/report expects one packet expression")?;
224    let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
225    cx.factory().expr(report.to_expr())
226}
227
228fn call_receipt(cx: &mut Cx, args: Args) -> Result<Value> {
229    let packet = packet_arg(cx, args, "bridge/receipt expects one packet expression")?;
230    let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
231    let receipt = receipt_packet_for_report(&report, "sim")?;
232    cx.factory().expr(packet_to_expr(&receipt))
233}
234
235fn call_brief(cx: &mut Cx, args: Args) -> Result<Value> {
236    let mut exprs = expr_args(
237        cx,
238        args,
239        "bridge/brief expects target, frame, and return shape",
240    )?;
241    let [target, frame, return_shape] = take_three(&mut exprs)?;
242    let frame = BridgeFramePayload::from_expr(&frame)?;
243    let packet = bridge_brief(&target_name(&target)?, frame, return_shape)?;
244    cx.factory().expr(packet_to_expr(&packet))
245}
246
247fn call_ask(cx: &mut Cx, args: Args) -> Result<Value> {
248    let mut exprs = expr_args(
249        cx,
250        args,
251        "bridge/ask expects target, call, params, and return shape",
252    )?;
253    if exprs.len() != 4 {
254        return Err(Error::Eval(format!(
255            "bridge/ask expects 4 argument(s), found {}",
256            exprs.len()
257        )));
258    }
259    let return_shape = exprs.pop().expect("length checked");
260    let params = exprs.pop().expect("length checked");
261    let call = exprs.pop().expect("length checked");
262    let target = exprs.pop().expect("length checked");
263    let packet = ask_packet(
264        cx,
265        &call_name(&call)?,
266        call_params(&params)?,
267        return_shape,
268        &target_name(&target)?,
269    )?;
270    cx.factory().expr(packet_to_expr(&packet))
271}
272
273fn packet_arg(
274    cx: &mut Cx,
275    args: Args,
276    message: &'static str,
277) -> Result<sim_codec_bridge::BridgePacket> {
278    expr_to_packet(&one_expr_arg(cx, args, message)?)
279}
280
281fn expr_args(cx: &mut Cx, args: Args, message: &'static str) -> Result<Vec<sim_kernel::Expr>> {
282    let values = args.into_vec();
283    if values.is_empty() {
284        return Err(Error::Eval(message.to_owned()));
285    }
286    values
287        .into_iter()
288        .map(|value| value.object().as_expr(cx))
289        .collect()
290}
291
292fn one_expr_arg(cx: &mut Cx, args: Args, message: &'static str) -> Result<sim_kernel::Expr> {
293    let mut values = args.into_vec();
294    if values.len() != 1 {
295        return Err(Error::Eval(message.to_owned()));
296    }
297    values.remove(0).object().as_expr(cx)
298}
299
300fn take_three(exprs: &mut Vec<sim_kernel::Expr>) -> Result<[sim_kernel::Expr; 3]> {
301    let [target, frame, return_shape] =
302        std::mem::take(exprs).try_into().map_err(|values: Vec<_>| {
303            Error::Eval(format!(
304                "bridge/brief expects 3 argument(s), found {}",
305                values.len()
306            ))
307        })?;
308    Ok([target, frame, return_shape])
309}
310
311fn target_name(expr: &sim_kernel::Expr) -> Result<String> {
312    match expr {
313        sim_kernel::Expr::String(target) => Ok(target.clone()),
314        sim_kernel::Expr::Symbol(target) => Ok(target.as_qualified_str().to_owned()),
315        _ => Err(Error::Eval(
316            "bridge/brief target must be a string or symbol".to_owned(),
317        )),
318    }
319}
320
321fn call_name(expr: &sim_kernel::Expr) -> Result<String> {
322    match expr {
323        sim_kernel::Expr::String(name) => Ok(name.clone()),
324        sim_kernel::Expr::Symbol(name) => Ok(name.as_qualified_str()),
325        _ => Err(Error::Eval(
326            "bridge/ask call must be a string or symbol".to_owned(),
327        )),
328    }
329}
330
331fn call_params(expr: &sim_kernel::Expr) -> Result<Vec<(String, sim_kernel::Expr)>> {
332    let sim_kernel::Expr::Map(entries) = expr else {
333        return Err(Error::Eval("bridge/ask params must be a map".to_owned()));
334    };
335    entries
336        .iter()
337        .map(|(key, value)| {
338            let name = match key {
339                sim_kernel::Expr::String(name) => name.clone(),
340                sim_kernel::Expr::Symbol(name) => name.as_qualified_str(),
341                _ => {
342                    return Err(Error::Eval(
343                        "bridge/ask param keys must be strings or symbols".to_owned(),
344                    ));
345                }
346            };
347            Ok((name, value.clone()))
348        })
349        .collect()
350}