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
mod codec_domain;
mod k6;
mod server_agent;

use sim_kernel::{
    CapabilityName, ContentId, Cx, Datum, DatumStore, Expr, Ref, Result, Symbol, Value,
    value_from_datum,
};

use super::schema::{FacetBuilder, RedactionBuilder};

pub(super) fn surface_facets(
    cx: &mut Cx,
    subject: &Ref,
    _spine: &[(Symbol, Value)],
) -> Result<Vec<Value>> {
    if let Ref::Content(id) = subject {
        return content_facets(cx, id);
    }
    let Ref::Symbol(symbol) = subject else {
        return Ok(Vec::new());
    };
    let mut facets = Vec::new();
    if server_agent::is_server_surface(symbol) {
        facets.extend(server_agent::server_facets(cx)?);
    }
    if server_agent::is_agent_surface(symbol) {
        facets.push(server_agent::agent_surface_facet(cx, symbol)?);
    }
    if cx.registry().codec_by_symbol(symbol).is_some() {
        facets.extend(codec_domain::codec_facets(cx, symbol)?);
    }
    if cx.registry().number_domain_by_symbol(symbol).is_some() {
        facets.extend(codec_domain::number_domain_facets(cx, symbol)?);
    }
    if k6::is_stream_surface(symbol) {
        facets.extend(k6::stream_facets(cx, symbol)?);
    }
    if k6::is_rank_surface(symbol) {
        facets.push(k6::rank_space_facet(cx)?);
    }
    if k6::is_standard_surface(symbol) {
        facets.push(k6::standard_fidelity_facet(cx)?);
    }
    Ok(facets)
}

fn content_facets(cx: &mut Cx, id: &ContentId) -> Result<Vec<Value>> {
    let facet = match cx.datum_store().get(id)?.cloned() {
        Some(datum) => content_facet(cx, id, datum)?,
        None => missing_content_facet(cx, id)?,
    };
    Ok(vec![facet])
}

fn content_facet(cx: &mut Cx, id: &ContentId, datum: Datum) -> Result<Value> {
    let datum_kind = datum_kind(&datum);
    let value = value_from_datum(cx, datum)?;
    let payload = cx.factory().table(vec![
        (
            field("algorithm"),
            cx.factory().symbol(id.algorithm.clone())?,
        ),
        (field("bytes"), cx.factory().bytes(id.bytes.to_vec())?),
        (field("datum-kind"), cx.factory().symbol(datum_kind)?),
        (field("value"), value),
    ])?;
    public_facet(
        cx,
        Symbol::qualified("browse", "content"),
        Symbol::new("content"),
        payload,
        Vec::new(),
    )
}

fn missing_content_facet(cx: &mut Cx, id: &ContentId) -> Result<Value> {
    let missing_ref = sim_kernel::card::ref_value(cx, &Ref::Content(id.clone()))?;
    let payload = cx.factory().table(vec![
        (
            field("algorithm"),
            cx.factory().symbol(id.algorithm.clone())?,
        ),
        (field("bytes"), cx.factory().bytes(id.bytes.to_vec())?),
        (field("missing-ref"), missing_ref),
    ])?;
    public_facet(
        cx,
        Symbol::qualified("browse", "missing-ref"),
        Symbol::new("missing-ref"),
        payload,
        Vec::new(),
    )
}

fn datum_kind(datum: &Datum) -> Symbol {
    let name = match datum {
        Datum::Nil => "nil",
        Datum::Bool(_) => "bool",
        Datum::Number(_) => "number",
        Datum::Symbol(_) => "symbol",
        Datum::String(_) => "string",
        Datum::Bytes(_) => "bytes",
        Datum::List(_) => "list",
        Datum::Vector(_) => "vector",
        Datum::Map(_) => "map",
        Datum::Set(_) => "set",
        Datum::Node { .. } => "node",
    };
    Symbol::qualified("datum", name)
}

fn public_facet(
    cx: &mut Cx,
    name: Symbol,
    kind: Symbol,
    value: Value,
    evidence: Vec<Value>,
) -> Result<Value> {
    facet(cx, name, kind, value, Vec::new(), "public", evidence)
}

