Skip to main content

sim_lib_physics_runtime/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3//! Loadable composition for the SIM physics stack.
4//!
5//! This crate contains no solver, audit, proof, study, or finding behavior. It
6//! projects the independently selectable layer-owned Shapes and operations as
7//! runtime cards through SIM's existing [`sim_kernel::Lib`] contract.
8
9use sim_kernel::{
10    AbiVersion, Cx, Export, Lib, LibManifest, LibTarget, Linker, LoadCx, Result, Symbol, Version,
11};
12
13/// Stable host-loader identity for the composed physics stack.
14pub const HOST_ID: &str = "lib/physics";
15
16/// Thin host-registered library that projects the enabled physics layers.
17pub struct PhysicsRuntimeLib;
18
19impl Lib for PhysicsRuntimeLib {
20    fn manifest(&self) -> LibManifest {
21        LibManifest {
22            id: Symbol::qualified("sim", "physics"),
23            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
24            abi: AbiVersion { major: 0, minor: 1 },
25            target: LibTarget::HostRegistered,
26            requires: Vec::new(),
27            capabilities: Vec::new(),
28            exports: surface_symbols()
29                .into_iter()
30                .map(|symbol| Export::Value { symbol })
31                .collect(),
32        }
33    }
34
35    fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
36        for (symbol, layer, kind) in surface_rows() {
37            let values = [symbol.to_string(), layer.to_owned(), kind.to_owned()]
38                .into_iter()
39                .map(|value| cx.factory().string(value))
40                .collect::<Result<Vec<_>>>()?;
41            linker.value(symbol, cx.factory().list(values)?)?;
42        }
43        Ok(())
44    }
45}
46
47/// Installs the enabled physics layer cards exactly once.
48pub fn install_physics_runtime(cx: &mut Cx) -> Result<()> {
49    let id = PhysicsRuntimeLib.manifest().id;
50    if cx.registry().lib(&id).is_none() {
51        cx.load_lib(&PhysicsRuntimeLib)?;
52    }
53    Ok(())
54}
55
56/// Returns the stable runtime symbols projected by the enabled layers.
57pub fn surface_symbols() -> Vec<Symbol> {
58    surface_rows()
59        .into_iter()
60        .map(|(symbol, _layer, _kind)| symbol)
61        .collect()
62}
63
64fn surface_rows() -> Vec<(Symbol, &'static str, &'static str)> {
65    let mut rows = Vec::new();
66    extend(&mut rows, "core", "shape", sim_lib_physics_core::SHAPES);
67    #[cfg(feature = "power")]
68    {
69        extend(&mut rows, "power", "shape", sim_lib_physics_power::SHAPES);
70        extend(
71            &mut rows,
72            "power",
73            "operation",
74            sim_lib_physics_power::RUNTIME_EXPORTS,
75        );
76    }
77    #[cfg(feature = "audit")]
78    {
79        extend(&mut rows, "audit", "shape", sim_lib_physics_audit::SHAPES);
80        extend(
81            &mut rows,
82            "audit",
83            "operation",
84            sim_lib_physics_audit::RUNTIME_EXPORTS,
85        );
86    }
87    #[cfg(feature = "proof")]
88    extend(
89        &mut rows,
90        "proof",
91        "operation",
92        &["physics/certified-verdict", "physics/refine"],
93    );
94    #[cfg(feature = "influence")]
95    extend(
96        &mut rows,
97        "influence",
98        "operation",
99        &["physics/audit-influence", "physics/prepare-selection"],
100    );
101    #[cfg(feature = "study")]
102    for symbol in sim_lib_physics_study::study_surface_symbols() {
103        rows.push((symbol, "study", "operation"));
104    }
105    #[cfg(feature = "findings")]
106    extend(
107        &mut rows,
108        "findings",
109        "operation",
110        &[
111            "physics/open-finding",
112            "physics/append-finding",
113            "physics/browse-findings",
114        ],
115    );
116    rows
117}
118
119fn extend(
120    rows: &mut Vec<(Symbol, &'static str, &'static str)>,
121    layer: &'static str,
122    kind: &'static str,
123    names: &[&str],
124) {
125    rows.extend(names.iter().map(|name| (parse_symbol(name), layer, kind)));
126}
127
128fn parse_symbol(name: &str) -> Symbol {
129    name.split_once('/').map_or_else(
130        || Symbol::new(name),
131        |(namespace, local)| Symbol::qualified(namespace, local),
132    )
133}