1use std::collections::BTreeSet;
4
5use sim_kernel::{
6 AbiVersion, Error, Export, Lib, LibManifest, LibTarget, Linker, LoadCx, Result, Symbol, Version,
7};
8
9use crate::{CitizenRuntime, parse_symbol};
10
11pub type InstallFn = for<'a> fn(&mut Linker<'a>) -> Result<()>;
13pub type ConformanceFn = fn(&mut sim_kernel::Cx) -> Result<()>;
15
16#[derive(Clone, Copy)]
22pub struct CitizenInfo {
23 pub symbol: &'static str,
25 pub version: u32,
27 pub crate_name: &'static str,
29 pub arity: usize,
31 pub install: InstallFn,
33 pub conformance: ConformanceFn,
35}
36
37inventory::collect!(CitizenInfo);
38
39#[derive(Clone, Copy)]
44pub struct NonCitizenInfo {
45 pub type_name: &'static str,
47 pub crate_name: &'static str,
49 pub reason: &'static str,
51 pub kind: &'static str,
53 pub descriptor: &'static str,
55}
56
57inventory::collect!(NonCitizenInfo);
58
59#[derive(Clone, Default)]
67pub struct CitizenRegistry {
68 citizens: Vec<CitizenInfo>,
69}
70
71impl CitizenRegistry {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn from_inventory() -> Result<Self> {
83 let mut registry = Self::new();
84 for info in registered_citizens() {
85 registry.register_info(*info)?;
86 }
87 Ok(registry)
88 }
89
90 pub fn register<T>(&mut self) -> Result<&mut Self>
96 where
97 T: CitizenRuntime,
98 {
99 self.register_info(T::citizen_info())
100 }
101
102 pub fn register_info(&mut self, info: CitizenInfo) -> Result<&mut Self> {
107 if self
108 .citizens
109 .iter()
110 .any(|existing| existing.symbol == info.symbol)
111 {
112 return Err(Error::Eval(format!(
113 "duplicate citizen registration for {}",
114 info.symbol
115 )));
116 }
117 self.citizens.push(info);
118 Ok(self)
119 }
120
121 pub fn citizens(&self) -> impl Iterator<Item = &CitizenInfo> {
123 self.citizens.iter()
124 }
125
126 pub fn len(&self) -> usize {
128 self.citizens.len()
129 }
130
131 pub fn is_empty(&self) -> bool {
133 self.citizens.is_empty()
134 }
135
136 pub fn missing_symbols<'a>(&self, expected: &'a [&'a str]) -> Vec<&'a str> {
138 let found = self
139 .citizens
140 .iter()
141 .map(|info| info.symbol)
142 .collect::<BTreeSet<_>>();
143 expected
144 .iter()
145 .copied()
146 .filter(|symbol| !found.contains(symbol))
147 .collect()
148 }
149
150 pub fn ensure_contains_symbols(&self, expected: &[&str]) -> Result<()> {
152 let missing = self.missing_symbols(expected);
153 if missing.is_empty() {
154 return Ok(());
155 }
156 Err(Error::HostError(format!(
157 "citizen registry incomplete: {} expected, {} registered; missing {:?}",
158 expected.len(),
159 self.len(),
160 missing
161 )))
162 }
163
164 pub fn install_all(&self, linker: &mut Linker<'_>) -> Result<()> {
166 for info in self.citizens() {
167 (info.install)(linker)?;
168 }
169 Ok(())
170 }
171
172 pub fn install_namespace(&self, linker: &mut Linker<'_>, namespace: &str) -> Result<()> {
174 for info in self
175 .citizens()
176 .filter(|info| symbol_namespace(info.symbol) == Some(namespace))
177 {
178 (info.install)(linker)?;
179 }
180 Ok(())
181 }
182}
183
184impl Lib for CitizenRegistry {
185 fn manifest(&self) -> LibManifest {
186 LibManifest {
187 id: Symbol::qualified("citizen", "explicit"),
188 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
189 abi: AbiVersion { major: 0, minor: 1 },
190 target: LibTarget::HostRegistered,
191 requires: Vec::new(),
192 capabilities: Vec::new(),
193 exports: self
194 .citizens()
195 .map(|info| Export::Class {
196 symbol: parse_symbol(info.symbol),
197 class_id: None,
198 })
199 .collect(),
200 }
201 }
202
203 fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
204 self.install_all(linker)
205 }
206}
207
208pub fn registered_citizens() -> impl Iterator<Item = &'static CitizenInfo> {
210 inventory::iter::<CitizenInfo>.into_iter()
211}
212
213pub fn registered_non_citizens() -> impl Iterator<Item = &'static NonCitizenInfo> {
215 inventory::iter::<NonCitizenInfo>.into_iter()
216}
217
218pub fn install_all(linker: &mut Linker<'_>) -> Result<()> {
220 for info in registered_citizens() {
221 (info.install)(linker)?;
222 }
223 Ok(())
224}
225
226pub fn install_namespace(linker: &mut Linker<'_>, namespace: &str) -> Result<()> {
228 for info in
229 registered_citizens().filter(|info| symbol_namespace(info.symbol) == Some(namespace))
230 {
231 (info.install)(linker)?;
232 }
233 Ok(())
234}
235
236#[derive(Clone, Copy, Debug, Default)]
242pub struct CitizenLib {
243 namespace: Option<&'static str>,
244}
245
246impl CitizenLib {
247 pub fn all() -> Self {
249 Self { namespace: None }
250 }
251
252 pub fn namespace(namespace: &'static str) -> Self {
254 Self {
255 namespace: Some(namespace),
256 }
257 }
258}
259
260impl Lib for CitizenLib {
261 fn manifest(&self) -> LibManifest {
262 let id = match self.namespace {
263 Some(namespace) => Symbol::qualified("citizen", namespace.to_owned()),
264 None => Symbol::qualified("citizen", "all"),
265 };
266 LibManifest {
267 id,
268 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
269 abi: AbiVersion { major: 0, minor: 1 },
270 target: LibTarget::HostRegistered,
271 requires: Vec::new(),
272 capabilities: Vec::new(),
273 exports: registered_citizens()
274 .filter(|info| match self.namespace {
275 Some(namespace) => symbol_namespace(info.symbol) == Some(namespace),
276 None => true,
277 })
278 .map(|info| Export::Class {
279 symbol: parse_symbol(info.symbol),
280 class_id: None,
281 })
282 .collect(),
283 }
284 }
285
286 fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
287 match self.namespace {
288 Some(namespace) => install_namespace(linker, namespace),
289 None => install_all(linker),
290 }
291 }
292}
293
294fn symbol_namespace(symbol: &str) -> Option<&str> {
295 symbol.split_once('/').map(|(namespace, _)| namespace)
296}