sim-lib-server 0.1.0-rc.1

SIM workspace package for sim lib server.
Documentation
use std::fs;

use crate::{Connection, Server};
use sim_kernel::{Args, CapabilityName, Cx, Error, Expr, Result, Value};

pub(crate) fn symbol_from_value(
    cx: &mut Cx,
    value: Value,
    message: &'static str,
) -> Result<sim_kernel::Symbol> {
    match value.object().as_expr(cx)? {
        Expr::Symbol(symbol) => Ok(symbol),
        _ => Err(Error::Eval(message.to_owned())),
    }
}

pub(crate) fn string_like_from_value(
    cx: &mut Cx,
    value: Value,
    message: &'static str,
) -> Result<String> {
    match value.object().as_expr(cx)? {
        Expr::String(text) => Ok(text),
        Expr::Symbol(symbol) => Ok(symbol.to_string()),
        _ => Err(Error::Eval(message.to_owned())),
    }
}

pub(crate) fn wasm_module_bytes_from_value(
    cx: &mut Cx,
    value: Value,
    message: &'static str,
) -> Result<Vec<u8>> {
    match value.object().as_expr(cx)? {
        Expr::Bytes(bytes) => Ok(bytes),
        Expr::String(path) => fs::read(&path)
            .map_err(|err| Error::HostError(format!("failed to read wasm module {path}: {err}"))),
        _ => Err(Error::Eval(message.to_owned())),
    }
}

pub(crate) fn bool_from_value(cx: &mut Cx, value: Value, message: &'static str) -> Result<bool> {
    match value.object().as_expr(cx)? {
        Expr::Bool(value) => Ok(value),
        _ => Err(Error::Eval(message.to_owned())),
    }
}

pub(crate) fn symbol_list_from_value(cx: &mut Cx, value: Value) -> Result<Vec<sim_kernel::Symbol>> {
    match value.object().as_expr(cx)? {
        Expr::Nil => Ok(Vec::new()),
        Expr::List(items) | Expr::Vector(items) => items
            .into_iter()
            .map(|expr| match expr {
                Expr::Symbol(symbol) => Ok(symbol),
                _ => Err(Error::TypeMismatch {
                    expected: "symbol list",
                    found: "non-symbol",
                }),
            })
            .collect(),
        Expr::Symbol(symbol) => Ok(vec![symbol]),
        _ => Err(Error::TypeMismatch {
            expected: "symbol list",
            found: "non-list",
        }),
    }
}

pub(crate) fn capability_names_from_value(
    cx: &mut Cx,
    value: Value,
) -> Result<Vec<CapabilityName>> {
    match value.object().as_expr(cx)? {
        Expr::Nil => Ok(Vec::new()),
        Expr::List(items) | Expr::Vector(items) => {
            items.into_iter().map(capability_name_from_expr).collect()
        }
        Expr::Symbol(symbol) => Ok(vec![CapabilityName::new(symbol.to_string())]),
        Expr::String(text) => Ok(vec![CapabilityName::new(text)]),
        _ => Err(Error::TypeMismatch {
            expected: "capability list",
            found: "non-list",
        }),
    }
}

fn capability_name_from_expr(expr: Expr) -> Result<CapabilityName> {
    match expr {
        Expr::Symbol(symbol) => Ok(CapabilityName::new(symbol.to_string())),
        Expr::String(text) => Ok(CapabilityName::new(text)),
        _ => Err(Error::TypeMismatch {
            expected: "capability symbol or string",
            found: "non-capability",
        }),
    }
}

pub(crate) fn clone_server_cx(seed: &Cx) -> Cx {
    let mut cloned = Cx::new(seed.eval_policy_ref(), seed.factory_ref());
    *cloned.env_mut() = seed.env().clone();
    *cloned.registry_mut() = seed.registry().clone();
    *cloned.sources_mut() = seed.sources().clone();
    cloned.set_promotion_search_limits(seed.promotion_search_limits());
    for capability in seed.capabilities().iter() {
        cloned.grant(capability.clone());
    }
    if let Some(expander) = seed.macro_expander_ref() {
        cloned.set_macro_expander(expander);
    }
    cloned
}

pub(crate) fn coerce_result_shape(
    cx: &mut Cx,
    value: Value,
) -> Result<Option<sim_kernel::ShapeRef>> {
    if matches!(value.object().as_expr(cx)?, Expr::Nil) {
        return Ok(None);
    }
    if value.object().as_shape().is_some() {
        return Ok(Some(value));
    }
    if let Some(class) = value.object().as_class() {
        return Ok(Some(class.instance_shape(cx)?));
    }
    Err(Error::TypeMismatch {
        expected: "shape or class",
        found: "non-shape",
    })
}

pub(crate) fn server_arg<'a>(
    args: &'a Args,
    index: usize,
    message: &'static str,
) -> Result<&'a Server> {
    let Some(value) = args.values().get(index) else {
        return Err(Error::Eval(message.to_owned()));
    };
    value
        .object()
        .downcast_ref::<Server>()
        .ok_or(Error::TypeMismatch {
            expected: "server",
            found: "non-server",
        })
}

pub(crate) fn connection_arg<'a>(
    args: &'a Args,
    index: usize,
    message: &'static str,
) -> Result<&'a Connection> {
    let Some(value) = args.values().get(index) else {
        return Err(Error::Eval(message.to_owned()));
    };
    value
        .object()
        .downcast_ref::<Connection>()
        .ok_or(Error::TypeMismatch {
            expected: "connection",
            found: "non-connection",
        })
}