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 std::sync::Arc;

use sim_kernel::{Args, Cx, DefaultFactory, Expr, NoopEvalPolicy, Symbol, Value};

use crate::runtime::install_core_runtime;

use super::support::table_value;

const SHAPE_HELPERS: &[&str] = &[
    "and",
    "all",
    "or",
    "any",
    "not",
    "none",
    "without",
    "list",
    "list-rest",
    "table",
    "table-required",
    "table-open",
    "table-closed",
    "repeat",
    "repeat-bounds",
    "compare",
    "compare-with",
    "venn",
    "venn-union",
    "venn-intersection",
    "venn-only",
    "venn-outside",
    "venn-exactly",
    "hook",
    "hook-trace",
    "hook-score-floor",
    "hook-accept-on-no-diagnostics",
    "hook-discard-on-diagnostic-prefix",
];

#[test]
fn shape_extension_helpers_are_browseable_with_authored_help() {
    let mut cx = test_cx();

    for name in SHAPE_HELPERS {
        let subject = Symbol::qualified("shape", *name);
        let subject_value = cx.factory().symbol(subject.clone()).unwrap();
        let card = call(
            &mut cx,
            Symbol::qualified("core", "browse"),
            vec![subject_value],
        );
        let card = expr(&mut cx, &card);

        assert_eq!(
            table_value(&card, &field("subject")),
            Some(&Expr::Symbol(subject.clone())),
            "{subject} Card should keep subject identity"
        );
        assert_eq!(
            table_value(&card, &field("kind")),
            Some(&Expr::Symbol(Symbol::qualified("core", "function"))),
            "{subject} should browse as a function"
        );
        assert_eq!(
            table_value(&card, &field("shape-known")),
            Some(&Expr::Bool(true)),
            "{subject} should publish call shapes"
        );

        let help = table_value(&card, &field("help")).expect("help");
        assert_eq!(
            table_value(help, &field("kind")),
            Some(&Expr::Symbol(Symbol::qualified("core", "function"))),
            "{subject} help should be authored as function help"
        );
        assert!(
            matches!(
                table_value(help, &field("summary")),
                Some(Expr::String(summary)) if !summary.is_empty()
            ),
            "{subject} should have a summary"
        );
        assert!(
            matches!(
                table_value(help, &field("detail")),
                Some(Expr::String(detail)) if detail.contains("shape:")
            ),
            "{subject} should have a shape helper detail"
        );
        assert!(
            has_shape_see_also(table_value(help, &field("see-also")).expect("see-also")),
            "{subject} should link to related shape helpers"
        );

        let subject_value = cx.factory().symbol(subject.clone()).unwrap();
        let legacy_help = call(
            &mut cx,
            Symbol::qualified("core", "help"),
            vec![subject_value],
        );
        let legacy_help = expr(&mut cx, &legacy_help);
        assert!(
            matches!(
                table_value(&legacy_help, &field("purpose")),
                Some(Expr::String(summary)) if !summary.is_empty()
            ),
            "{subject} should also be discoverable through core/help"
        );
    }
}

fn test_cx() -> Cx {
    let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    cx
}

fn call(cx: &mut Cx, symbol: Symbol, args: Vec<Value>) -> Value {
    cx.call_function(&symbol, Args::new(args)).unwrap()
}

fn expr(cx: &mut Cx, value: &Value) -> Expr {
    value.object().as_expr(cx).unwrap()
}

fn has_shape_see_also(expr: &Expr) -> bool {
    let Expr::List(items) = expr else {
        return false;
    };
    items.iter().any(|item| {
        matches!(
            item,
            Expr::Symbol(symbol) if symbol.namespace.as_deref() == Some("shape")
        )
    })
}

fn field(name: &str) -> Symbol {
    Symbol::new(name)
}