1use crate::model::{EnumVariant, TypeExpr, ValueType};
2use std::any::{TypeId, type_name};
3use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
4use std::sync::OnceLock;
5use std::sync::RwLock;
6
7#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16pub struct RegisteredType {
17 pub rust: String,
18 pub expr: TypeExpr,
19}
20
21struct TypeRegistry {
22 by_type_id: HashMap<TypeId, RegisteredType>,
23 by_rust_name: HashMap<String, TypeExpr>,
24}
25
26fn registry() -> &'static RwLock<TypeRegistry> {
27 static REGISTRY: OnceLock<RwLock<TypeRegistry>> = OnceLock::new();
28 REGISTRY.get_or_init(|| {
29 RwLock::new(TypeRegistry {
30 by_type_id: HashMap::new(),
31 by_rust_name: HashMap::new(),
32 })
33 })
34}
35
36#[derive(Clone, Debug)]
37struct CompatRegistry {
38 edges: BTreeMap<TypeExpr, BTreeSet<TypeExpr>>,
39 resolved: BTreeMap<(TypeExpr, TypeExpr), bool>,
40}
41
42fn compat_registry() -> &'static RwLock<CompatRegistry> {
43 static REGISTRY: OnceLock<RwLock<CompatRegistry>> = OnceLock::new();
44 REGISTRY.get_or_init(|| {
45 RwLock::new(CompatRegistry {
46 edges: BTreeMap::new(),
47 resolved: BTreeMap::new(),
48 })
49 })
50}
51
52fn normalize_rust_type_name(raw: &str) -> String {
53 raw.chars().filter(|c| !c.is_whitespace()).collect()
54}
55
56fn rust_type_key<T: 'static>() -> String {
57 normalize_rust_type_name(type_name::<T>())
58}
59
60pub fn register_type<T: 'static>(expr: TypeExpr) {
73 let rust = rust_type_key::<T>();
74 let expr = expr.normalize();
75
76 let mut guard = registry()
77 .write()
78 .expect("daedalus_data::typing registry lock poisoned");
79 guard.by_rust_name.insert(rust.clone(), expr.clone());
80 guard
81 .by_type_id
82 .insert(TypeId::of::<T>(), RegisteredType { rust, expr });
83}
84
85pub fn register_compatibility(from: TypeExpr, to: TypeExpr) {
103 let from = from.normalize();
104 let to = to.normalize();
105 let mut guard = compat_registry()
106 .write()
107 .expect("daedalus_data::typing compatibility registry lock poisoned");
108 guard.edges.entry(from).or_default().insert(to);
109 guard.resolved.clear();
110}
111
112pub fn can_convert_typeexpr(from: &TypeExpr, to: &TypeExpr) -> bool {
124 let from = from.clone().normalize();
125 let to = to.clone().normalize();
126 if from == to {
127 return true;
128 }
129 let mut guard = compat_registry()
130 .write()
131 .expect("daedalus_data::typing compatibility registry lock poisoned");
132 if let Some(cached) = guard.resolved.get(&(from.clone(), to.clone())) {
133 return *cached;
134 }
135 let mut queue: VecDeque<TypeExpr> = VecDeque::new();
136 let mut seen: BTreeSet<TypeExpr> = BTreeSet::new();
137 queue.push_back(from.clone());
138 seen.insert(from.clone());
139 let mut found = false;
140 while let Some(cur) = queue.pop_front() {
141 if cur == to {
142 found = true;
143 break;
144 }
145 if let Some(nexts) = guard.edges.get(&cur) {
146 for next in nexts {
147 if seen.insert(next.clone()) {
148 queue.push_back(next.clone());
149 }
150 }
151 }
152 }
153 guard.resolved.insert((from, to), found);
154 found
155}
156
157pub fn register_enum<T: 'static>(variants: impl IntoIterator<Item = impl Into<String>>) {
165 let variants = variants
166 .into_iter()
167 .map(|name| EnumVariant {
168 name: name.into(),
169 ty: None,
170 })
171 .collect();
172 register_type::<T>(TypeExpr::Enum(variants));
173}
174
175pub fn lookup_type<T: 'static>() -> Option<TypeExpr> {
185 let guard = registry()
186 .read()
187 .expect("daedalus_data::typing registry lock poisoned");
188 guard
189 .by_type_id
190 .get(&TypeId::of::<T>())
191 .map(|v| v.expr.clone())
192}
193
194fn builtin_type_expr<T: 'static>() -> Option<TypeExpr> {
195 let tid = TypeId::of::<T>();
196 let scalar = |v| TypeExpr::Scalar(v);
197
198 if tid == TypeId::of::<()>() {
199 return Some(scalar(ValueType::Unit));
200 }
201 if tid == TypeId::of::<bool>() {
202 return Some(scalar(ValueType::Bool));
203 }
204
205 if tid == TypeId::of::<i8>()
206 || tid == TypeId::of::<i16>()
207 || tid == TypeId::of::<i32>()
208 || tid == TypeId::of::<i64>()
209 || tid == TypeId::of::<i128>()
210 || tid == TypeId::of::<isize>()
211 || tid == TypeId::of::<u8>()
212 || tid == TypeId::of::<u16>()
213 || tid == TypeId::of::<u32>()
214 || tid == TypeId::of::<u64>()
215 || tid == TypeId::of::<u128>()
216 || tid == TypeId::of::<usize>()
217 {
218 return Some(scalar(ValueType::Int));
219 }
220
221 if tid == TypeId::of::<f32>() || tid == TypeId::of::<f64>() {
222 return Some(scalar(ValueType::Float));
223 }
224
225 if tid == TypeId::of::<String>() {
226 return Some(scalar(ValueType::String));
227 }
228
229 if tid == TypeId::of::<Vec<u8>>() {
230 return Some(scalar(ValueType::Bytes));
231 }
232
233 None
234}
235
236pub fn override_type_expr<T: 'static>() -> Option<TypeExpr> {
246 lookup_type::<T>().or_else(builtin_type_expr::<T>)
247}
248
249pub fn type_expr<T: 'static>() -> TypeExpr {
262 if let Some(expr) = override_type_expr::<T>() {
263 return expr;
264 }
265 TypeExpr::Opaque(format!("rust:{}", rust_type_key::<T>()))
266}
267
268pub fn lookup_type_by_rust_name(raw: &str) -> Option<TypeExpr> {
273 let key = normalize_rust_type_name(raw);
274 let guard = registry()
275 .read()
276 .expect("daedalus_data::typing registry lock poisoned");
277 guard.by_rust_name.get(&key).cloned()
278}
279
280pub fn snapshot_by_rust_name() -> Vec<RegisteredType> {
284 let guard = registry()
285 .read()
286 .expect("daedalus_data::typing registry lock poisoned");
287 let mut out: Vec<RegisteredType> = guard
288 .by_rust_name
289 .iter()
290 .map(|(rust, expr)| RegisteredType {
291 rust: rust.clone(),
292 expr: expr.clone(),
293 })
294 .collect();
295 out.sort_by(|a, b| a.rust.cmp(&b.rust));
296 out
297}