use std::sync::Arc;
use sim_codec_bridge::{BridgeBook, BridgeFramePayload, expr_to_packet, packet_to_expr};
use sim_kernel::{
AbiVersion, Args, Callable, Cx, Error, Export, Lib, LibManifest, LibTarget, Linker, LoadCx,
Object, ObjectCompat, Result, Symbol, Value, Version,
};
use sim_shape::{AnyShape, ListShape, Shape, shape_value};
use crate::{ask_packet, bridge_brief, bridge_tx, receipt_packet_for_report, rx_check};
pub struct BridgeLib;
impl Lib for BridgeLib {
fn manifest(&self) -> LibManifest {
LibManifest {
id: manifest_name(),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::new(),
capabilities: Vec::new(),
exports: bridge_exports(),
}
}
fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
for kind in BridgeFunctionKind::ALL {
let function = BridgeFunction::value(kind);
linker.function_value(function.symbol(), cx.factory().opaque(function)?)?;
}
Ok(())
}
}
pub fn install_bridge_lib(cx: &mut Cx) -> Result<()> {
cx.load_lib(&BridgeLib).map(|_| ())
}
pub fn manifest_name() -> Symbol {
Symbol::qualified("sim", "bridge")
}
pub fn bridge_tx_symbol() -> Symbol {
Symbol::qualified("bridge", "tx")
}
pub fn bridge_rx_symbol() -> Symbol {
Symbol::qualified("bridge", "rx")
}
pub fn bridge_report_symbol() -> Symbol {
Symbol::qualified("bridge", "report")
}
pub fn bridge_brief_symbol() -> Symbol {
Symbol::qualified("bridge", "brief")
}
pub fn bridge_ask_symbol() -> Symbol {
Symbol::qualified("bridge", "ask")
}
fn bridge_exports() -> Vec<Export> {
BridgeFunctionKind::ALL
.iter()
.map(|kind| Export::Function {
symbol: kind.symbol(),
function_id: None,
})
.collect()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BridgeFunctionKind {
Tx,
Rx,
Report,
Receipt,
Brief,
Ask,
}
impl BridgeFunctionKind {
pub const ALL: [Self; 6] = [
Self::Tx,
Self::Rx,
Self::Report,
Self::Receipt,
Self::Brief,
Self::Ask,
];
pub fn symbol(self) -> Symbol {
match self {
Self::Tx => bridge_tx_symbol(),
Self::Rx => bridge_rx_symbol(),
Self::Report => bridge_report_symbol(),
Self::Receipt => crate::receipt_symbol(),
Self::Brief => bridge_brief_symbol(),
Self::Ask => bridge_ask_symbol(),
}
}
}
#[derive(Clone)]
pub struct BridgeFunction {
kind: BridgeFunctionKind,
}
impl BridgeFunction {
pub fn new(kind: BridgeFunctionKind) -> Self {
Self { kind }
}
pub fn symbol(&self) -> Symbol {
self.kind.symbol()
}
pub fn value(kind: BridgeFunctionKind) -> Arc<Self> {
Arc::new(Self::new(kind))
}
}
impl Object for BridgeFunction {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<function {}>", self.symbol()))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for BridgeFunction {
fn as_callable(&self) -> Option<&dyn Callable> {
Some(self)
}
}
impl Callable for BridgeFunction {
fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
match self.kind {
BridgeFunctionKind::Tx => call_tx(cx, args),
BridgeFunctionKind::Rx => call_rx(cx, args),
BridgeFunctionKind::Report => call_report(cx, args),
BridgeFunctionKind::Receipt => call_receipt(cx, args),
BridgeFunctionKind::Brief => call_brief(cx, args),
BridgeFunctionKind::Ask => call_ask(cx, args),
}
}
fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
let shape: Arc<dyn Shape> = match self.kind {
BridgeFunctionKind::Tx | BridgeFunctionKind::Report => {
Arc::new(ListShape::new(vec![Arc::new(AnyShape)]))
}
BridgeFunctionKind::Rx => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
BridgeFunctionKind::Receipt => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
BridgeFunctionKind::Brief => Arc::new(ListShape::new(vec![
Arc::new(AnyShape),
Arc::new(AnyShape),
Arc::new(AnyShape),
])),
BridgeFunctionKind::Ask => Arc::new(ListShape::new(vec![
Arc::new(AnyShape),
Arc::new(AnyShape),
Arc::new(AnyShape),
Arc::new(AnyShape),
])),
};
Ok(Some(shape_value(
Symbol::qualified(self.symbol().to_string(), "args"),
shape,
)))
}
fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
Ok(Some(shape_value(
Symbol::qualified(self.symbol().to_string(), "result"),
Arc::new(AnyShape),
)))
}
}
fn call_tx(cx: &mut Cx, args: Args) -> Result<Value> {
let packet = packet_arg(cx, args, "bridge/tx expects one packet expression")?;
let request = bridge_tx(cx, &BridgeBook::standard(), &packet)?;
cx.factory().opaque(Arc::new(request))
}
fn call_rx(cx: &mut Cx, args: Args) -> Result<Value> {
let response = one_expr_arg(cx, args, "bridge/rx expects one model response expression")?;
let (packet, report) = crate::bridge_rx(cx, &BridgeBook::standard(), response, None)?;
cx.factory().expr(sim_kernel::Expr::Map(vec![
sim_value::build::entry("packet", packet_to_expr(&packet)),
sim_value::build::entry("report", report.to_expr()),
]))
}
fn call_report(cx: &mut Cx, args: Args) -> Result<Value> {
let packet = packet_arg(cx, args, "bridge/report expects one packet expression")?;
let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
cx.factory().expr(report.to_expr())
}
fn call_receipt(cx: &mut Cx, args: Args) -> Result<Value> {
let packet = packet_arg(cx, args, "bridge/receipt expects one packet expression")?;
let report = rx_check(cx, &BridgeBook::standard(), &packet, None)?;
let receipt = receipt_packet_for_report(&report, "sim")?;
cx.factory().expr(packet_to_expr(&receipt))
}
fn call_brief(cx: &mut Cx, args: Args) -> Result<Value> {
let mut exprs = expr_args(
cx,
args,
"bridge/brief expects target, frame, and return shape",
)?;
let [target, frame, return_shape] = take_three(&mut exprs)?;
let frame = BridgeFramePayload::from_expr(&frame)?;
let packet = bridge_brief(&target_name(&target)?, frame, return_shape)?;
cx.factory().expr(packet_to_expr(&packet))
}
fn call_ask(cx: &mut Cx, args: Args) -> Result<Value> {
let mut exprs = expr_args(
cx,
args,
"bridge/ask expects target, call, params, and return shape",
)?;
if exprs.len() != 4 {
return Err(Error::Eval(format!(
"bridge/ask expects 4 argument(s), found {}",
exprs.len()
)));
}
let return_shape = exprs.pop().expect("length checked");
let params = exprs.pop().expect("length checked");
let call = exprs.pop().expect("length checked");
let target = exprs.pop().expect("length checked");
let packet = ask_packet(
cx,
&call_name(&call)?,
call_params(¶ms)?,
return_shape,
&target_name(&target)?,
)?;
cx.factory().expr(packet_to_expr(&packet))
}
fn packet_arg(
cx: &mut Cx,
args: Args,
message: &'static str,
) -> Result<sim_codec_bridge::BridgePacket> {
expr_to_packet(&one_expr_arg(cx, args, message)?)
}
fn expr_args(cx: &mut Cx, args: Args, message: &'static str) -> Result<Vec<sim_kernel::Expr>> {
let values = args.into_vec();
if values.is_empty() {
return Err(Error::Eval(message.to_owned()));
}
values
.into_iter()
.map(|value| value.object().as_expr(cx))
.collect()
}
fn one_expr_arg(cx: &mut Cx, args: Args, message: &'static str) -> Result<sim_kernel::Expr> {
let mut values = args.into_vec();
if values.len() != 1 {
return Err(Error::Eval(message.to_owned()));
}
values.remove(0).object().as_expr(cx)
}
fn take_three(exprs: &mut Vec<sim_kernel::Expr>) -> Result<[sim_kernel::Expr; 3]> {
let [target, frame, return_shape] =
std::mem::take(exprs).try_into().map_err(|values: Vec<_>| {
Error::Eval(format!(
"bridge/brief expects 3 argument(s), found {}",
values.len()
))
})?;
Ok([target, frame, return_shape])
}
fn target_name(expr: &sim_kernel::Expr) -> Result<String> {
match expr {
sim_kernel::Expr::String(target) => Ok(target.clone()),
sim_kernel::Expr::Symbol(target) => Ok(target.as_qualified_str().to_owned()),
_ => Err(Error::Eval(
"bridge/brief target must be a string or symbol".to_owned(),
)),
}
}
fn call_name(expr: &sim_kernel::Expr) -> Result<String> {
match expr {
sim_kernel::Expr::String(name) => Ok(name.clone()),
sim_kernel::Expr::Symbol(name) => Ok(name.as_qualified_str()),
_ => Err(Error::Eval(
"bridge/ask call must be a string or symbol".to_owned(),
)),
}
}
fn call_params(expr: &sim_kernel::Expr) -> Result<Vec<(String, sim_kernel::Expr)>> {
let sim_kernel::Expr::Map(entries) = expr else {
return Err(Error::Eval("bridge/ask params must be a map".to_owned()));
};
entries
.iter()
.map(|(key, value)| {
let name = match key {
sim_kernel::Expr::String(name) => name.clone(),
sim_kernel::Expr::Symbol(name) => name.as_qualified_str(),
_ => {
return Err(Error::Eval(
"bridge/ask param keys must be strings or symbols".to_owned(),
));
}
};
Ok((name, value.clone()))
})
.collect()
}