Skip to main content

valence_core/
trait_registry.rs

1//! Runtime trait metadata registry.
2
3use crate::trait_schema::{TraitDefinition, TraitDefinitionInit, TraitImplementor};
4use std::collections::HashMap;
5use std::sync::OnceLock;
6
7pub use crate::trait_schema::{TraitFieldDef, TraitPolicies, TraitPolicyRules};
8
9#[derive(Debug)]
10pub struct TraitRegistry {
11    inner: HashMap<String, &'static TraitDefinition>,
12    implementors: HashMap<String, Vec<String>>,
13}
14
15impl TraitRegistry {
16    pub fn new() -> Self {
17        Self {
18            inner: HashMap::new(),
19            implementors: HashMap::new(),
20        }
21    }
22
23    pub fn auto_discover() -> Self {
24        let mut registry = Self::new();
25        for init in inventory::iter::<TraitDefinitionInit> {
26            let def = (init.0)();
27            registry.inner.insert(def.name.to_string(), def);
28        }
29        for imp in inventory::iter::<TraitImplementor> {
30            registry
31                .implementors
32                .entry(imp.trait_name.to_string())
33                .or_default()
34                .push(imp.table_name.to_string());
35        }
36        registry
37    }
38
39    pub fn global() -> &'static TraitRegistry {
40        GLOBAL_TRAIT_REGISTRY.get_or_init(TraitRegistry::auto_discover)
41    }
42
43    pub fn set_global(registry: TraitRegistry) {
44        GLOBAL_TRAIT_REGISTRY
45            .set(registry)
46            .expect("TraitRegistry::set_global called more than once");
47    }
48
49    pub fn get_definition(&self, trait_name: &str) -> Option<&'static TraitDefinition> {
50        self.inner.get(trait_name).copied()
51    }
52
53    pub fn tables_for_trait(&self, trait_name: &str) -> Vec<&str> {
54        self.implementors
55            .get(trait_name)
56            .map(|v| v.iter().map(|s| s.as_str()).collect())
57            .unwrap_or_default()
58    }
59
60    pub fn list_traits(&self) -> Vec<&str> {
61        let mut keys: Vec<&str> = self.inner.keys().map(String::as_str).collect();
62        keys.sort_unstable();
63        keys
64    }
65
66    pub fn iter(&self) -> impl Iterator<Item = &'static TraitDefinition> + '_ {
67        self.inner.values().copied()
68    }
69}
70
71static GLOBAL_TRAIT_REGISTRY: OnceLock<TraitRegistry> = OnceLock::new();
72
73impl Default for TraitRegistry {
74    fn default() -> Self {
75        Self::new()
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn trait_tables() {
85        let mut registry = TraitRegistry::new();
86        let def: &'static TraitDefinition = Box::leak(Box::new(TraitDefinition {
87            name: "Named",
88            fields: &[],
89            connection_names: &[],
90            policies: None,
91        }));
92        registry.inner.insert(def.name.to_string(), def);
93        registry
94            .implementors
95            .entry("Named".into())
96            .or_default()
97            .push("user".into());
98        assert_eq!(registry.tables_for_trait("Named"), vec!["user"]);
99    }
100}