sim-lib-forge 0.1.3

Compiled intent records and one-shot BRIDGE lifts for reusable packet programs.
Documentation
use std::sync::Arc;

use sim_kernel::{
    AbiVersion, Args, Callable, CapabilityName, Claim, ClassRef, Cx, Datum, DefaultFactory,
    EagerPolicy, Export, ExportKind, Expr, ExprKind, Lib, LibManifest, LibTarget, Linker, LoadCx,
    Object, ObjectCompat, Ref, Result, Symbol, Value, Version, card::card_help_predicate,
};
use sim_shape::{
    AnyShape, ExprKindShape, ListShape, Shape, TableExtraPolicy, TableFieldSpec, TableShape,
    check_shape_on_expr, parse_shape_expr, shape_value,
};

use crate::{
    ContractDeckCache, ContractGap, ShapeQuery, assemble_contract_deck, contract_card_from_expr,
    contract_card_shape, query_contract_deck,
};

#[test]
fn contract_deck_assembles_runtime_cards_and_round_trips() {
    let (mut cx, seat) = Cx::new_seated(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    seat.grant(&mut cx, CapabilityName::new("forge.fixture"))
        .unwrap();
    cx.load_lib(&FixtureContracts).unwrap();
    insert_help_claim(&mut cx, known_symbol(), "shape-known callable").unwrap();

    let deck = assemble_contract_deck(&mut cx).unwrap();
    let known = deck
        .cards
        .iter()
        .find(|card| card.symbol == known_symbol())
        .expect("known export card")
        .clone();
    let partial = deck
        .cards
        .iter()
        .find(|card| card.symbol == partial_symbol())
        .expect("partial export card");

    assert_eq!(known.lib, Symbol::qualified("fixture", "contracts"));
    assert_eq!(known.export_kind, Symbol::new(ExportKind::FUNCTION));
    assert_eq!(
        known.capability_symbols,
        vec![CapabilityName::new("forge.fixture").as_symbol()]
    );
    assert!(known.args_shape.is_some());
    assert!(known.result_shape.is_some());
    assert_eq!(known.summary, "shape-known callable");
    assert!(known.example.is_some());
    let Some(Expr::Call { args, .. }) = known.example.as_ref() else {
        panic!("expected synthesized call example");
    };
    assert_eq!(args, &vec![Expr::String("example".to_owned())]);
    assert_eq!(known.partial, vec![ContractGap::SynthesizedExample]);

    let expr = known.as_expr();
    let shape = parse_shape_expr(&contract_card_shape()).unwrap();
    let checked = check_shape_on_expr(shape.as_ref(), &mut cx, &expr).unwrap();
    assert!(checked.accepted, "{:?}", checked.diagnostics);
    let decoded = contract_card_from_expr(&mut cx, &expr).unwrap();
    assert_eq!(decoded, known);

    assert_eq!(partial.lib, Symbol::qualified("fixture", "contracts"));
    assert!(partial.partial.contains(&ContractGap::MissingCallableShape));
    assert!(partial.partial.contains(&ContractGap::MissingCard));
    assert!(partial.partial.contains(&ContractGap::MissingExample));
}

#[test]
fn shape_query_ranks_filters_reports_and_reuses_cached_deck() {
    let (mut cx, seat) = Cx::new_seated(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    seat.grant(&mut cx, CapabilityName::new("forge.fixture"))
        .unwrap();
    cx.load_lib(&FixtureContracts).unwrap();

    let mut cache = ContractDeckCache::new();
    let ranked = query_contract_deck(
        &mut cx,
        &mut cache,
        &ShapeQuery {
            args: Some(table_set_args_shape()),
            result: Some(table_result_shape()),
            limit: 8,
        },
    )
    .unwrap();

    assert_eq!(cache.misses(), 1);
    assert_eq!(cache.hits(), 0);
    assert!(!cache.last_report().cache_hit);
    assert!(cache.last_report().skipped_missing_shapes >= 1);
    assert_eq!(
        ranked.first().map(|ranked| ranked.card.symbol.clone()),
        Some(table_set_symbol())
    );
    assert!(
        ranked
            .iter()
            .any(|ranked| ranked.card.symbol == table_broad_symbol())
    );
    assert!(
        !ranked
            .iter()
            .any(|ranked| ranked.card.symbol == table_wrong_result_symbol())
    );

    let capped = query_contract_deck(
        &mut cx,
        &mut cache,
        &ShapeQuery {
            args: None,
            result: None,
            limit: 1,
        },
    )
    .unwrap();
    let report = cache.last_report();
    assert_eq!(cache.misses(), 1);
    assert_eq!(cache.hits(), 1);
    assert!(report.cache_hit);
    assert_eq!(capped.len(), 1);
    assert!(report.capped_results > 0);
    assert_eq!(
        report.matched_before_limit,
        capped.len() + report.capped_results
    );
}

struct FixtureContracts;

impl Lib for FixtureContracts {
    fn manifest(&self) -> LibManifest {
        LibManifest {
            id: Symbol::qualified("fixture", "contracts"),
            version: Version("0.1.0".to_owned()),
            abi: AbiVersion { major: 0, minor: 1 },
            target: LibTarget::HostRegistered,
            requires: Vec::new(),
            capabilities: vec![CapabilityName::new("forge.fixture")],
            exports: vec![
                Export::Function {
                    symbol: known_symbol(),
                    function_id: None,
                },
                Export::Function {
                    symbol: partial_symbol(),
                    function_id: None,
                },
                Export::Function {
                    symbol: table_set_symbol(),
                    function_id: None,
                },
                Export::Function {
                    symbol: table_broad_symbol(),
                    function_id: None,
                },
                Export::Function {
                    symbol: table_wrong_result_symbol(),
                    function_id: None,
                },
            ],
        }
    }

    fn load(&self, cx: &mut LoadCx, linker: &mut Linker) -> Result<()> {
        linker.function_value(
            known_symbol(),
            cx.factory().opaque(Arc::new(KnownCallable))?,
        )?;
        linker.unsupported_export(
            ExportKind::named(ExportKind::FUNCTION),
            partial_symbol(),
            "fixture partial",
        )?;
        linker.function_value(
            table_set_symbol(),
            cx.factory().opaque(Arc::new(ShapeCallable {
                display: "#<fixture-table-set-callable>",
                args: table_set_args_shape,
                result: table_result_shape,
            }))?,
        )?;
        linker.function_value(
            table_broad_symbol(),
            cx.factory().opaque(Arc::new(ShapeCallable {
                display: "#<fixture-table-broad-callable>",
                args: broad_table_set_args_shape,
                result: table_result_shape,
            }))?,
        )?;
        linker.function_value(
            table_wrong_result_symbol(),
            cx.factory().opaque(Arc::new(ShapeCallable {
                display: "#<fixture-table-wrong-result-callable>",
                args: table_set_args_shape,
                result: string_result_shape,
            }))?,
        )?;
        Ok(())
    }
}

struct KnownCallable;

impl Object for KnownCallable {
    fn display(&self, _cx: &mut Cx) -> Result<String> {
        Ok("#<fixture-known-callable>".to_owned())
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

impl ObjectCompat for KnownCallable {
    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
        cx.factory().class_stub(
            sim_kernel::CORE_FUNCTION_CLASS_ID,
            Symbol::qualified("core", "Function"),
        )
    }

    fn as_callable(&self) -> Option<&dyn Callable> {
        Some(self)
    }
}

impl Callable for KnownCallable {
    fn call(&self, cx: &mut Cx, _args: Args) -> Result<Value> {
        cx.factory().nil()
    }

    fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<Value>> {
        Ok(Some(shape_value(
            Symbol::qualified("fixture", "known-args"),
            Arc::new(ListShape::new(vec![Arc::new(ExprKindShape::new(
                ExprKind::String,
            ))])),
        )))
    }

    fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<Value>> {
        Ok(Some(shape_value(
            Symbol::qualified("fixture", "known-result"),
            Arc::new(ExprKindShape::new(ExprKind::Bool)),
        )))
    }
}

struct ShapeCallable {
    display: &'static str,
    args: fn() -> Value,
    result: fn() -> Value,
}

impl Object for ShapeCallable {
    fn display(&self, _cx: &mut Cx) -> Result<String> {
        Ok(self.display.to_owned())
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

impl ObjectCompat for ShapeCallable {
    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
        cx.factory().class_stub(
            sim_kernel::CORE_FUNCTION_CLASS_ID,
            Symbol::qualified("core", "Function"),
        )
    }

    fn as_callable(&self) -> Option<&dyn Callable> {
        Some(self)
    }
}

impl Callable for ShapeCallable {
    fn call(&self, cx: &mut Cx, _args: Args) -> Result<Value> {
        cx.factory().nil()
    }

    fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<Value>> {
        Ok(Some((self.args)()))
    }

    fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<Value>> {
        Ok(Some((self.result)()))
    }
}

fn insert_help_claim(cx: &mut Cx, subject: Symbol, help: &str) -> Result<()> {
    let claim = Claim::content_object(
        cx.datum_store_mut(),
        Ref::Symbol(subject),
        card_help_predicate(),
        Datum::String(help.to_owned()),
    )?;
    cx.insert_fact(claim)?;
    Ok(())
}

fn known_symbol() -> Symbol {
    Symbol::qualified("fixture", "known")
}

fn partial_symbol() -> Symbol {
    Symbol::qualified("fixture", "partial")
}

fn table_set_symbol() -> Symbol {
    Symbol::qualified("fixture", "table-set")
}

fn table_broad_symbol() -> Symbol {
    Symbol::qualified("fixture", "table-broad")
}

fn table_wrong_result_symbol() -> Symbol {
    Symbol::qualified("fixture", "table-wrong-result")
}

fn table_set_args_shape() -> Value {
    shape_value(
        Symbol::qualified("fixture", "table-set-args"),
        Arc::new(ListShape::new(vec![
            table_shape(),
            Arc::new(ExprKindShape::new(ExprKind::Symbol)),
            Arc::new(AnyShape),
        ])),
    )
}

fn broad_table_set_args_shape() -> Value {
    shape_value(
        Symbol::qualified("fixture", "broad-table-set-args"),
        Arc::new(ListShape::new(vec![
            Arc::new(AnyShape),
            Arc::new(AnyShape),
            Arc::new(AnyShape),
        ])),
    )
}

fn table_result_shape() -> Value {
    shape_value(Symbol::qualified("fixture", "table-result"), table_shape())
}

fn string_result_shape() -> Value {
    shape_value(
        Symbol::qualified("fixture", "string-result"),
        Arc::new(ExprKindShape::new(ExprKind::String)),
    )
}

fn table_shape() -> Arc<dyn Shape> {
    Arc::new(TableShape::new(
        vec![TableFieldSpec {
            key: Symbol::new("key"),
            shape: Arc::new(ExprKindShape::new(ExprKind::Symbol)),
            required: true,
        }],
        TableExtraPolicy::Allow,
    ))
}