use std::collections::BTreeSet;
use sim_kernel::{
AbiVersion, Error, Export, Lib, LibManifest, LibTarget, Linker, LoadCx, Result, Symbol, Version,
};
use crate::{CitizenRuntime, 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);
#[derive(Clone, Copy)]
pub struct NonCitizenInfo {
pub type_name: &'static str,
pub crate_name: &'static str,
pub reason: &'static str,
pub kind: &'static str,
pub descriptor: &'static str,
}
inventory::collect!(NonCitizenInfo);
#[derive(Clone, Default)]
pub struct CitizenRegistry {
citizens: Vec<CitizenInfo>,
}
impl CitizenRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn from_inventory() -> Result<Self> {
let mut registry = Self::new();
for info in registered_citizens() {
registry.register_info(*info)?;
}
Ok(registry)
}
pub fn register<T>(&mut self) -> Result<&mut Self>
where
T: CitizenRuntime,
{
self.register_info(T::citizen_info())
}
pub fn register_info(&mut self, info: CitizenInfo) -> Result<&mut Self> {
if self
.citizens
.iter()
.any(|existing| existing.symbol == info.symbol)
{
return Err(Error::Eval(format!(
"duplicate citizen registration for {}",
info.symbol
)));
}
self.citizens.push(info);
Ok(self)
}
pub fn citizens(&self) -> impl Iterator<Item = &CitizenInfo> {
self.citizens.iter()
}
pub fn len(&self) -> usize {
self.citizens.len()
}
pub fn is_empty(&self) -> bool {
self.citizens.is_empty()
}
pub fn missing_symbols<'a>(&self, expected: &'a [&'a str]) -> Vec<&'a str> {
let found = self
.citizens
.iter()
.map(|info| info.symbol)
.collect::<BTreeSet<_>>();
expected
.iter()
.copied()
.filter(|symbol| !found.contains(symbol))
.collect()
}
pub fn ensure_contains_symbols(&self, expected: &[&str]) -> Result<()> {
let missing = self.missing_symbols(expected);
if missing.is_empty() {
return Ok(());
}
Err(Error::HostError(format!(
"citizen registry incomplete: {} expected, {} registered; missing {:?}",
expected.len(),
self.len(),
missing
)))
}
pub fn install_all(&self, linker: &mut Linker<'_>) -> Result<()> {
for info in self.citizens() {
(info.install)(linker)?;
}
Ok(())
}
pub fn install_namespace(&self, linker: &mut Linker<'_>, namespace: &str) -> Result<()> {
for info in self
.citizens()
.filter(|info| symbol_namespace(info.symbol) == Some(namespace))
{
(info.install)(linker)?;
}
Ok(())
}
}
impl Lib for CitizenRegistry {
fn manifest(&self) -> LibManifest {
LibManifest {
id: Symbol::qualified("citizen", "explicit"),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::new(),
capabilities: Vec::new(),
exports: self
.citizens()
.map(|info| Export::Class {
symbol: parse_symbol(info.symbol),
class_id: None,
})
.collect(),
}
}
fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
self.install_all(linker)
}
}
pub fn registered_citizens() -> impl Iterator<Item = &'static CitizenInfo> {
inventory::iter::<CitizenInfo>.into_iter()
}
pub fn registered_non_citizens() -> impl Iterator<Item = &'static NonCitizenInfo> {
inventory::iter::<NonCitizenInfo>.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)
}