Skip to main content

sim_citizen/
registry.rs

1//! Registry rows and library installation for registered citizens.
2
3use sim_kernel::{
4    AbiVersion, Export, Lib, LibManifest, LibTarget, Linker, LoadCx, Result, Symbol, Version,
5};
6
7use crate::parse_symbol;
8
9/// Installs a citizen's class into a kernel [`Linker`].
10pub type InstallFn = for<'a> fn(&mut Linker<'a>) -> Result<()>;
11/// Runs a citizen's conformance fixture against a kernel context.
12pub type ConformanceFn = fn(&mut sim_kernel::Cx) -> Result<()>;
13
14/// One inventory-registered citizen's static registry row.
15///
16/// Collected via `inventory` so every linked crate's citizens are discoverable
17/// without a central list. Carries identity metadata plus the install and
18/// conformance hooks driving registration and the strict gate.
19#[derive(Clone, Copy)]
20pub struct CitizenInfo {
21    /// The citizen's `namespace/name` symbol text.
22    pub symbol: &'static str,
23    /// The citizen's encoding version.
24    pub version: u32,
25    /// The crate that defined the citizen.
26    pub crate_name: &'static str,
27    /// Number of constructor fields (excluding the version argument).
28    pub arity: usize,
29    /// Hook that installs the citizen's class into a linker.
30    pub install: InstallFn,
31    /// Hook that runs the citizen's conformance fixture.
32    pub conformance: ConformanceFn,
33}
34
35inventory::collect!(CitizenInfo);
36
37/// Iterates every [`CitizenInfo`] registered through `inventory`.
38pub fn registered_citizens() -> impl Iterator<Item = &'static CitizenInfo> {
39    inventory::iter::<CitizenInfo>.into_iter()
40}
41
42/// Installs every registered citizen's class into `linker`.
43pub fn install_all(linker: &mut Linker<'_>) -> Result<()> {
44    for info in registered_citizens() {
45        (info.install)(linker)?;
46    }
47    Ok(())
48}
49
50/// Installs only the registered citizens whose symbol is in `namespace`.
51pub fn install_namespace(linker: &mut Linker<'_>, namespace: &str) -> Result<()> {
52    for info in
53        registered_citizens().filter(|info| symbol_namespace(info.symbol) == Some(namespace))
54    {
55        (info.install)(linker)?;
56    }
57    Ok(())
58}
59
60/// A kernel [`Lib`] that loads registered citizens into a context.
61///
62/// Either loads every registered citizen or only those in a chosen namespace.
63/// Its manifest exports one class per included citizen so the runtime can
64/// resolve them by symbol.
65#[derive(Clone, Copy, Debug, Default)]
66pub struct CitizenLib {
67    namespace: Option<&'static str>,
68}
69
70impl CitizenLib {
71    /// Returns a lib that loads every registered citizen.
72    pub fn all() -> Self {
73        Self { namespace: None }
74    }
75
76    /// Returns a lib that loads only citizens in `namespace`.
77    pub fn namespace(namespace: &'static str) -> Self {
78        Self {
79            namespace: Some(namespace),
80        }
81    }
82}
83
84impl Lib for CitizenLib {
85    fn manifest(&self) -> LibManifest {
86        let id = match self.namespace {
87            Some(namespace) => Symbol::qualified("citizen", namespace.to_owned()),
88            None => Symbol::qualified("citizen", "all"),
89        };
90        LibManifest {
91            id,
92            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
93            abi: AbiVersion { major: 0, minor: 1 },
94            target: LibTarget::HostRegistered,
95            requires: Vec::new(),
96            capabilities: Vec::new(),
97            exports: registered_citizens()
98                .filter(|info| match self.namespace {
99                    Some(namespace) => symbol_namespace(info.symbol) == Some(namespace),
100                    None => true,
101                })
102                .map(|info| Export::Class {
103                    symbol: parse_symbol(info.symbol),
104                    class_id: None,
105                })
106                .collect(),
107        }
108    }
109
110    fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
111        match self.namespace {
112            Some(namespace) => install_namespace(linker, namespace),
113            None => install_all(linker),
114        }
115    }
116}
117
118fn symbol_namespace(symbol: &str) -> Option<&str> {
119    symbol.split_once('/').map(|(namespace, _)| namespace)
120}