use crate::model::{EnumVariant, TypeExpr, ValueType};
use std::any::{TypeId, type_name};
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
use std::sync::OnceLock;
use std::sync::RwLock;
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct RegisteredType {
pub rust: String,
pub expr: TypeExpr,
}
struct TypeRegistry {
by_type_id: HashMap<TypeId, RegisteredType>,
by_rust_name: HashMap<String, TypeExpr>,
}
fn registry() -> &'static RwLock<TypeRegistry> {
static REGISTRY: OnceLock<RwLock<TypeRegistry>> = OnceLock::new();
REGISTRY.get_or_init(|| {
RwLock::new(TypeRegistry {
by_type_id: HashMap::new(),
by_rust_name: HashMap::new(),
})
})
}
#[derive(Clone, Debug)]
struct CompatRegistry {
edges: BTreeMap<TypeExpr, BTreeSet<TypeExpr>>,
resolved: BTreeMap<(TypeExpr, TypeExpr), bool>,
}
fn compat_registry() -> &'static RwLock<CompatRegistry> {
static REGISTRY: OnceLock<RwLock<CompatRegistry>> = OnceLock::new();
REGISTRY.get_or_init(|| {
RwLock::new(CompatRegistry {
edges: BTreeMap::new(),
resolved: BTreeMap::new(),
})
})
}
fn normalize_rust_type_name(raw: &str) -> String {
raw.chars().filter(|c| !c.is_whitespace()).collect()
}
fn rust_type_key<T: 'static>() -> String {
normalize_rust_type_name(type_name::<T>())
}
pub fn register_type<T: 'static>(expr: TypeExpr) {
let rust = rust_type_key::<T>();
let expr = expr.normalize();
let mut guard = registry()
.write()
.expect("daedalus_data::typing registry lock poisoned");
guard.by_rust_name.insert(rust.clone(), expr.clone());
guard
.by_type_id
.insert(TypeId::of::<T>(), RegisteredType { rust, expr });
}
pub fn register_compatibility(from: TypeExpr, to: TypeExpr) {
let from = from.normalize();
let to = to.normalize();
let mut guard = compat_registry()
.write()
.expect("daedalus_data::typing compatibility registry lock poisoned");
guard.edges.entry(from).or_default().insert(to);
guard.resolved.clear();
}
pub fn can_convert_typeexpr(from: &TypeExpr, to: &TypeExpr) -> bool {
let from = from.clone().normalize();
let to = to.clone().normalize();
if from == to {
return true;
}
let mut guard = compat_registry()
.write()
.expect("daedalus_data::typing compatibility registry lock poisoned");
if let Some(cached) = guard.resolved.get(&(from.clone(), to.clone())) {
return *cached;
}
let mut queue: VecDeque<TypeExpr> = VecDeque::new();
let mut seen: BTreeSet<TypeExpr> = BTreeSet::new();
queue.push_back(from.clone());
seen.insert(from.clone());
let mut found = false;
while let Some(cur) = queue.pop_front() {
if cur == to {
found = true;
break;
}
if let Some(nexts) = guard.edges.get(&cur) {
for next in nexts {
if seen.insert(next.clone()) {
queue.push_back(next.clone());
}
}
}
}
guard.resolved.insert((from, to), found);
found
}
pub fn register_enum<T: 'static>(variants: impl IntoIterator<Item = impl Into<String>>) {
let variants = variants
.into_iter()
.map(|name| EnumVariant {
name: name.into(),
ty: None,
})
.collect();
register_type::<T>(TypeExpr::Enum(variants));
}
pub fn lookup_type<T: 'static>() -> Option<TypeExpr> {
let guard = registry()
.read()
.expect("daedalus_data::typing registry lock poisoned");
guard
.by_type_id
.get(&TypeId::of::<T>())
.map(|v| v.expr.clone())
}
fn builtin_type_expr<T: 'static>() -> Option<TypeExpr> {
let tid = TypeId::of::<T>();
let scalar = |v| TypeExpr::Scalar(v);
if tid == TypeId::of::<()>() {
return Some(scalar(ValueType::Unit));
}
if tid == TypeId::of::<bool>() {
return Some(scalar(ValueType::Bool));
}
if tid == TypeId::of::<i8>()
|| tid == TypeId::of::<i16>()
|| tid == TypeId::of::<i32>()
|| tid == TypeId::of::<i64>()
|| tid == TypeId::of::<i128>()
|| tid == TypeId::of::<isize>()
|| tid == TypeId::of::<u8>()
|| tid == TypeId::of::<u16>()
|| tid == TypeId::of::<u32>()
|| tid == TypeId::of::<u64>()
|| tid == TypeId::of::<u128>()
|| tid == TypeId::of::<usize>()
{
return Some(scalar(ValueType::Int));
}
if tid == TypeId::of::<f32>() || tid == TypeId::of::<f64>() {
return Some(scalar(ValueType::Float));
}
if tid == TypeId::of::<String>() {
return Some(scalar(ValueType::String));
}
if tid == TypeId::of::<Vec<u8>>() {
return Some(scalar(ValueType::Bytes));
}
None
}
pub fn override_type_expr<T: 'static>() -> Option<TypeExpr> {
lookup_type::<T>().or_else(builtin_type_expr::<T>)
}
pub fn type_expr<T: 'static>() -> TypeExpr {
if let Some(expr) = override_type_expr::<T>() {
return expr;
}
TypeExpr::Opaque(format!("rust:{}", rust_type_key::<T>()))
}
pub fn lookup_type_by_rust_name(raw: &str) -> Option<TypeExpr> {
let key = normalize_rust_type_name(raw);
let guard = registry()
.read()
.expect("daedalus_data::typing registry lock poisoned");
guard.by_rust_name.get(&key).cloned()
}
pub fn snapshot_by_rust_name() -> Vec<RegisteredType> {
let guard = registry()
.read()
.expect("daedalus_data::typing registry lock poisoned");
let mut out: Vec<RegisteredType> = guard
.by_rust_name
.iter()
.map(|(rust, expr)| RegisteredType {
rust: rust.clone(),
expr: expr.clone(),
})
.collect();
out.sort_by(|a, b| a.rust.cmp(&b.rust));
out
}