1use std::any::{Any, TypeId};
27use std::collections::HashMap;
28
29use adam_rs::{CellId, ConditionalId, MatchExpr, RelationshipId, Sheet};
30use cel_parser::{ResolvedLeafType, ResolvedType, TypeResolver};
31use cel_runtime::DynSegment;
32
33#[derive(Clone, PartialEq, Eq, Hash, Debug)]
48pub enum TypeShape {
49 Named(TypeId),
51 Tuple(Vec<TypeShape>),
53 Array(Box<TypeShape>),
55 Callable(Vec<TypeShape>, Box<TypeShape>),
57}
58
59pub type PushArgFn = fn(&mut DynSegment, usize);
68
69pub type AddCellFn = fn(&mut Sheet, Box<dyn Any>) -> CellId;
78
79pub type AddSourceFn = fn(&mut Sheet, Box<dyn Any>) -> CellId;
89
90pub type CallDynFn = fn(&mut DynSegment, &[&dyn Any]) -> anyhow::Result<Box<dyn Any>>;
102
103pub type AddConditionalFn = fn(
117 &mut Sheet,
118 MatchExpr,
119 Vec<(Box<dyn Any>, Vec<RelationshipId>)>,
120 Vec<RelationshipId>,
121) -> Result<ConditionalId, adam_rs::Error>;
122
123#[allow(clippy::type_complexity)] #[derive(Clone)]
128pub(crate) struct RangeEntry {
129 pub(crate) element_type_id: TypeId,
131 pub(crate) clamp_fn: fn(cel_runtime::OwnedValue, &dyn Any) -> anyhow::Result<Box<dyn Any>>,
136 pub(crate) bounds_fn:
140 fn(cel_runtime::OwnedValue) -> anyhow::Result<(Box<dyn Any>, Box<dyn Any>)>,
141}
142
143#[derive(Clone)]
167pub struct TypeEntry {
168 pub(crate) template: crate::composition::LeafAdapters,
170 pub(crate) box_owned: unsafe fn(cel_runtime::OwnedValue) -> Box<dyn Any>,
172 pub type_id: TypeId,
174 pub type_name: &'static str,
176 pub push_arg_fn: PushArgFn,
178 pub add_cell_fn: AddCellFn,
180 pub add_source_fn: AddSourceFn,
183 pub call_dyn_fn: CallDynFn,
185 pub eq_dyn_fn: fn(&dyn Any, &dyn Any) -> bool,
187 pub default_fn: Option<fn() -> Box<dyn Any>>,
189 pub add_conditional_fn: AddConditionalFn,
191 pub value_type_fn: fn() -> cel_runtime::value_type::ValueType,
193}
194
195#[derive(Clone)]
211pub struct TypeRegistry {
212 by_name: HashMap<String, TypeEntry>,
213 by_type_id: HashMap<TypeId, String>,
214 range_inclusive: HashMap<TypeId, RangeEntry>,
215}
216
217fn push_arg_impl<T: 'static + Clone>(segment: &mut DynSegment, index: usize) {
221 segment.push_arg::<T>(index);
222}
223
224unsafe fn box_owned_impl<T: 'static>(value: cel_runtime::OwnedValue) -> Box<dyn Any> {
229 Box::new(unsafe { value.into_typed_bound::<cel_runtime::Leaf<T>>() })
230}
231
232fn add_conditional_impl<T: Any + PartialEq + 'static>(
236 sheet: &mut Sheet,
237 source: MatchExpr,
238 branches: Vec<(Box<dyn Any>, Vec<RelationshipId>)>,
239 default: Vec<RelationshipId>,
240) -> Result<ConditionalId, adam_rs::Error> {
241 debug_assert!(branches.iter().all(|(value, _)| value.is::<T>()));
242 let typed_branches: Vec<(Vec<T>, Vec<RelationshipId>)> = branches
243 .into_iter()
244 .map(|(val, rel_ids)| {
245 let v = *val
246 .downcast::<T>()
247 .expect("add_conditional_impl: type matches registration");
248 (vec![v], rel_ids)
249 })
250 .collect();
251 sheet.add_conditional::<T>(source, typed_branches, default)
252}
253
254fn add_cell_impl<T: Any + PartialEq + 'static>(sheet: &mut Sheet, value: Box<dyn Any>) -> CellId {
258 debug_assert!(value.is::<T>());
259 let v = value
260 .downcast::<T>()
261 .expect("add_cell_impl: type matches registration");
262 sheet.add_cell(*v)
263}
264
265fn add_source_impl<T: Any + PartialEq + 'static>(sheet: &mut Sheet, value: Box<dyn Any>) -> CellId {
270 debug_assert!(value.is::<T>());
271 let v = value
272 .downcast::<T>()
273 .expect("add_source_impl: type matches registration");
274 sheet.add_source(*v)
275}
276
277fn eq_dyn_impl<T: PartialEq + 'static>(a: &dyn Any, b: &dyn Any) -> bool {
281 match (a.downcast_ref::<T>(), b.downcast_ref::<T>()) {
282 (Some(a), Some(b)) => a == b,
283 _ => false,
284 }
285}
286
287fn call_dyn_impl<T: 'static + Clone>(
295 seg: &mut DynSegment,
296 inputs: &[&dyn Any],
297) -> anyhow::Result<Box<dyn Any>> {
298 Ok(Box::new(seg.call_dyn::<T>(inputs)?))
299}
300
301fn value_type_impl<T: Clone + PartialEq + std::fmt::Debug + 'static>()
303-> cel_runtime::value_type::ValueType {
304 use cel_runtime::value_type::{Leaf, ValueType};
305 ValueType::cloneable_leaf::<T>()
306 .with_equality::<Leaf<T>>()
307 .expect("same registered leaf")
308 .with_debug::<Leaf<T>>()
309 .expect("same registered leaf")
310}
311
312fn range_clamp_impl<T: Clone + PartialOrd + 'static>(
317 result: cel_runtime::OwnedValue,
318 value: &dyn Any,
319) -> anyhow::Result<Box<dyn Any>> {
320 debug_assert!(value.is::<T>());
321 let range =
323 unsafe { result.into_typed_bound::<cel_runtime::Leaf<std::ops::RangeInclusive<T>>>() };
324 let v = unsafe { &*std::ptr::from_ref(value).cast::<T>() };
325 let clamped = if v < range.start() {
326 range.start().clone()
327 } else if v > range.end() {
328 range.end().clone()
329 } else {
330 v.clone()
331 };
332 Ok(Box::new(clamped) as Box<dyn Any>)
333}
334
335fn range_bounds_impl<T: Clone + 'static>(
340 result: cel_runtime::OwnedValue,
341) -> anyhow::Result<(Box<dyn Any>, Box<dyn Any>)> {
342 let range =
344 unsafe { result.into_typed_bound::<cel_runtime::Leaf<std::ops::RangeInclusive<T>>>() };
345 Ok((
346 Box::new(range.start().clone()) as Box<dyn Any>,
347 Box::new(range.end().clone()) as Box<dyn Any>,
348 ))
349}
350
351impl TypeRegistry {
352 pub(crate) fn resolved_leaf_type(&self, type_id: TypeId) -> Option<ResolvedLeafType> {
356 let name = self.by_type_id.get(&type_id)?;
357 let entry = self.by_name.get(name)?;
358 Some(ResolvedLeafType::new(name.clone(), (entry.value_type_fn)()))
359 }
360
361 #[must_use]
379 pub fn cel_type_resolver(&self) -> RegistryTypeResolver {
380 let by_name = self
381 .by_name
382 .iter()
383 .map(|(name, entry)| {
384 (
385 name.clone(),
386 ResolvedLeafType::new(name.clone(), (entry.value_type_fn)()),
387 )
388 })
389 .collect();
390 RegistryTypeResolver { by_name }
391 }
392
393 #[must_use]
405 pub fn new() -> Self {
406 let mut r = TypeRegistry {
407 by_name: HashMap::new(),
408 by_type_id: HashMap::new(),
409 range_inclusive: HashMap::new(),
410 };
411 r.register::<i8>("i8");
412 r.register::<i16>("i16");
413 r.register::<i32>("i32");
414 r.register::<i64>("i64");
415 r.register::<i128>("i128");
416 r.register::<isize>("isize");
417 r.register::<u8>("u8");
418 r.register::<u16>("u16");
419 r.register::<u32>("u32");
420 r.register::<u64>("u64");
421 r.register::<u128>("u128");
422 r.register::<usize>("usize");
423 r.register::<f32>("f32");
424 r.register::<f64>("f64");
425 r.register::<bool>("bool");
426 r.register::<String>("String");
427 r.register_range_inclusive::<i8>();
428 r.register_range_inclusive::<i16>();
429 r.register_range_inclusive::<i32>();
430 r.register_range_inclusive::<i64>();
431 r.register_range_inclusive::<i128>();
432 r.register_range_inclusive::<isize>();
433 r.register_range_inclusive::<u8>();
434 r.register_range_inclusive::<u16>();
435 r.register_range_inclusive::<u32>();
436 r.register_range_inclusive::<u64>();
437 r.register_range_inclusive::<u128>();
438 r.register_range_inclusive::<usize>();
439 r.register_range_inclusive::<f32>();
440 r.register_range_inclusive::<f64>();
441 r
442 }
443
444 pub fn register<T: Any + PartialEq + Default + Clone + std::fmt::Debug + 'static>(
460 &mut self,
461 name: &str,
462 ) {
463 let type_id = TypeId::of::<T>();
464 self.remove_registration_name(name);
465 self.by_name.insert(
466 name.to_owned(),
467 TypeEntry {
468 template: crate::composition::LeafAdapters::of::<T>(),
469 box_owned: box_owned_impl::<T>,
470 type_id,
471 type_name: std::any::type_name::<T>(),
472 push_arg_fn: push_arg_impl::<T>,
473 add_cell_fn: add_cell_impl::<T>,
474 add_source_fn: add_source_impl::<T>,
475 call_dyn_fn: call_dyn_impl::<T>,
476 eq_dyn_fn: eq_dyn_impl::<T>,
477 default_fn: Some(|| Box::new(T::default()) as Box<dyn Any>),
478 add_conditional_fn: add_conditional_impl::<T>,
479 value_type_fn: value_type_impl::<T>,
480 },
481 );
482 self.by_type_id.insert(type_id, name.to_owned());
483 }
484
485 pub fn register_no_default<T: Any + PartialEq + Clone + std::fmt::Debug + 'static>(
506 &mut self,
507 name: &str,
508 ) {
509 let type_id = TypeId::of::<T>();
510 self.remove_registration_name(name);
511 self.by_name.insert(
512 name.to_owned(),
513 TypeEntry {
514 template: crate::composition::LeafAdapters::of::<T>(),
515 box_owned: box_owned_impl::<T>,
516 type_id,
517 type_name: std::any::type_name::<T>(),
518 push_arg_fn: push_arg_impl::<T>,
519 add_cell_fn: add_cell_impl::<T>,
520 add_source_fn: add_source_impl::<T>,
521 call_dyn_fn: call_dyn_impl::<T>,
522 eq_dyn_fn: eq_dyn_impl::<T>,
523 default_fn: None,
524 add_conditional_fn: add_conditional_impl::<T>,
525 value_type_fn: value_type_impl::<T>,
526 },
527 );
528 self.by_type_id.insert(type_id, name.to_owned());
529 }
530
531 fn remove_registration_name(&mut self, name: &str) {
536 let Some(old) = self.by_name.remove(name) else {
537 return;
538 };
539 let old_id = old.type_id;
540 if self
541 .by_type_id
542 .get(&old_id)
543 .is_none_or(|canonical| canonical != name)
544 {
545 return;
546 }
547 let remaining = self
548 .by_name
549 .iter()
550 .find(|(_, entry)| entry.type_id == old_id)
551 .map(|(alias, _)| alias.clone());
552 if let Some(alias) = remaining {
553 self.by_type_id.insert(old_id, alias);
554 } else {
555 self.by_type_id.remove(&old_id);
556 }
557 }
558
559 #[must_use]
574 pub fn get(&self, name: &str) -> Option<&TypeEntry> {
575 self.by_name.get(name)
576 }
577
578 #[must_use]
594 pub fn entry_by_type_id(&self, type_id: TypeId) -> Option<&TypeEntry> {
595 let name = self.by_type_id.get(&type_id)?;
596 self.by_name.get(name)
597 }
598
599 fn register_range_inclusive<T: Clone + PartialOrd + 'static>(&mut self) {
606 self.range_inclusive.insert(
607 TypeId::of::<std::ops::RangeInclusive<T>>(),
608 RangeEntry {
609 element_type_id: TypeId::of::<T>(),
610 clamp_fn: range_clamp_impl::<T>,
611 bounds_fn: range_bounds_impl::<T>,
612 },
613 );
614 }
615
616 #[allow(dead_code)] pub(crate) fn range_entry(&self, range_type_id: TypeId) -> Option<&RangeEntry> {
622 self.range_inclusive.get(&range_type_id)
623 }
624
625 pub fn resolve(
641 &self,
642 expr: &cel_parser::TypeExpr,
643 ) -> std::result::Result<TypeShape, (String, proc_macro2::Span)> {
644 let resolved = expr
645 .resolve(self)
646 .map_err(|error| (error.message().to_string(), error.span()))?;
647 self.shape_of(
648 &resolved
649 .into_value_type()
650 .map_err(|error| (error.to_string(), expr.span().start))?,
651 )
652 .map_err(|error| (error, expr.span().start))
653 }
654
655 #[must_use]
669 pub fn registered_name(&self, type_id: TypeId) -> Option<&str> {
670 self.by_type_id.get(&type_id).map(String::as_str)
671 }
672
673 #[must_use]
686 pub fn display_name(&self, shape: &TypeShape) -> String {
687 let mut output = String::new();
688 self.write_shape_name(&mut output, shape);
689 output
690 }
691
692 fn write_shape_name(&self, output: &mut String, shape: &TypeShape) {
697 match shape {
698 TypeShape::Named(type_id) => {
699 output.push_str(self.registered_name(*type_id).unwrap_or("?"));
700 }
701 TypeShape::Tuple(elements) => self.write_tuple_name(output, elements),
702 TypeShape::Array(element) => {
703 output.push('[');
704 self.write_shape_name(output, element);
705 output.push(']');
706 }
707 TypeShape::Callable(parameters, result) => {
708 output.push_str("Fn(");
709 self.write_tuple_name(output, parameters);
710 output.push_str(", ");
711 self.write_shape_name(output, result);
712 output.push(')');
713 }
714 }
715 }
716
717 fn write_tuple_name(&self, output: &mut String, elements: &[TypeShape]) {
722 output.push('(');
723 for (index, element) in elements.iter().enumerate() {
725 if index != 0 {
726 output.push_str(", ");
727 }
728 self.write_shape_name(output, element);
729 }
730 if elements.len() == 1 {
731 output.push(',');
732 }
733 output.push(')');
734 }
735
736 fn one_associated(&self, shape: &TypeShape) -> cel_runtime::AssociatedType {
746 let value_type = match shape {
747 TypeShape::Named(type_id) => {
748 let entry = self
749 .entry_by_type_id(*type_id)
750 .expect("one_associated: type registered");
751 (entry.value_type_fn)()
752 }
753 TypeShape::Tuple(elements) => cel_runtime::ValueType::tuple(
754 elements.iter().map(|e| self.one_associated(e)).collect(),
755 )
756 .expect("registered tuple layout"),
757 TypeShape::Array(element) => cel_runtime::ValueType::array(self.value_type(element)),
758 TypeShape::Callable(parameters, result) => cel_runtime::ValueType::callable(
759 parameters.iter().map(|p| self.value_type(p)).collect(),
760 self.value_type(result),
761 ),
762 };
763 cel_runtime::AssociatedType {
764 offset: 0,
765 value_type,
766 }
767 }
768
769 pub(crate) fn value_type(&self, shape: &TypeShape) -> cel_runtime::ValueType {
774 self.one_associated(shape).value_type
775 }
776
777 pub fn validate_shape(&self, shape: &TypeShape) -> anyhow::Result<()> {
793 self.checked_value_type(shape).map(|_| ())
794 }
795
796 pub(crate) fn checked_value_type(
802 &self,
803 shape: &TypeShape,
804 ) -> anyhow::Result<cel_runtime::ValueType> {
805 use cel_runtime::{AssociatedType, ValueType};
806 Ok(match shape {
807 TypeShape::Named(id) => {
808 let entry = self
809 .entry_by_type_id(*id)
810 .ok_or_else(|| anyhow::anyhow!("unregistered shape leaf {id:?}"))?;
811 (entry.value_type_fn)()
812 }
813 TypeShape::Tuple(fields) => ValueType::tuple(
814 fields
815 .iter()
816 .map(|field| {
817 Ok(AssociatedType {
818 offset: 0,
819 value_type: self.checked_value_type(field)?,
820 })
821 })
822 .collect::<anyhow::Result<_>>()?,
823 )?,
824 TypeShape::Array(element) => ValueType::array(self.checked_value_type(element)?),
825 TypeShape::Callable(parameters, result) => ValueType::callable(
826 parameters
827 .iter()
828 .map(|p| self.checked_value_type(p))
829 .collect::<anyhow::Result<_>>()?,
830 self.checked_value_type(result)?,
831 ),
832 })
833 }
834
835 pub(crate) fn shape_of(&self, ty: &cel_runtime::ValueType) -> Result<TypeShape, String> {
841 use cel_runtime::ValueKind;
842 Ok(match ty.kind() {
843 ValueKind::Leaf(id) => {
844 if self.entry_by_type_id(*id).is_none() {
845 return Err(format!(
846 "cannot infer a type: unregistered type `{}`",
847 ty.type_name()
848 ));
849 }
850 TypeShape::Named(*id)
851 }
852 ValueKind::Array(element) => TypeShape::Array(Box::new(self.shape_of(element)?)),
853 ValueKind::Tuple(fields) => TypeShape::Tuple(
854 fields
855 .iter()
856 .map(|field| self.shape_of(&field.value_type))
857 .collect::<Result<_, _>>()?,
858 ),
859 ValueKind::Callable { parameters, result } => TypeShape::Callable(
860 parameters
861 .iter()
862 .map(|p| self.shape_of(p))
863 .collect::<Result<_, _>>()?,
864 Box::new(self.shape_of(result)?),
865 ),
866 })
867 }
868
869 pub(crate) fn validate_default(&self, shape: &TypeShape) -> anyhow::Result<()> {
876 match shape {
877 TypeShape::Named(id) => {
878 anyhow::ensure!(
879 self.entry_by_type_id(*id)
880 .expect("resolved leaf")
881 .default_fn
882 .is_some(),
883 "type `{}` has no default; provide `= ...`",
884 self.display_name(shape)
885 );
886 }
887 TypeShape::Tuple(fields) => {
888 for field in fields {
889 self.validate_default(field)?;
890 }
891 }
892 TypeShape::Array(_) => {}
893 TypeShape::Callable(_, _) => {
894 anyhow::bail!("callable has no default; provide an initializer `= ...`")
895 }
896 }
897 Ok(())
898 }
899
900 pub(crate) fn emit_default(
909 &self,
910 shape: &TypeShape,
911 body: &mut DynSegment,
912 ) -> anyhow::Result<()> {
913 let origin = body.current_stack_offset();
914 match shape {
915 TypeShape::Named(id) => {
916 let entry = self.entry_by_type_id(*id).expect("resolved leaf");
917 let default = entry.default_fn.ok_or_else(|| {
918 anyhow::anyhow!(
919 "type `{}` has no default; provide `= ...`",
920 self.display_name(shape)
921 )
922 })?;
923 body.just_external(self.value_type(shape), default())?;
924 }
925 TypeShape::Tuple(fields) => {
926 for field in fields {
927 self.emit_default(field, body)?;
928 }
929 body.make_tuple(fields.len(), origin)?;
930 }
931 TypeShape::Array(element) => {
932 body.make_typed_array(0, origin, self.value_type(element))?
933 }
934 TypeShape::Callable(_, _) => {
935 anyhow::bail!("callable has no default; provide an initializer `= ...`")
936 }
937 }
938 Ok(())
939 }
940}
941
942#[derive(Clone, Default)]
950pub struct RegistryTypeResolver {
951 by_name: HashMap<String, ResolvedLeafType>,
952}
953
954impl TypeResolver for RegistryTypeResolver {
955 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
959 let key = named_key(name, args);
960 if let Some(leaf) = self.by_name.get(key.as_ref()).cloned() {
961 return Some(leaf);
962 }
963 cel_parser::resolve_builtin_named_type(name, args)
964 }
965}
966
967impl TypeResolver for TypeRegistry {
968 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
972 let key = named_key(name, args);
973 self.get(key.as_ref())
974 .map(|entry| ResolvedLeafType::new(key.into_owned(), (entry.value_type_fn)()))
975 .or_else(|| cel_parser::resolve_builtin_named_type(name, args))
976 }
977}
978
979fn named_key<'a>(name: &'a str, args: &[ResolvedType]) -> std::borrow::Cow<'a, str> {
983 if args.is_empty() {
984 std::borrow::Cow::Borrowed(name)
985 } else {
986 let mut key = String::from(name);
987 key.push('(');
988 for (index, argument) in args.iter().enumerate() {
989 if index != 0 {
990 key.push_str(", ");
991 }
992 key.push_str(&argument.display_name());
993 }
994 key.push(')');
995 std::borrow::Cow::Owned(key)
996 }
997}
998
999impl Default for TypeRegistry {
1000 fn default() -> Self {
1002 Self::new()
1003 }
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008 use super::*;
1009 use proc_macro2::Span;
1010 use std::any::TypeId;
1011
1012 #[test]
1014 fn display_name_covers_recursive_shape_variants() {
1015 let integer = TypeShape::Named(TypeId::of::<i32>());
1016 let cases = [
1017 (TypeShape::Tuple(vec![]), "()"),
1018 (TypeShape::Tuple(vec![integer.clone()]), "(i32,)"),
1019 (
1020 TypeShape::Array(Box::new(TypeShape::Tuple(vec![integer.clone()]))),
1021 "[(i32,)]",
1022 ),
1023 (
1024 TypeShape::Callable(vec![integer], Box::new(TypeShape::Tuple(vec![]))),
1025 "Fn((i32,), ())",
1026 ),
1027 (TypeShape::Named(TypeId::of::<Vec<i32>>()), "?"),
1028 ];
1029 let registry = TypeRegistry::new();
1030 for (shape, expected) in cases {
1031 assert_eq!(registry.display_name(&shape), expected);
1032 }
1033 }
1034
1035 fn point(span: Span) -> crate::ast::ExprSpan {
1037 crate::ast::ExprSpan {
1038 start: span,
1039 end: span,
1040 }
1041 }
1042
1043 #[test]
1045 fn new_registers_builtin_i32() {
1046 let reg = TypeRegistry::new();
1047 let entry = reg.get("i32").expect("i32 registered");
1048 assert_eq!(entry.type_id, TypeId::of::<i32>());
1049 }
1050
1051 #[test]
1053 fn complete_factory_registered_foreign_leaf_retains_capabilities() {
1054 use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
1055 let mut registry = TypeRegistry::new();
1056 registry.register::<std::path::PathBuf>("Path");
1057 registry.register_no_default::<std::ops::RangeInclusive<i32>>("Bounds");
1058 let entry = registry.get("Path").unwrap();
1059 let descriptor = (entry.value_type_fn)();
1060 let owner = OwnedValue::new::<Leaf<std::path::PathBuf>>("example".into())
1061 .with_value_type(descriptor)
1062 .unwrap();
1063 assert!(owner.try_eq(&owner.try_clone().unwrap()).unwrap());
1064 assert_eq!(owner.try_debug().unwrap(), "\"example\"");
1065 for name in ["Path", "Bounds"] {
1066 let resolved = registry
1067 .cel_type_resolver()
1068 .resolve_named_type(name, &[])
1069 .unwrap();
1070 assert_eq!(resolved.type_name(), name);
1071 assert!(
1072 resolved
1073 .complete_value_type()
1074 .unwrap()
1075 .same_shape(&(registry.get(name).unwrap().value_type_fn)())
1076 );
1077 }
1078 }
1079
1080 #[test]
1082 fn new_registers_builtin_f64_with_default() {
1083 let reg = TypeRegistry::new();
1084 let entry = reg.get("f64").expect("f64 registered");
1085 assert_eq!(entry.type_id, TypeId::of::<f64>());
1086 assert!(entry.default_fn.is_some(), "f64 must have a default");
1087 }
1088
1089 #[test]
1091 fn new_registers_builtin_string() {
1092 let reg = TypeRegistry::new();
1093 let entry = reg.get("String").expect("String registered");
1094 assert_eq!(entry.type_id, TypeId::of::<String>());
1095 }
1096
1097 #[test]
1099 fn register_custom_type_with_default() {
1100 let mut reg = TypeRegistry::new();
1101 reg.register::<u64>("my_u64");
1102 let entry = reg.get("my_u64").expect("custom type registered");
1103 assert_eq!(entry.type_id, TypeId::of::<u64>());
1104 assert!(entry.default_fn.is_some());
1105 }
1106
1107 #[test]
1109 fn register_no_default_has_no_default_fn() {
1110 #[derive(PartialEq, Clone, Debug)]
1112 struct NoDefault(i32);
1113
1114 let mut reg = TypeRegistry::new();
1115 reg.register_no_default::<NoDefault>("no_default");
1116 let entry = reg.get("no_default").expect("registered");
1117 assert!(entry.default_fn.is_none());
1118 }
1119
1120 #[test]
1122 fn cel_type_resolver_uses_registered_leaf_names() {
1123 #[derive(PartialEq, Clone, Debug)]
1125 struct Custom(i32);
1126
1127 let mut reg = TypeRegistry::new();
1128 reg.register_no_default::<Custom>("Custom");
1129
1130 let resolver = reg.cel_type_resolver();
1131 let leaf = resolver
1132 .resolve_named_type("Custom", &[])
1133 .expect("custom leaf registered");
1134
1135 assert_eq!(leaf.type_id(), TypeId::of::<Custom>());
1136 assert_eq!(leaf.type_name(), "Custom");
1137 }
1138
1139 #[test]
1141 fn push_arg_fn_drives_call_dyn() {
1142 use cel_runtime::DynSegment;
1143 use std::any::Any;
1144
1145 let reg = TypeRegistry::new();
1146 let entry = reg.get("i32").unwrap();
1147 let mut seg = DynSegment::new::<()>();
1148 (entry.push_arg_fn)(&mut seg, 0);
1149 let x: i32 = 7;
1150 let result: i32 = seg.call_dyn(&[&x as &dyn Any]).unwrap();
1151 assert_eq!(result, 7);
1152 }
1153
1154 #[test]
1156 fn add_cell_fn_creates_cell() {
1157 use adam_rs::Sheet;
1158 use std::any::Any;
1159
1160 let reg = TypeRegistry::new();
1161 let entry = reg.get("f64").unwrap();
1162 let mut sheet = Sheet::new();
1163 let val: Box<dyn Any> = Box::new(42.14_f64);
1164 let cell_id = (entry.add_cell_fn)(&mut sheet, val);
1165 assert_eq!(sheet.cells().count(), 1);
1166 assert_eq!(*sheet.read::<f64>(cell_id).unwrap(), 42.14);
1167 }
1168
1169 #[test]
1171 fn call_dyn_fn_returns_boxed_result() {
1172 use cel_runtime::DynSegment;
1173 use std::any::Any;
1174
1175 let reg = TypeRegistry::new();
1176 let entry = reg.get("i32").unwrap();
1177 let mut seg = DynSegment::new::<()>();
1178 (entry.push_arg_fn)(&mut seg, 0);
1179 let x: i32 = 99;
1180 let boxed = (entry.call_dyn_fn)(&mut seg, &[&x as &dyn Any]).unwrap();
1181 let result = boxed.downcast::<i32>().expect("i32");
1182 assert_eq!(*result, 99);
1183 }
1184
1185 #[test]
1187 fn entry_by_type_id_roundtrip() {
1188 let reg = TypeRegistry::new();
1189 let entry = reg
1190 .entry_by_type_id(std::any::TypeId::of::<f64>())
1191 .expect("f64 registered");
1192 assert_eq!(entry.type_id, std::any::TypeId::of::<f64>());
1193 }
1194
1195 #[test]
1197 fn get_nonexistent_returns_none() {
1198 let reg = TypeRegistry::new();
1199 assert!(reg.get("nonexistent").is_none());
1200 }
1201
1202 #[test]
1204 fn entry_by_type_id_nonexistent_returns_none() {
1205 let reg = TypeRegistry::new();
1206 assert!(
1208 reg.entry_by_type_id(std::any::TypeId::of::<Vec<u8>>())
1209 .is_none()
1210 );
1211 }
1212
1213 #[test]
1215 fn register_overwrite_removes_stale_type_id() {
1216 #[derive(PartialEq, Clone, Default, Debug)]
1218 struct TypeA;
1219 #[derive(PartialEq, Clone, Default, Debug)]
1221 struct TypeB;
1222
1223 let mut reg = TypeRegistry::new();
1224 reg.register::<TypeA>("alias");
1225 reg.register::<TypeB>("alias");
1226
1227 assert!(
1229 reg.entry_by_type_id(TypeId::of::<TypeA>()).is_none(),
1230 "stale TypeA mapping should have been removed"
1231 );
1232 assert!(reg.entry_by_type_id(TypeId::of::<TypeB>()).is_some());
1234 assert_eq!(reg.get("alias").unwrap().type_id, TypeId::of::<TypeB>());
1235 }
1236
1237 #[test]
1239 fn register_no_default_overwrite_removes_stale_type_id() {
1240 #[derive(PartialEq, Clone, Debug)]
1242 struct TypeA;
1243 #[derive(PartialEq, Clone, Debug)]
1245 struct TypeB;
1246
1247 let mut reg = TypeRegistry::new();
1248 reg.register_no_default::<TypeA>("alias");
1249 reg.register_no_default::<TypeB>("alias");
1250
1251 assert!(
1252 reg.entry_by_type_id(TypeId::of::<TypeA>()).is_none(),
1253 "stale TypeA mapping should have been removed"
1254 );
1255 assert!(reg.entry_by_type_id(TypeId::of::<TypeB>()).is_some());
1256 }
1257
1258 #[test]
1260 fn registered_leaf_capabilities_operate_on_canonical_owners() {
1261 let reg = TypeRegistry::new();
1262 let entry = reg.get("i32").unwrap();
1263 let value = cel_runtime::OwnedValue::new::<cel_runtime::Leaf<i32>>(7)
1264 .with_value_type((entry.value_type_fn)())
1265 .unwrap();
1266 let cloned = value.try_clone().unwrap();
1267 assert!(value.try_eq(&cloned).unwrap());
1268 assert_eq!(cloned.try_debug().unwrap(), "7");
1269 }
1270
1271 #[test]
1273 fn resolve_named_type_expr_returns_the_matching_type_shape() {
1274 let reg = TypeRegistry::new();
1275 let expr = cel_parser::TypeExpr::Named {
1276 name: "i32".to_string(),
1277 args: Vec::new(),
1278 span: point(Span::call_site()),
1279 };
1280 let shape = reg.resolve(&expr).unwrap();
1281 assert_eq!(shape, TypeShape::Named(TypeId::of::<i32>()));
1282 }
1283
1284 #[test]
1286 fn resolve_unknown_named_type_expr_is_an_error() {
1287 let reg = TypeRegistry::new();
1288 let expr = cel_parser::TypeExpr::Named {
1289 name: "bogus".to_string(),
1290 args: Vec::new(),
1291 span: point(Span::call_site()),
1292 };
1293 assert!(reg.resolve(&expr).is_err());
1294 }
1295
1296 #[test]
1298 fn resolve_tuple_type_expr_returns_a_nested_type_shape() {
1299 let reg = TypeRegistry::new();
1300 let span = point(Span::call_site());
1301 let expr = cel_parser::TypeExpr::Tuple {
1302 elements: vec![
1303 cel_parser::TypeExpr::Named {
1304 name: "i32".to_string(),
1305 args: Vec::new(),
1306 span,
1307 },
1308 cel_parser::TypeExpr::Tuple {
1309 elements: vec![
1310 cel_parser::TypeExpr::Named {
1311 name: "f64".to_string(),
1312 args: Vec::new(),
1313 span,
1314 },
1315 cel_parser::TypeExpr::Named {
1316 name: "String".to_string(),
1317 args: Vec::new(),
1318 span,
1319 },
1320 ],
1321 span,
1322 },
1323 ],
1324 span,
1325 };
1326 let shape = reg.resolve(&expr).unwrap();
1327 assert_eq!(
1328 shape,
1329 TypeShape::Tuple(vec![
1330 TypeShape::Named(TypeId::of::<i32>()),
1331 TypeShape::Tuple(vec![
1332 TypeShape::Named(TypeId::of::<f64>()),
1333 TypeShape::Named(TypeId::of::<String>()),
1334 ]),
1335 ])
1336 );
1337 }
1338
1339 #[test]
1341 fn resolve_accepts_structural_array_cell_type() {
1342 let registry = TypeRegistry::new();
1343 let span = point(Span::call_site());
1344 let expr = cel_parser::TypeExpr::Array {
1345 element: Box::new(cel_parser::TypeExpr::Named {
1346 name: "i32".to_string(),
1347 args: Vec::new(),
1348 span,
1349 }),
1350 span,
1351 };
1352 assert_eq!(
1353 registry.resolve(&expr).unwrap(),
1354 TypeShape::Array(Box::new(TypeShape::Named(TypeId::of::<i32>())))
1355 );
1356 }
1357
1358 #[test]
1360 fn resolve_accepts_registered_generic_leaf() {
1361 let mut registry = TypeRegistry::new();
1362 registry.register_no_default::<std::ops::RangeInclusive<f64>>("RangeInclusive(f64)");
1363 let span = point(Span::call_site());
1364 let expr = cel_parser::TypeExpr::Named {
1365 name: "RangeInclusive".to_string(),
1366 args: vec![cel_parser::TypeExpr::Named {
1367 name: "f64".to_string(),
1368 args: Vec::new(),
1369 span,
1370 }],
1371 span,
1372 };
1373 assert_eq!(
1374 registry.resolve(&expr).unwrap(),
1375 TypeShape::Named(TypeId::of::<std::ops::RangeInclusive<f64>>())
1376 );
1377 }
1378
1379 #[test]
1381 fn registered_aliases_preserve_requested_leaf_names() {
1382 for names in [["Integer", "i32"], ["i32", "Integer"]] {
1383 let mut registry = TypeRegistry::new();
1384 for name in names {
1385 registry.register::<i32>(name);
1386 }
1387 let snapshot = registry.cel_type_resolver();
1388 for name in names {
1389 let direct = registry.resolve_named_type(name, &[]).unwrap();
1390 let copied = snapshot.resolve_named_type(name, &[]).unwrap();
1391 assert_eq!(direct.type_name(), name);
1392 assert_eq!(copied.type_name(), name);
1393 assert!(
1394 direct
1395 .complete_value_type()
1396 .unwrap()
1397 .same_shape(&cel_runtime::ValueType::leaf::<i32>())
1398 );
1399 assert!(
1400 direct
1401 .complete_value_type()
1402 .unwrap()
1403 .same_shape(copied.complete_value_type().unwrap())
1404 );
1405 }
1406 }
1407 }
1408
1409 #[test]
1411 fn registered_aliases_preserve_recursive_generic_keys() {
1412 use std::ops::RangeInclusive;
1413
1414 for alias_first in [false, true] {
1415 let mut registry = TypeRegistry::new();
1416 if alias_first {
1417 registry.register::<i32>("Integer");
1418 }
1419 let names = [
1420 "Bounds(i32)",
1421 "Bounds(Integer)",
1422 "Bounds(Bounds(i32))",
1423 "Bounds([i32])",
1424 "Bounds((i32, [Integer]))",
1425 "Bounds((i32,))",
1426 "Bounds(Fn((), Integer))",
1427 "Bounds(Fn((Integer,), [i32]))",
1428 "Bounds(Fn((Integer, [i32]), ()))",
1429 ];
1430 for name in names {
1431 registry.register_no_default::<RangeInclusive<i32>>(name);
1432 }
1433 if !alias_first {
1434 registry.register::<i32>("Integer");
1435 }
1436 let snapshot = registry.cel_type_resolver();
1437 for name in names {
1438 for source in [name.to_owned(), format!("[({name}, [Integer])]")] {
1439 let expr = cel_parser::CELParser::new(cel_parser::OpLookup::new())
1440 .parse_type_expr_str(&source)
1441 .unwrap();
1442 let copied = expr.resolve(&snapshot).unwrap().into_value_type().unwrap();
1443 let direct = expr.resolve(®istry).unwrap();
1444 assert_eq!(direct.display_name(), source);
1445 let direct = direct.into_value_type().unwrap();
1446 assert!(direct.same_shape(&copied), "{source}");
1447 assert_eq!(
1448 registry.resolve(&expr).unwrap(),
1449 registry.shape_of(&copied).unwrap(),
1450 "{source}"
1451 );
1452 }
1453 }
1454 let missing = cel_parser::CELParser::new(cel_parser::OpLookup::new())
1455 .parse_type_expr_str("Bounds([Integer])")
1456 .unwrap();
1457 let direct = missing.resolve(®istry).unwrap_err();
1458 let copied = missing.resolve(&snapshot).unwrap_err();
1459 assert_eq!(direct.message(), copied.message());
1460 assert!(direct.message().contains("unknown type `Bounds`"));
1461 }
1462 }
1463
1464 #[test]
1466 fn cel_type_resolver_falls_back_to_builtin_generics() {
1467 let registry = TypeRegistry::new();
1468 let resolver = registry.cel_type_resolver();
1469 let f64_leaf = resolver.resolve_named_type("f64", &[]).expect("f64");
1470 let resolved = resolver
1471 .resolve_named_type("RangeInclusive", &[ResolvedType::Scalar(f64_leaf)])
1472 .expect("RangeInclusive(f64) should resolve via the builtin fallback");
1473 assert_eq!(resolved.type_name(), "RangeInclusive(f64)");
1474 }
1475
1476 #[test]
1478 fn display_name_formats_a_nested_tuple_shape() {
1479 let reg = TypeRegistry::new();
1480 let shape = TypeShape::Tuple(vec![
1481 TypeShape::Named(TypeId::of::<i32>()),
1482 TypeShape::Tuple(vec![
1483 TypeShape::Named(TypeId::of::<f64>()),
1484 TypeShape::Named(TypeId::of::<String>()),
1485 ]),
1486 ]);
1487 assert_eq!(reg.display_name(&shape), "(i32, (f64, String))");
1488 }
1489
1490 #[test]
1492 fn canonical_descriptor_describes_a_flat_tuple() {
1493 let reg = TypeRegistry::new();
1494 let shape = TypeShape::Tuple(vec![
1495 TypeShape::Named(TypeId::of::<i32>()),
1496 TypeShape::Named(TypeId::of::<f64>()),
1497 ]);
1498 let descriptor = reg.value_type(&shape);
1499 let prototype = descriptor.tuple_elements().unwrap();
1500 assert_eq!(prototype.len(), 2);
1501 assert_eq!(prototype[0].value_type.type_id(), TypeId::of::<i32>());
1502 assert_eq!(prototype[1].value_type.type_id(), TypeId::of::<f64>());
1503 assert_eq!(prototype[0].offset, 8);
1504 assert_eq!(prototype[1].offset, 0);
1505 }
1506
1507 #[test]
1509 fn canonical_descriptor_describes_a_nested_tuple() {
1510 let reg = TypeRegistry::new();
1511 let shape = TypeShape::Tuple(vec![
1512 TypeShape::Named(TypeId::of::<i32>()),
1513 TypeShape::Tuple(vec![
1514 TypeShape::Named(TypeId::of::<i32>()),
1515 TypeShape::Named(TypeId::of::<i32>()),
1516 ]),
1517 ]);
1518 let descriptor = reg.value_type(&shape);
1519 let prototype = descriptor.tuple_elements().unwrap();
1520 assert_eq!(prototype.len(), 2);
1521 assert_eq!(
1522 prototype[1].value_type.type_id(),
1523 TypeId::of::<cel_runtime::CNil<()>>()
1524 );
1525 assert_eq!(
1526 prototype[1]
1527 .value_type
1528 .tuple_elements()
1529 .expect("nested tuple")
1530 .len(),
1531 2
1532 );
1533 }
1534
1535 #[test]
1537 fn canonical_defaults_compose_and_report_missing_leaf_defaults() {
1538 use cel_runtime::{ArrayOf, Cons, IntoRustTuple, Leaf, Nil};
1539 let parsed = crate::AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
1540 .parse_str("sheet s { cell x: (i32, ([f64],)); }")
1541 .unwrap();
1542 type Shape = Cons<Leaf<i32>, Cons<Cons<ArrayOf<Leaf<f64>>, Nil>, Nil>>;
1544 let (number, nested) = parsed
1545 .read_compound::<Shape>(parsed.cell_names["x"].0)
1546 .unwrap()
1547 .into_rust_tuple();
1548 assert_eq!(number, 0);
1549 assert!(nested.into_rust_tuple().0.is_empty());
1550 #[derive(Clone, PartialEq, Debug)]
1552 struct NoDefault;
1553 let mut registry = TypeRegistry::new();
1554 registry.register_no_default::<NoDefault>("NoDefault");
1555 let error = crate::AdamParser::new(registry, cel_parser::OpLookup::new())
1556 .parse_str("sheet s { cell x: (NoDefault,); }")
1557 .err()
1558 .unwrap();
1559 assert!(error.to_string().contains("NoDefault"));
1560 }
1561
1562 #[test]
1564 fn eq_dyn_fn_compares_equal_i32_values_as_equal() {
1565 let reg = TypeRegistry::new();
1566 let entry = reg.get("i32").unwrap();
1567 let a: i32 = 7;
1568 let b: i32 = 7;
1569 assert!((entry.eq_dyn_fn)(&a, &b));
1570 }
1571
1572 #[test]
1574 fn eq_dyn_fn_compares_unequal_i32_values_as_unequal() {
1575 let reg = TypeRegistry::new();
1576 let entry = reg.get("i32").unwrap();
1577 let a: i32 = 7;
1578 let b: i32 = 8;
1579 assert!(!(entry.eq_dyn_fn)(&a, &b));
1580 }
1581
1582 #[test]
1584 fn register_no_default_also_populates_eq_dyn_fn() {
1585 #[derive(PartialEq, Clone, Debug)]
1587 struct NoDefault(i32);
1588
1589 let mut reg = TypeRegistry::new();
1590 reg.register_no_default::<NoDefault>("NoDefault");
1591 let entry = reg.get("NoDefault").unwrap();
1592 let a = NoDefault(1);
1593 let b = NoDefault(1);
1594 let c = NoDefault(2);
1595 assert!((entry.eq_dyn_fn)(&a, &b));
1596 assert!(!(entry.eq_dyn_fn)(&a, &c));
1597 }
1598
1599 #[test]
1601 fn range_entry_recognizes_a_registered_numeric_range_inclusive_type() {
1602 let reg = TypeRegistry::new();
1603 let entry = reg
1604 .range_entry(TypeId::of::<std::ops::RangeInclusive<i32>>())
1605 .expect("i32 range recognized");
1606 assert_eq!(entry.element_type_id, TypeId::of::<i32>());
1607 }
1608
1609 #[test]
1611 fn range_entry_returns_none_for_a_non_range_type() {
1612 let reg = TypeRegistry::new();
1613 assert!(reg.range_entry(TypeId::of::<i32>()).is_none());
1614 }
1615
1616 #[test]
1618 fn range_entry_clamp_fn_clamps_a_value_into_the_evaluated_bounds() {
1619 let reg = TypeRegistry::new();
1620 let entry = reg
1621 .range_entry(TypeId::of::<std::ops::RangeInclusive<i32>>())
1622 .unwrap();
1623 let mut segment = DynSegment::new::<()>();
1624 segment.push_arg::<i32>(1);
1625 segment.push_arg::<i32>(2);
1626 segment.op2(|a: i32, b: i32| a..=b).unwrap();
1627 let value = 500i32;
1628 let lo = 0i32;
1629 let hi = 100i32;
1630 let evaluated = segment.call_dyn_value(&[&value, &lo, &hi]).unwrap();
1631 let result = (entry.clamp_fn)(evaluated, &value).unwrap();
1632 assert_eq!(*result.downcast_ref::<i32>().unwrap(), 100);
1633 }
1634
1635 #[test]
1637 fn range_entry_bounds_fn_returns_the_evaluated_bounds() {
1638 let reg = TypeRegistry::new();
1639 let entry = reg
1640 .range_entry(TypeId::of::<std::ops::RangeInclusive<i32>>())
1641 .unwrap();
1642 let mut segment = DynSegment::new::<()>();
1643 segment.push_arg::<i32>(1);
1644 segment.push_arg::<i32>(2);
1645 segment.op2(|a: i32, b: i32| a..=b).unwrap();
1646 let placeholder = 0i32;
1647 let lo = 0i32;
1648 let hi = 100i32;
1649 let evaluated = segment.call_dyn_value(&[&placeholder, &lo, &hi]).unwrap();
1650 let (lo_out, hi_out) = (entry.bounds_fn)(evaluated).unwrap();
1651 assert_eq!(*lo_out.downcast_ref::<i32>().unwrap(), 0);
1652 assert_eq!(*hi_out.downcast_ref::<i32>().unwrap(), 100);
1653 }
1654}