use sim_kernel::{
AbiVersion, Export, Lib, LibManifest, LibTarget, Linker, LoadCx, Result, Symbol, Version,
};
use crate::parse_symbol;
pub type InstallFn = for<'a> fn(&mut Linker<'a>) -> Result<()>;
pub type ConformanceFn = fn(&mut sim_kernel::Cx) -> Result<()>;
#[derive(Clone, Copy)]
pub struct CitizenInfo {
pub symbol: &'static str,
pub version: u32,
pub crate_name: &'static str,
pub arity: usize,
pub install: InstallFn,
pub conformance: ConformanceFn,
}
inventory::collect!(CitizenInfo);
pub fn registered_citizens() -> impl Iterator<Item = &'static CitizenInfo> {
inventory::iter::<CitizenInfo>.into_iter()
}
pub fn install_all(linker: &mut Linker<'_>) -> Result<()> {
for info in registered_citizens() {
(info.install)(linker)?;
}
Ok(())
}
pub fn install_namespace(linker: &mut Linker<'_>, namespace: &str) -> Result<()> {
for info in
registered_citizens().filter(|info| symbol_namespace(info.symbol) == Some(namespace))
{
(info.install)(linker)?;
}
Ok(())
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CitizenLib {
namespace: Option<&'static str>,
}
impl CitizenLib {
pub fn all() -> Self {
Self { namespace: None }
}
pub fn namespace(namespace: &'static str) -> Self {
Self {
namespace: Some(namespace),
}
}
}
impl Lib for CitizenLib {
fn manifest(&self) -> LibManifest {
let id = match self.namespace {
Some(namespace) => Symbol::qualified("citizen", namespace.to_owned()),
None => Symbol::qualified("citizen", "all"),
};
LibManifest {
id,
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::new(),
capabilities: Vec::new(),
exports: registered_citizens()
.filter(|info| match self.namespace {
Some(namespace) => symbol_namespace(info.symbol) == Some(namespace),
None => true,
})
.map(|info| Export::Class {
symbol: parse_symbol(info.symbol),
class_id: None,
})
.collect(),
}
}
fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
match self.namespace {
Some(namespace) => install_namespace(linker, namespace),
None => install_all(linker),
}
}
}
fn symbol_namespace(symbol: &str) -> Option<&str> {
symbol.split_once('/').map(|(namespace, _)| namespace)
}