sim-kernel 0.1.0-rc.1

SIM workspace package for sim kernel.
Documentation
use super::registry_catalog_view;
use crate::{Cx, Error, Expr, Symbol, registry_catalog_read_capability};

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

#[test]
fn catalog_view_lists_registry_tables_opens_exports_and_shows_rows() {
    let mut cx = Cx::stub();
    let symbol = sym("catalog-view-value");
    let value = cx.factory().bool(true).unwrap();
    cx.registry_mut()
        .register_value(symbol.clone(), value)
        .unwrap();
    cx.grant(registry_catalog_read_capability());

    let root = registry_catalog_view(&mut cx).unwrap();
    let root_table = root.object().as_table_impl().unwrap();
    assert_eq!(root_table.keys(&mut cx).unwrap(), vec![sym("registry")]);

    let registry_dir = root
        .object()
        .as_dir()
        .unwrap()
        .opendir(&mut cx, sym("registry"))
        .unwrap()
        .unwrap();
    let registry_table = registry_dir.object().as_table_impl().unwrap();
    assert!(
        registry_table
            .keys(&mut cx)
            .unwrap()
            .contains(&sym("exports"))
    );

    let exports = registry_dir
        .object()
        .as_dir()
        .unwrap()
        .opendir(&mut cx, sym("exports"))
        .unwrap()
        .unwrap();
    let exports_table = exports.object().as_table_impl().unwrap();
    let rows = exports_table.entries(&mut cx).unwrap();
    let row = rows
        .into_iter()
        .find_map(
            |(_, value)| match value.object().as_expr(&mut cx).unwrap() {
                Expr::Map(entries)
                    if map_field(&entries, "symbol") == Some(&Expr::Symbol(symbol.clone())) =>
                {
                    Some(entries)
                }
                _ => None,
            },
        )
        .unwrap();

    assert_eq!(
        map_field(&row, "state"),
        Some(&Expr::Symbol(sym("resolved")))
    );
    assert_eq!(
        exports_table
            .get(&mut cx, sym("missing"))
            .unwrap()
            .object()
            .as_expr(&mut cx)
            .unwrap(),
        Expr::Nil
    );
}

#[test]
fn catalog_view_mutations_fail_closed_and_typed_lookups_stay_unchanged() {
    let mut cx = Cx::stub();
    let symbol = sym("catalog-view-typed-value");
    let value = cx.factory().bool(true).unwrap();
    cx.registry_mut()
        .register_value(symbol.clone(), value.clone())
        .unwrap();

    assert!(matches!(
        registry_catalog_view(&mut cx),
        Err(Error::CapabilityDenied { capability })
            if capability == registry_catalog_read_capability()
    ));

    cx.grant(registry_catalog_read_capability());
    let root = registry_catalog_view(&mut cx).unwrap();
    assert_eq!(cx.registry().value_by_symbol(&symbol), Some(&value));

    let exports = root
        .object()
        .as_dir()
        .unwrap()
        .opendir(&mut cx, Symbol::qualified("registry", "exports"))
        .unwrap()
        .unwrap();
    let exports_table = exports.object().as_table_impl().unwrap();
    let nil = cx.factory().nil().unwrap();
    assert!(matches!(
        exports_table.set(&mut cx, sym("new"), nil),
        Err(Error::CatalogReadOnly { table })
            if table == Symbol::qualified("registry", "exports")
    ));
    assert!(matches!(
        root.object().as_dir().unwrap().mkdir(&mut cx, sym("new")),
        Err(Error::CatalogReadOnly { .. })
    ));

    assert_eq!(cx.registry().value_by_symbol(&symbol), Some(&value));
}

fn map_field<'a>(entries: &'a [(Expr, Expr)], field: &str) -> Option<&'a Expr> {
    entries
        .iter()
        .find_map(|(key, value)| (key == &Expr::Symbol(sym(field))).then_some(value))
}