runmat_runtime/builtin/
registry.rs1use super::RuntimeBuiltinBinding;
2use std::collections::{BTreeMap, BTreeSet};
3
4#[cfg(not(target_arch = "wasm32"))]
5inventory::collect!(RuntimeBuiltinBinding);
6
7#[cfg(not(target_arch = "wasm32"))]
8pub fn runtime_builtin_bindings() -> Vec<RuntimeBuiltinBinding> {
9 inventory::iter::<RuntimeBuiltinBinding>()
10 .copied()
11 .collect()
12}
13
14#[cfg(target_arch = "wasm32")]
15pub mod wasm_registry {
16 use super::RuntimeBuiltinBinding;
17 use std::cell::RefCell;
18
19 thread_local! {
20 static BINDINGS: RefCell<Vec<&'static RuntimeBuiltinBinding>> = const { RefCell::new(Vec::new()) };
21 }
22
23 pub fn submit(binding: RuntimeBuiltinBinding) {
24 BINDINGS.with_borrow_mut(|bindings| bindings.push(Box::leak(Box::new(binding))));
25 }
26
27 pub fn bindings() -> Vec<&'static RuntimeBuiltinBinding> {
28 BINDINGS.with_borrow(Clone::clone)
29 }
30}
31
32#[cfg(target_arch = "wasm32")]
33pub fn runtime_builtin_bindings() -> Vec<RuntimeBuiltinBinding> {
34 wasm_registry::bindings().into_iter().copied().collect()
35}
36
37pub fn runtime_builtin_bindings_by_name(name: &str) -> Vec<RuntimeBuiltinBinding> {
38 runtime_builtin_bindings()
39 .into_iter()
40 .filter(|binding| binding.identity.builtin.name.eq_ignore_ascii_case(name))
41 .collect()
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct RuntimeBuiltinRegistryError {
46 pub identity: String,
47 pub message: String,
48}
49
50pub fn validate_runtime_builtin_registry() -> Vec<RuntimeBuiltinRegistryError> {
51 let catalog = runmat_builtins::builtin_catalog_entries();
52 let actual = runtime_builtin_bindings();
53 let mut errors = Vec::new();
54 let mut actual_counts = BTreeMap::new();
55 for binding in &actual {
56 *actual_counts.entry(binding.identity).or_insert(0usize) += 1;
57 }
58 for (identity, count) in &actual_counts {
59 if *count > 1 {
60 errors.push(registry_error(
61 identity,
62 "runtime binding identity is registered more than once",
63 ));
64 }
65 }
66
67 let mut declared = BTreeSet::new();
68 for entry in catalog {
69 if runmat_builtins::builtin_function_by_name(entry.identity.name).is_some() {
70 errors.push(RuntimeBuiltinRegistryError {
71 identity: entry.identity.name.to_string(),
72 message: "catalog identity also has a legacy BuiltinFunction authority".into(),
73 });
74 }
75 for binding in entry.bindings {
76 declared.insert(binding.identity);
77 let count = actual_counts.get(&binding.identity).copied().unwrap_or(0);
78 if matches!(
79 binding.availability,
80 runmat_builtins::BuiltinBindingAvailability::Required
81 ) && count != 1
82 {
83 errors.push(registry_error(
84 &binding.identity,
85 "required runtime binding is not registered exactly once",
86 ));
87 }
88 }
89 }
90 for binding in actual {
91 if !declared.contains(&binding.identity) {
92 errors.push(registry_error(
93 &binding.identity,
94 "runtime binding has no canonical catalog declaration",
95 ));
96 }
97 }
98 errors
99}
100
101fn registry_error(
102 identity: &runmat_builtins::BuiltinBindingIdentity,
103 message: impl Into<String>,
104) -> RuntimeBuiltinRegistryError {
105 RuntimeBuiltinRegistryError {
106 identity: format!("{}#{}", identity.builtin.name, identity.variant),
107 message: message.into(),
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn canonical_catalog_and_runtime_bindings_join_one_to_one() {
117 let errors = validate_runtime_builtin_registry();
118 assert!(errors.is_empty(), "{errors:#?}");
119 assert_eq!(runtime_builtin_bindings_by_name("full").len(), 1);
120 assert!(runmat_builtins::builtin_function_by_name("full").is_none());
121 }
122}