fn private_facet(
    cx: &mut Cx,
    name: Symbol,
    kind: Symbol,
    value: Value,
    requires: Vec<CapabilityName>,
    evidence: Vec<Value>,
) -> Result<Value> {
    if requires
        .iter()
        .all(|capability| cx.capabilities().contains(capability))
    {
        return facet(cx, name, kind, value, requires, "private", evidence);
    }
    let redacted = redaction(cx, &requires)?;
    facet(cx, name, kind, redacted, requires, "private", evidence)
}

fn empty_public_facet(cx: &mut Cx, name: Symbol) -> Result<Value> {
    let value = cx.factory().list(Vec::new())?;
    public_facet(cx, name, Symbol::new("custom"), value, Vec::new())
}

fn facet(
    cx: &mut Cx,
    name: Symbol,
    kind: Symbol,
    value: Value,
    requires: Vec<CapabilityName>,
    visibility: &'static str,
    evidence: Vec<Value>,
) -> Result<Value> {
    let mut builder = FacetBuilder::new(name);
    builder.kind = kind;
    builder.shape = Symbol::qualified("core", "Any");
    builder.value = Some(value);
    builder.requires = capability_values(cx, &requires)?;
    builder.visibility = Symbol::new(visibility);
    builder.evidence = evidence;
    builder.build(cx)
}

fn redaction(cx: &mut Cx, requires: &[CapabilityName]) -> Result<Value> {
    let mut builder = RedactionBuilder::unavailable();
    builder.reason = Symbol::new("capability-required");
    builder.requires = capability_values(cx, requires)?;
    builder.summary = format!(
        "requires {}",
        requires
            .iter()
            .map(CapabilityName::as_str)
            .collect::<Vec<_>>()
            .join(", ")
    );
    builder.build(cx)
}

fn object_table(cx: &mut Cx, value: Value) -> Result<Vec<(Symbol, Value)>> {
    if let Some(table) = value.object().as_table_impl() {
        return table.entries(cx);
    }
    let table = value.object().as_table(cx)?;
    match table.object().as_table_impl() {
        Some(table) => table.entries(cx),
        None => Ok(Vec::new()),
    }
}

fn string_field(cx: &mut Cx, entries: &[(Symbol, Value)], name: &str) -> Result<Option<String>> {
    let Some(value) = field_value(entries, name) else {
        return Ok(None);
    };
    Ok(match value.object().as_expr(cx)? {
        Expr::String(text) => Some(text),
        _ => None,
    })
}

use super::fields::{key as field, value_field as field_value};

fn symbol_payload(cx: &mut Cx, key: &'static str, symbols: Vec<Symbol>) -> Result<Value> {
    let values = symbol_list(cx, symbols)?;
    cx.factory().table(vec![(field(key), values)])
}

fn symbol_list(cx: &mut Cx, symbols: Vec<Symbol>) -> Result<Value> {
    let values = symbols
        .into_iter()
        .map(|symbol| cx.factory().symbol(symbol))
        .collect::<Result<Vec<_>>>()?;
    cx.factory().list(values)
}

fn string_list(cx: &mut Cx, values: Vec<&str>) -> Result<Value> {
    let values = values
        .into_iter()
        .map(|value| cx.factory().string(value.to_owned()))
        .collect::<Result<Vec<_>>>()?;
    cx.factory().list(values)
}

fn capability_list(cx: &mut Cx, capabilities: Vec<&'static str>) -> Result<Value> {
    let values = capabilities
        .into_iter()
        .map(|capability| {
            cx.factory()
                .symbol(Symbol::qualified("capability", capability))
        })
        .collect::<Result<Vec<_>>>()?;
    cx.factory().list(values)
}

fn capability_values(cx: &mut Cx, capabilities: &[CapabilityName]) -> Result<Vec<Value>> {
    capabilities
        .iter()
        .map(|capability| cx.factory().symbol(capability.as_symbol()))
        .collect()
}