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, OneOfShape, Shape, shape_value};
9
10use crate::{
11 RepairPolicy, ask_packet_with_model_params, bridge_brief, bridge_tx, receipt_packet_for_report,
12 run_ask_with_policy, rx_check,
13};
14
15const BRIDGE_RUN_ASK_NAME: &str = "bridge/run-ask";
16
17pub struct BridgeLib;
19
20impl Lib for BridgeLib {
21 fn manifest(&self) -> LibManifest {
22 LibManifest {
23 id: manifest_name(),
24 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
25 abi: AbiVersion { major: 0, minor: 1 },
26 target: LibTarget::HostRegistered,
27 requires: Vec::new(),
28 capabilities: Vec::new(),
29 exports: bridge_exports(),
30 }
31 }
32
33 fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
34 for kind in BridgeFunctionKind::ALL {
35 let function = BridgeFunction::value(kind);
36 linker.function_value(function.symbol(), cx.factory().opaque(function)?)?;
37 }
38 Ok(())
39 }
40}
41
42pub fn install_bridge_lib(cx: &mut Cx) -> Result<()> {
44 cx.load_lib(&BridgeLib).map(|_| ())
45}
46
47pub fn manifest_name() -> Symbol {
49 Symbol::qualified("sim", "bridge")
50}
51
52pub fn bridge_tx_symbol() -> Symbol {
54 Symbol::qualified("bridge", "tx")
55}
56
57pub fn bridge_rx_symbol() -> Symbol {
59 Symbol::qualified("bridge", "rx")
60}
61
62pub fn bridge_report_symbol() -> Symbol {
64 Symbol::qualified("bridge", "report")
65}
66
67pub fn bridge_brief_symbol() -> Symbol {
69 Symbol::qualified("bridge", "brief")
70}
71
72pub fn bridge_ask_symbol() -> Symbol {
74 Symbol::qualified("bridge", "ask")
75}
76
77pub fn bridge_run_ask_symbol() -> Symbol {
79 Symbol::qualified("bridge", "run-ask")
80}
81
82fn bridge_exports() -> Vec<Export> {
83 BridgeFunctionKind::ALL
84 .iter()
85 .map(|kind| Export::Function {
86 symbol: kind.symbol(),
87 function_id: None,
88 })
89 .collect()
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub enum BridgeFunctionKind {
95 Tx,
97 Rx,
99 Report,
101 Receipt,
103 Brief,
105 Ask,
107 RunAsk,
109}
110
111impl BridgeFunctionKind {
112 pub const ALL: [Self; 7] = [
114 Self::Tx,
115 Self::Rx,
116 Self::Report,
117 Self::Receipt,
118 Self::Brief,
119 Self::Ask,
120 Self::RunAsk,
121 ];
122
123 pub fn symbol(self) -> Symbol {
125 match self {
126 Self::Tx => bridge_tx_symbol(),
127 Self::Rx => bridge_rx_symbol(),
128 Self::Report => bridge_report_symbol(),
129 Self::Receipt => crate::receipt_symbol(),
130 Self::Brief => bridge_brief_symbol(),
131 Self::Ask => bridge_ask_symbol(),
132 Self::RunAsk => bridge_run_ask_symbol(),
133 }
134 }
135}
136
137#[derive(Clone)]
139pub struct BridgeFunction {
140 kind: BridgeFunctionKind,
141}
142
143impl BridgeFunction {
144 pub fn new(kind: BridgeFunctionKind) -> Self {
146 Self { kind }
147 }
148
149 pub fn symbol(&self) -> Symbol {
151 self.kind.symbol()
152 }
153
154 pub fn value(kind: BridgeFunctionKind) -> Arc<Self> {
156 Arc::new(Self::new(kind))
157 }
158}
159
160impl Object for BridgeFunction {
161 fn display(&self, _cx: &mut Cx) -> Result<String> {
162 Ok(format!("#<function {}>", self.symbol()))
163 }
164
165 fn as_any(&self) -> &dyn std::any::Any {
166 self
167 }
168}
169
170impl ObjectCompat for BridgeFunction {
171 fn as_callable(&self) -> Option<&dyn Callable> {
172 Some(self)
173 }
174}
175
176impl Callable for BridgeFunction {
177 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
178 match self.kind {
179 BridgeFunctionKind::Tx => call_tx(cx, args),
180 BridgeFunctionKind::Rx => call_rx(cx, args),
181 BridgeFunctionKind::Report => call_report(cx, args),
182 BridgeFunctionKind::Receipt => call_receipt(cx, args),
183 BridgeFunctionKind::Brief => call_brief(cx, args),
184 BridgeFunctionKind::Ask => call_ask(cx, args),
185 BridgeFunctionKind::RunAsk => call_run_ask(cx, args),
186 }
187 }
188
189 fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
190 let shape: Arc<dyn Shape> = match self.kind {
191 BridgeFunctionKind::Tx | BridgeFunctionKind::Report => {
192 Arc::new(ListShape::new(vec![Arc::new(AnyShape)]))
193 }
194 BridgeFunctionKind::Rx => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
195 BridgeFunctionKind::Receipt => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
196 BridgeFunctionKind::Brief => Arc::new(ListShape::new(vec![
197 Arc::new(AnyShape),
198 Arc::new(AnyShape),
199 Arc::new(AnyShape),
200 ])),
201 BridgeFunctionKind::Ask => {
202 Arc::new(OneOfShape::new(vec![any_args_shape(4), any_args_shape(5)]))
203 }
204 BridgeFunctionKind::RunAsk => {
205 Arc::new(OneOfShape::new(vec![any_args_shape(2), any_args_shape(3)]))
206 }
207 };
208 Ok(Some(shape_value(
209 Symbol::qualified(self.symbol().to_string(), "args"),
210 shape,
211 )))
212 }
213
214 fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
215 Ok(Some(shape_value(
216 Symbol::qualified(self.symbol().to_string(), "result"),
217 Arc::new(AnyShape),
218 )))
219 }
220}
221
222fn call_tx(cx: &mut Cx, args: Args) -> Result<Value> {
223 let packet = packet_arg(cx, args, "bridge/tx expects one packet expression")?;
224 let request = bridge_tx(cx, &BridgeBook::standard(), &packet)?;
225 cx.factory().opaque(Arc::new(request))
226}
227
228fn call_rx(cx: &mut Cx, args: Args) -> Result<Value> {
229 let response = one_expr_arg(cx, args, "bridge/rx expects one model response expression")?;
230 let (packet, report) = crate::bridge_rx(cx, &BridgeBook::standard(), response, None)?;
231 cx.factory().expr(sim_kernel::Expr::Map(vec![
232 sim_value::build::entry("packet", packet_to_expr(&packet)),
233 sim_value::build::entry("report", report.to_expr()),
234 ]))
235}
236
237fn call_report(cx: &mut Cx, args: Args) -> Result<Value> {
238 let packet = packet_arg(cx, args, "bridge/report expects one packet expression")?;
239 let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
240 cx.factory().expr(report.to_expr())
241}
242
243fn call_receipt(cx: &mut Cx, args: Args) -> Result<Value> {
244 let packet = packet_arg(cx, args, "bridge/receipt expects one packet expression")?;
245 let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
246 let receipt = receipt_packet_for_report(&report, "sim")?;
247 cx.factory().expr(packet_to_expr(&receipt))
248}
249
250fn call_brief(cx: &mut Cx, args: Args) -> Result<Value> {
251 let mut exprs = expr_args(
252 cx,
253 args,
254 "bridge/brief expects target, frame, and return shape",
255 )?;
256 let [target, frame, return_shape] = take_three(&mut exprs)?;
257 let frame = BridgeFramePayload::from_expr(&frame)?;
258 let packet = bridge_brief(&target_name(&target)?, frame, return_shape)?;
259 cx.factory().expr(packet_to_expr(&packet))
260}
261
262fn call_ask(cx: &mut Cx, args: Args) -> Result<Value> {
263 let mut exprs = expr_args(
264 cx,
265 args,
266 "bridge/ask expects target, call, params, return shape, and optional model params",
267 )?;
268 if !matches!(exprs.len(), 4 | 5) {
269 return Err(Error::Eval(format!(
270 "bridge/ask expects 4 or 5 argument(s), found {}",
271 exprs.len()
272 )));
273 }
274 let model_params = if exprs.len() == 5 {
275 call_params(&exprs.pop().expect("length checked"))?
276 } else {
277 Vec::new()
278 };
279 let return_shape = exprs.pop().expect("length checked");
280 let params = exprs.pop().expect("length checked");
281 let call = exprs.pop().expect("length checked");
282 let target = exprs.pop().expect("length checked");
283 let packet = ask_packet_with_model_params(
284 cx,
285 &call_name(&call)?,
286 call_params(¶ms)?,
287 model_params,
288 return_shape,
289 &target_name(&target)?,
290 )?;
291 cx.factory().expr(packet_to_expr(&packet))
292}
293
294fn call_run_ask(cx: &mut Cx, args: Args) -> Result<Value> {
295 let mut values = args.into_vec();
296 if !matches!(values.len(), 2 | 3) {
297 return Err(Error::Eval(format!(
298 "{BRIDGE_RUN_ASK_NAME} expects 2 or 3 argument(s), found {}",
299 values.len()
300 )));
301 }
302 let policy = if values.len() == 3 {
303 repair_policy(cx, &values.pop().expect("length checked"))?
304 } else {
305 RepairPolicy::default()
306 };
307 let packet = expr_to_packet(&values.pop().expect("length checked").object().as_expr(cx)?)?;
308 let target = values.pop().expect("length checked");
309 let Some(fabric) = target.object().as_eval_fabric() else {
310 return Err(Error::TypeMismatch {
311 expected: "eval-fabric",
312 found: "non-eval-fabric",
313 });
314 };
315 let reply = run_ask_with_policy(cx, fabric, packet, policy)?;
316 cx.factory().expr(packet_to_expr(&reply))
317}
318
319fn any_args_shape(arity: usize) -> Arc<dyn Shape> {
320 Arc::new(ListShape::new(
321 (0..arity)
322 .map(|_| Arc::new(AnyShape) as Arc<dyn Shape>)
323 .collect(),
324 ))
325}
326
327fn repair_policy(cx: &mut Cx, value: &Value) -> Result<RepairPolicy> {
328 let expr = value.object().as_expr(cx)?;
329 let sim_kernel::Expr::Number(number) = expr else {
330 return Err(Error::Eval(format!(
331 "{BRIDGE_RUN_ASK_NAME} retries must be a non-negative integer"
332 )));
333 };
334 let retries = number.canonical.parse::<u8>().map_err(|_| {
335 Error::Eval(format!(
336 "{BRIDGE_RUN_ASK_NAME} retries must be a non-negative integer"
337 ))
338 })?;
339 Ok(RepairPolicy::new(retries))
340}
341
342fn packet_arg(
343 cx: &mut Cx,
344 args: Args,
345 message: &'static str,
346) -> Result<sim_codec_bridge::BridgePacket> {
347 expr_to_packet(&one_expr_arg(cx, args, message)?)
348}
349
350fn expr_args(cx: &mut Cx, args: Args, message: &'static str) -> Result<Vec<sim_kernel::Expr>> {
351 let values = args.into_vec();
352 if values.is_empty() {
353 return Err(Error::Eval(message.to_owned()));
354 }
355 values
356 .into_iter()
357 .map(|value| value.object().as_expr(cx))
358 .collect()
359}
360
361fn one_expr_arg(cx: &mut Cx, args: Args, message: &'static str) -> Result<sim_kernel::Expr> {
362 let mut values = args.into_vec();
363 if values.len() != 1 {
364 return Err(Error::Eval(message.to_owned()));
365 }
366 values.remove(0).object().as_expr(cx)
367}
368
369fn take_three(exprs: &mut Vec<sim_kernel::Expr>) -> Result<[sim_kernel::Expr; 3]> {
370 let [target, frame, return_shape] =
371 std::mem::take(exprs).try_into().map_err(|values: Vec<_>| {
372 Error::Eval(format!(
373 "bridge/brief expects 3 argument(s), found {}",
374 values.len()
375 ))
376 })?;
377 Ok([target, frame, return_shape])
378}
379
380fn target_name(expr: &sim_kernel::Expr) -> Result<String> {
381 match expr {
382 sim_kernel::Expr::String(target) => Ok(target.clone()),
383 sim_kernel::Expr::Symbol(target) => Ok(target.as_qualified_str().to_owned()),
384 _ => Err(Error::Eval(
385 "bridge/brief target must be a string or symbol".to_owned(),
386 )),
387 }
388}
389
390fn call_name(expr: &sim_kernel::Expr) -> Result<String> {
391 match expr {
392 sim_kernel::Expr::String(name) => Ok(name.clone()),
393 sim_kernel::Expr::Symbol(name) => Ok(name.as_qualified_str()),
394 _ => Err(Error::Eval(
395 "bridge/ask call must be a string or symbol".to_owned(),
396 )),
397 }
398}
399
400fn call_params(expr: &sim_kernel::Expr) -> Result<Vec<(String, sim_kernel::Expr)>> {
401 let sim_kernel::Expr::Map(entries) = expr else {
402 return Err(Error::Eval("bridge/ask params must be a map".to_owned()));
403 };
404 entries
405 .iter()
406 .map(|(key, value)| {
407 let name = match key {
408 sim_kernel::Expr::String(name) => name.clone(),
409 sim_kernel::Expr::Symbol(name) => name.as_qualified_str(),
410 _ => {
411 return Err(Error::Eval(
412 "bridge/ask param keys must be strings or symbols".to_owned(),
413 ));
414 }
415 };
416 Ok((name, value.clone()))
417 })
418 .collect()
419}