sim-nest 0.1.5

The SIM constellation umbrella facade: one crate that re-exports the runtime kernel, codecs, number domains, and libraries behind features.
Documentation
use sim_kernel::{
    ContentId, Coordinate, Cx, Error, Expr, HandleId, PreparedArgs, Ref, RefResolver, Result,
    Symbol, TemporaryRefResolver,
};

pub(super) fn prepared_ref(
    prepared: &PreparedArgs,
    cx: &mut Cx,
    index: usize,
    message: &str,
) -> Result<Ref> {
    let Some(value) = prepared.get(index).cloned() else {
        return Err(Error::Eval(message.to_owned()));
    };
    let expr = value.object().as_expr(cx)?;
    if let Some(reference) = ref_from_expr(&expr)? {
        return Ok(reference);
    }
    match expr {
        Expr::Symbol(symbol) => Ok(Ref::Symbol(symbol)),
        Expr::String(text) => Ok(Ref::Symbol(parse_symbol_text(&text))),
        _ => {
            let mut resolver = TemporaryRefResolver::new();
            resolver.ref_for_value(cx, &value)
        }
    }
}

pub(super) fn ref_from_expr(expr: &Expr) -> Result<Option<Ref>> {
    let Expr::Extension { tag, payload } = expr else {
        return Ok(None);
    };
    if tag != &Symbol::qualified("core", "ref") {
        return Ok(None);
    }
    let Expr::Map(entries) = payload.as_ref() else {
        return Err(Error::Eval("core/ref payload must be a map".to_owned()));
    };
    let kind = required_symbol(entries, "kind")?;
    if kind == Symbol::qualified("core", "content") {
        return content_id_from_entries(entries).map(|id| Some(Ref::Content(id)));
    }
    if kind == Symbol::qualified("core", "handle") {
        return handle_id_from_entries(entries).map(|handle| Some(Ref::Handle(handle)));
    }
    if kind == Symbol::qualified("core", "coord") {
        return coordinate_from_entries(entries).map(|coord| Some(Ref::Coord(coord)));
    }
    Err(Error::Eval(format!("unknown core/ref kind {kind}")))
}

pub(super) fn parse_symbol_text(value: &str) -> Symbol {
    match value.split_once('/') {
        Some((namespace, name)) if !namespace.is_empty() && !name.is_empty() => {
            Symbol::qualified(namespace.to_owned(), name.to_owned())
        }
        _ => Symbol::new(value.to_owned()),
    }
}

fn content_id_from_entries(entries: &[(Expr, Expr)]) -> Result<ContentId> {
    let algorithm = required_symbol(entries, "algorithm")?;
    let bytes = required_bytes(entries, "bytes", 32)?;
    let mut digest = [0_u8; 32];
    digest.copy_from_slice(bytes);
    Ok(ContentId::from_bytes(algorithm, digest))
}

fn handle_id_from_entries(entries: &[(Expr, Expr)]) -> Result<HandleId> {
    let bytes = required_bytes(entries, "id", 16)?;
    let mut id = [0_u8; 16];
    id.copy_from_slice(bytes);
    Ok(HandleId(u128::from_be_bytes(id)))
}

fn coordinate_from_entries(entries: &[(Expr, Expr)]) -> Result<Coordinate> {
    let space = required_symbol(entries, "space")?;
    let ordinal = required_field(entries, "ordinal")?;
    let Some(Ref::Content(ordinal)) = ref_from_expr(ordinal)? else {
        return Err(Error::Eval(
            "core/ref coord ordinal must be a content ref".to_owned(),
        ));
    };
    Ok(Coordinate { space, ordinal })
}

fn required_symbol(entries: &[(Expr, Expr)], name: &str) -> Result<Symbol> {
    match required_field(entries, name)? {
        Expr::Symbol(symbol) => Ok(symbol.clone()),
        _ => Err(Error::TypeMismatch {
            expected: "symbol",
            found: "non-symbol",
        }),
    }
}

fn required_bytes<'a>(
    entries: &'a [(Expr, Expr)],
    name: &str,
    expected: usize,
) -> Result<&'a [u8]> {
    let bytes = match required_field(entries, name)? {
        Expr::Bytes(bytes) => bytes.as_slice(),
        _ => {
            return Err(Error::TypeMismatch {
                expected: "bytes",
                found: "non-bytes",
            });
        }
    };
    if bytes.len() != expected {
        return Err(Error::Eval(format!(
            "core/ref {name} must contain {expected} bytes"
        )));
    }
    Ok(bytes)
}

fn required_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a Expr> {
    expr_field(entries, name).ok_or_else(|| Error::Eval(format!("missing core/ref field {name}")))
}

use super::fields::expr_entry_field as expr_field;