sim-lib-namespace 0.2.0

Namespace records and capability-aware source module lifecycle for SIM libraries.
Documentation
#[test]
fn neutral_source_policy_specimen_matches_the_frozen_capture() {
    use sim_lib_core::{DynamicSourcePolicy, ReadEvalOutcome, RequestOrigin};
    use sim_shape::{ExprKind, ExprKindShape};

    fn authority() -> SourceAuthority {
        SourceAuthority::new(
            ReadPolicy {
                trust: TrustLevel::TrustedSource,
                capabilities: CapabilitySet::new().grant(read_eval_capability()),
            },
            vec![module_load_capability()],
            CapabilitySet::new().grant(read_eval_capability()),
        )
        .unwrap()
    }

    let mut cx = context();
    let root = Arc::new(MemoryDir::default());
    root.source(&mut cx, "value.sim", "\"module-value\"");
    root.source(&mut cx, "broken.sim", "(");

    let modules = SourceModulePolicy::new(
        Symbol::qualified("codec", "lisp"),
        Arc::new(IdentitySpecifierPolicy),
    );
    let first = modules
        .load(
            &mut cx,
            Symbol::new("synthetic-root"),
            root.clone(),
            "value.sim",
            authority(),
        )
        .unwrap();
    let cached = modules
        .dynamic_import(
            &mut cx,
            Symbol::new("synthetic-root"),
            root.clone(),
            None,
            "value.sim",
            authority(),
        )
        .unwrap();
    assert_eq!(first.identity().path(), "value.sim");
    assert_eq!(cached.generation(), first.generation());

    let first_failure = modules
        .load(
            &mut cx,
            Symbol::new("synthetic-root"),
            root.clone(),
            "broken.sim",
            authority(),
        )
        .unwrap_err()
        .to_string();
    root.source(&mut cx, "broken.sim", "\"repair must remain cached\"");
    let cached_failure = modules
        .load(
            &mut cx,
            Symbol::new("synthetic-root"),
            root,
            "broken.sim",
            authority(),
        )
        .unwrap_err()
        .to_string();
    assert_eq!(cached_failure, first_failure);

    let dynamic = DynamicSourcePolicy::new(
        Symbol::qualified("codec", "lisp"),
        RequestOrigin::new(Symbol::qualified("specimen", "neutral-source")),
    );
    dynamic
        .evaluate_text(
            &mut cx,
            "\"dynamic-value\"",
            authority(),
            Arc::new(ExprKindShape::new(ExprKind::String)),
        )
        .unwrap();
    dynamic
        .evaluate_text(
            &mut cx,
            "nil",
            authority(),
            Arc::new(ExprKindShape::new(ExprKind::String)),
        )
        .unwrap_err();

    let cycle_identity = ModuleIdentity {
        root: Symbol::new("synthetic-root"),
        path: "cycle.sim".to_owned(),
    };
    modules.loader.state.lock().unwrap().cache.insert(
        cycle_identity,
        CacheState::Initializing {
            owner: std::thread::current().id(),
            generation: 1,
        },
    );
    modules
        .load(
            &mut cx,
            Symbol::new("synthetic-root"),
            Arc::new(MemoryDir::default()),
            "cycle.sim",
            authority(),
        )
        .unwrap_err();

    let receipts = modules.receipts().unwrap();
    let module_capture = receipts
        .iter()
        .map(|receipt| {
            (
                receipt.identity.path().to_owned(),
                receipt.generation,
                receipt.outcome,
                receipt.read_eval_event.as_ref().map(|event| event.seq),
            )
        })
        .collect::<Vec<_>>();
    assert_eq!(
        module_capture,
        vec![
            (
                "value.sim".to_owned(),
                1,
                ModuleResolutionOutcome::Linked,
                Some(0)
            ),
            (
                "value.sim".to_owned(),
                1,
                ModuleResolutionOutcome::CacheHit,
                None
            ),
            (
                "broken.sim".to_owned(),
                1,
                ModuleResolutionOutcome::DecodeFailed,
                Some(1)
            ),
            (
                "broken.sim".to_owned(),
                1,
                ModuleResolutionOutcome::DecodeFailed,
                None
            ),
            (
                "cycle.sim".to_owned(),
                1,
                ModuleResolutionOutcome::Cycle,
                None
            ),
        ]
    );

    let module_decisions = modules.decisions(&cx).unwrap();
    assert_eq!(module_decisions.len(), 2);
    assert!(module_decisions.iter().all(|decision| {
        decision.requested == vec![read_eval_capability()]
            && decision.active == vec![read_eval_capability()]
    }));
    assert_eq!(
        dynamic
            .decisions(&cx)
            .unwrap()
            .iter()
            .map(|decision| decision.outcome.clone())
            .collect::<Vec<_>>(),
        vec![ReadEvalOutcome::Admitted, ReadEvalOutcome::ShapeDenied]
    );
}