valence_core/
trait_registry.rs1use 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}