1use std::any::TypeId;
8use std::borrow::Cow;
9
10use cel_runtime::ValueType;
11
12use crate::{ExprSpan, ParseError, Result, op_table};
13
14#[derive(Clone, Debug)]
31pub enum TypeExpr {
32 Named {
35 name: String,
37 args: Vec<TypeExpr>,
39 span: ExprSpan,
41 },
42 Array {
44 element: Box<TypeExpr>,
46 span: ExprSpan,
48 },
49 Tuple {
51 elements: Vec<TypeExpr>,
53 span: ExprSpan,
55 },
56}
57
58impl TypeExpr {
59 #[must_use]
68 pub fn span(&self) -> ExprSpan {
69 match self {
70 TypeExpr::Named { span, .. }
71 | TypeExpr::Array { span, .. }
72 | TypeExpr::Tuple { span, .. } => *span,
73 }
74 }
75
76 pub fn resolve(&self, resolver: &dyn TypeResolver) -> Result<ResolvedType> {
92 match self {
93 TypeExpr::Named { name, args, span } => {
94 let resolved_args = args
95 .iter()
96 .map(|arg| arg.resolve(resolver))
97 .collect::<Result<Vec<_>>>()?;
98 if name == "Fn" {
99 let mut resolved_args = resolved_args.into_iter();
100 return match (
101 resolved_args.next(),
102 resolved_args.next(),
103 resolved_args.next(),
104 ) {
105 (Some(ResolvedType::Tuple { elements }), Some(result), None) => {
106 Ok(ResolvedType::Callable {
107 parameters: elements,
108 result: Box::new(result),
109 })
110 }
111 _ => Err(ParseError::new_range(
112 "Fn requires a parameter tuple and a result type",
113 span.start,
114 span.end,
115 )),
116 };
117 }
118 resolver
119 .resolve_named_type(name, &resolved_args)
120 .map_or_else(
121 || {
122 Err(ParseError::new_range(
123 format!("unknown type `{name}`"),
124 span.start,
125 span.end,
126 ))
127 },
128 |leaf| Ok(ResolvedType::Scalar(leaf)),
129 )
130 }
131 TypeExpr::Array { element, .. } => Ok(ResolvedType::Array {
132 element: Box::new(element.resolve(resolver)?),
133 }),
134 TypeExpr::Tuple { elements, .. } => Ok(ResolvedType::Tuple {
135 elements: elements
136 .iter()
137 .map(|element| element.resolve(resolver))
138 .collect::<Result<Vec<_>>>()?,
139 }),
140 }
141 }
142}
143
144pub trait TypeResolver: Send + Sync {
157 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType>;
163}
164
165impl<F> TypeResolver for F
166where
167 F: Fn(&str, &[ResolvedType]) -> Option<ResolvedLeafType> + Send + Sync,
168{
169 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
171 self(name, args)
172 }
173}
174
175impl<const N: usize> TypeResolver for [(&'static str, ResolvedLeafType); N] {
176 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
180 if !args.is_empty() {
181 return None;
182 }
183 self.iter()
184 .find(|(registered_name, _)| *registered_name == name)
185 .map(|(_, leaf)| leaf.clone())
186 }
187}
188
189#[derive(Clone, Copy, Default)]
191pub(crate) struct BuiltinTypeResolver;
192
193impl TypeResolver for BuiltinTypeResolver {
194 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
196 let builtin = match args {
197 [] => op_table::builtin_scalar_type(name)
198 .or_else(|| op_table::builtin_generic_type_0(name)),
199 [ResolvedType::Scalar(arg_leaf)] => {
200 op_table::builtin_generic_type(name, arg_leaf.type_name())
201 }
202 _ => None,
203 }?;
204 Some(ResolvedLeafType::new(
205 builtin.type_name,
206 (builtin.value_type)(),
207 ))
208 }
209}
210
211#[must_use]
227pub fn builtin_type_resolver() -> Box<dyn TypeResolver> {
228 Box::new(BuiltinTypeResolver)
229}
230
231#[must_use]
244pub fn resolve_builtin_named_type(name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
245 BuiltinTypeResolver.resolve_named_type(name, args)
246}
247
248#[derive(Clone, Debug)]
262pub struct ResolvedLeafType {
263 type_id: TypeId,
264 type_name: Cow<'static, str>,
265 complete: ValueType,
266}
267
268impl PartialEq for ResolvedLeafType {
269 fn eq(&self, other: &Self) -> bool {
273 self.type_id == other.type_id
274 && self.type_name == other.type_name
275 && self.complete.same_shape(&other.complete)
276 }
277}
278
279impl Eq for ResolvedLeafType {}
280
281impl ResolvedLeafType {
282 pub fn leaf<T: 'static>(type_name: impl Into<Cow<'static, str>>) -> anyhow::Result<Self> {
300 anyhow::ensure!(
301 TypeId::of::<T>() != TypeId::of::<cel_runtime::OwnedValue>(),
302 "whole-value owners require an explicit representation witness"
303 );
304 Ok(Self::new(type_name, ValueType::leaf::<T>()))
305 }
306
307 #[must_use]
324 pub fn new(type_name: impl Into<Cow<'static, str>>, element_type: ValueType) -> Self {
325 let type_name = type_name.into();
326 debug_assert!(matches!(
327 element_type.kind(),
328 cel_runtime::ValueKind::Leaf(_)
329 ));
330 Self {
331 type_id: element_type.type_id(),
332 complete: element_type.with_type_name(type_name.clone()),
333 type_name,
334 }
335 }
336
337 pub fn with_complete_value_type(
357 mut self,
358 complete: cel_runtime::value_type::ValueType,
359 ) -> anyhow::Result<Self> {
360 complete.validate_opaque_leaf(self.type_id)?;
361 anyhow::ensure!(
362 complete.layout().size() == self.complete.size()
363 && complete.layout().align() == self.complete.align(),
364 "complete leaf layout mismatch"
365 );
366 self.complete = complete.with_type_name(self.type_name.clone());
367 Ok(self)
368 }
369
370 pub fn complete_value_type(&self) -> anyhow::Result<&cel_runtime::value_type::ValueType> {
384 anyhow::ensure!(
385 self.type_id != TypeId::of::<cel_runtime::OwnedValue>(),
386 "whole-value owners require an explicit representation witness"
387 );
388 Ok(&self.complete)
389 }
390
391 #[must_use]
399 pub fn type_id(&self) -> TypeId {
400 self.type_id
401 }
402
403 #[must_use]
411 pub fn type_name(&self) -> &str {
412 &self.type_name
413 }
414
415 #[must_use]
423 pub fn element_type(&self) -> &ValueType {
424 &self.complete
425 }
426}
427
428#[derive(Clone, Debug, PartialEq)]
433pub enum ResolvedType {
434 Scalar(ResolvedLeafType),
436 Array {
438 element: Box<ResolvedType>,
440 },
441 Tuple {
443 elements: Vec<ResolvedType>,
445 },
446 Callable {
448 parameters: Vec<ResolvedType>,
450 result: Box<ResolvedType>,
452 },
453}
454
455impl From<ResolvedLeafType> for ResolvedType {
456 fn from(leaf: ResolvedLeafType) -> Self {
458 Self::Scalar(leaf)
459 }
460}
461
462impl ResolvedType {
463 pub fn into_value_type(self) -> anyhow::Result<cel_runtime::ValueType> {
476 self.complete_value_type()
477 }
478
479 pub fn complete_value_type(&self) -> anyhow::Result<cel_runtime::value_type::ValueType> {
498 use cel_runtime::value_type::{AssociatedType, ValueType};
499 match self {
500 Self::Scalar(leaf) => Ok(leaf.complete_value_type()?.clone()),
501 Self::Array { element } => Ok(ValueType::array(element.complete_value_type()?)),
502 Self::Tuple { elements } => ValueType::tuple(
503 elements
504 .iter()
505 .map(|element| {
506 Ok(AssociatedType {
507 offset: 0,
508 value_type: element.complete_value_type()?,
509 })
510 })
511 .collect::<anyhow::Result<Vec<_>>>()?,
512 ),
513 Self::Callable { parameters, result } => Ok(ValueType::callable(
514 parameters
515 .iter()
516 .map(Self::complete_value_type)
517 .collect::<anyhow::Result<_>>()?,
518 result.complete_value_type()?,
519 )),
520 }
521 }
522
523 #[must_use]
537 pub fn display_name(&self) -> String {
538 let mut output = String::new();
539 self.write_name(&mut output);
540 output
541 }
542
543 fn write_name(&self, output: &mut String) {
545 match self {
546 ResolvedType::Scalar(leaf) => output.push_str(leaf.type_name()),
547 ResolvedType::Array { element } => {
548 output.push('[');
549 element.write_name(output);
550 output.push(']');
551 }
552 ResolvedType::Tuple { elements } => {
553 Self::write_tuple_name(elements, output);
554 }
555 Self::Callable { parameters, result } => {
556 output.push_str("Fn(");
557 Self::write_tuple_name(parameters, output);
558 output.push_str(", ");
559 result.write_name(output);
560 output.push(')');
561 }
562 }
563 }
564
565 fn write_tuple_name(elements: &[Self], output: &mut String) {
569 output.push('(');
570 for (index, element) in elements.iter().enumerate() {
572 if index != 0 {
573 output.push_str(", ");
574 }
575 element.write_name(output);
576 }
577 if elements.len() == 1 {
578 output.push(',');
579 }
580 output.push(')');
581 }
582}
583
584#[derive(Clone, Debug)]
589pub struct ResolvedArrayType {
590 element_type: cel_runtime::ValueType,
591}
592
593impl PartialEq for ResolvedArrayType {
594 fn eq(&self, other: &Self) -> bool {
598 self.element_type.same_shape(&other.element_type)
599 }
600}
601
602impl ResolvedArrayType {
603 #[must_use]
620 pub fn from_element_type(element_type: ValueType) -> Self {
621 Self { element_type }
622 }
623
624 pub fn from_resolved_type(resolved: ResolvedType, span: ExprSpan) -> Result<Self> {
644 match resolved {
645 ResolvedType::Array { element } => Ok(Self {
646 element_type: element.into_value_type().map_err(|error| {
647 ParseError::new_range(error.to_string(), span.start, span.end)
648 })?,
649 }),
650 other => Err(ParseError::new_range(
651 format!(
652 "array annotations must name a complete array type, found `{}`",
653 other.display_name()
654 ),
655 span.start,
656 span.end,
657 )),
658 }
659 }
660
661 #[must_use]
666 pub fn element_type(&self) -> &cel_runtime::ValueType {
667 &self.element_type
668 }
669
670 #[must_use]
675 pub fn into_element_type(self) -> cel_runtime::ValueType {
676 self.element_type
677 }
678}
679
680#[cfg(test)]
681mod tests {
682 use std::any::TypeId;
683 use std::str::FromStr;
684
685 use cel_runtime::ValueType;
686 use proc_macro2::TokenStream;
687
688 use crate::{CELParser, OpLookup};
689
690 use super::{ResolvedLeafType, ResolvedType, TypeExpr, TypeResolver};
691
692 #[derive(Clone)]
693 struct RegistryType;
694
695 #[test]
697 fn complete_factory_foreign_leaf_preserves_optional_capabilities() {
698 use cel_runtime::{
699 owned_value::OwnedValue,
700 value_type::{Leaf, ValueType},
701 };
702 let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path").unwrap();
703 let descriptor = leaf.complete_value_type().unwrap().clone();
704 let owner = OwnedValue::new::<Leaf<std::path::PathBuf>>("path".into())
705 .with_value_type(descriptor)
706 .unwrap();
707 assert!(owner.try_clone().is_err());
708 assert!(owner.try_eq(&owner).is_err());
709 assert!(owner.try_debug().is_err());
710 let descriptor = ValueType::leaf::<std::path::PathBuf>()
711 .with_equality::<Leaf<std::path::PathBuf>>()
712 .unwrap();
713 let leaf = leaf.with_complete_value_type(descriptor).unwrap();
714 let owner = owner
715 .with_value_type(leaf.complete_value_type().unwrap().clone())
716 .unwrap();
717 assert!(owner.try_eq(&owner).unwrap());
718 assert!(owner.try_clone().is_err());
719 assert!(owner.try_debug().is_err());
720 assert!(
721 leaf.with_complete_value_type(ValueType::leaf::<String>())
722 .is_err()
723 );
724 for (descriptor, cloneable, comparable, debuggable) in [
725 (
726 ValueType::cloneable_leaf::<std::path::PathBuf>(),
727 true,
728 false,
729 false,
730 ),
731 (
732 ValueType::leaf::<std::path::PathBuf>()
733 .with_debug::<Leaf<std::path::PathBuf>>()
734 .unwrap(),
735 false,
736 false,
737 true,
738 ),
739 ] {
740 let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path")
741 .unwrap()
742 .with_complete_value_type(descriptor)
743 .unwrap();
744 let owner = OwnedValue::new::<Leaf<std::path::PathBuf>>("path".into())
745 .with_value_type(leaf.complete_value_type().unwrap().clone())
746 .unwrap();
747 assert_eq!(owner.try_clone().is_ok(), cloneable);
748 assert_eq!(owner.try_eq(&owner).is_ok(), comparable);
749 assert_eq!(owner.try_debug().is_ok(), debuggable);
750 }
751 assert!(ResolvedLeafType::leaf::<cel_runtime::OwnedValue>("Owner").is_err());
752 assert!(
753 ResolvedLeafType::leaf::<i32>("Integer")
754 .unwrap()
755 .with_complete_value_type(ValueType::tuple(vec![]).unwrap())
756 .is_err()
757 );
758 assert!(
759 ResolvedLeafType::leaf::<i32>("Integer")
760 .unwrap()
761 .with_complete_value_type(ValueType::leaf::<u32>())
762 .is_err()
763 );
764 }
765
766 #[test]
768 fn complete_factory_does_not_require_capability_traits() {
769 use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
770 struct MoveOnly(String);
772 let leaf = ResolvedLeafType::leaf::<MoveOnly>("MoveOnly").unwrap();
773 let owner = OwnedValue::new::<Leaf<MoveOnly>>(MoveOnly(String::from("owned")))
774 .with_value_type(leaf.complete_value_type().unwrap().clone())
775 .unwrap();
776 assert!(owner.try_clone().is_err());
777 assert!(owner.try_eq(&owner).is_err());
778 assert!(owner.try_debug().is_err());
779 assert_eq!(owner.try_into_typed::<Leaf<MoveOnly>>().unwrap().0, "owned");
780 }
781
782 #[test]
784 fn complete_factory_resolved_recursive_shapes_retain_fields() {
785 use cel_runtime::value_type::{ArrayOf, Cons, Leaf, Nil, ValueRepr};
786 let resolved = ResolvedType::Array {
787 element: Box::new(ResolvedType::Tuple {
788 elements: vec![
789 ResolvedType::Scalar(ResolvedLeafType::leaf::<i32>("Integer").unwrap()),
790 ResolvedType::Array {
791 element: Box::new(ResolvedType::Scalar(
792 ResolvedLeafType::leaf::<String>("Text").unwrap(),
793 )),
794 },
795 ],
796 }),
797 };
798 assert!(
799 resolved
800 .complete_value_type()
801 .unwrap()
802 .same_shape(
803 &ArrayOf::<Cons<Leaf<i32>, Cons<ArrayOf<Leaf<String>>, Nil>>>::value_type()
804 )
805 );
806 let unresolved = ResolvedType::Array {
807 element: Box::new(ResolvedType::Scalar(ResolvedLeafType::new(
808 "Erased",
809 ValueType::leaf::<i32>(),
810 ))),
811 };
812 assert!(unresolved.complete_value_type().is_ok());
813 }
814
815 #[test]
817 fn complete_factory_resolved_source_unit_is_not_host_unit() {
818 use cel_runtime::value_type::{Nil, ValueRepr};
819 let host = ResolvedLeafType::leaf::<()>("HostUnit").unwrap();
820 let source = ResolvedType::Tuple { elements: vec![] };
821 let complete = source.complete_value_type().unwrap();
822 assert!(complete.same_shape(&Nil::value_type()));
823 assert!(!complete.same_shape(host.complete_value_type().unwrap()));
824 assert!(
825 ResolvedLeafType::new("old", ValueType::leaf::<i32>())
826 .complete_value_type()
827 .is_ok()
828 );
829 }
830
831 #[test]
833 fn complete_factory_builtin_capabilities_are_available() {
834 use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
835 let leaf = super::resolve_builtin_named_type("i32", &[]).unwrap();
836 let owner = OwnedValue::new::<Leaf<i32>>(7)
837 .with_value_type(leaf.complete_value_type().unwrap().clone())
838 .unwrap();
839 assert_eq!(
840 owner
841 .try_clone()
842 .unwrap()
843 .try_into_typed::<Leaf<i32>>()
844 .unwrap(),
845 7
846 );
847 assert!(owner.try_eq(&owner).unwrap());
848 assert_eq!(owner.try_debug().unwrap(), "7");
849 for (name, args) in [
850 ("RangeFull", vec![]),
851 ("RangeInclusive", vec![ResolvedType::Scalar(leaf)]),
852 ] {
853 assert!(
854 super::resolve_builtin_named_type(name, &args)
855 .unwrap()
856 .complete_value_type()
857 .is_ok()
858 );
859 }
860 }
861
862 #[test]
863 fn parse_named_type_expr_preserves_its_name_and_span() {
864 let mut parser = CELParser::new(OpLookup::new());
865 let expr = parser.parse_type_expr_str("i32").unwrap();
866
867 match expr {
868 TypeExpr::Named { name, args, span } => {
869 assert_eq!(name, "i32");
870 assert!(args.is_empty());
871 assert_eq!(span.start.source_text().as_deref(), Some("i32"));
872 assert_eq!(span.end.source_text().as_deref(), Some("i32"));
873 }
874 other => panic!("expected a named type expression, got {other:?}"),
875 }
876 }
877
878 #[test]
879 fn parse_type_expr_consumes_one_prefix_and_leaves_following_tokens() {
880 let mut parser = CELParser::new(OpLookup::new());
881 let input = TokenStream::from_str("[i32] [f64]").unwrap();
882 parser.set_tokens(input.into_iter());
883
884 let first = parser.parse_type_expr().unwrap();
885 let second = parser.parse_type_expr().unwrap();
886
887 match first {
888 TypeExpr::Array { element, .. } => match *element {
889 TypeExpr::Named { name, .. } => assert_eq!(name, "i32"),
890 other => panic!("expected the first element type to be named, got {other:?}"),
891 },
892 other => panic!("expected the first prefix to be an array type, got {other:?}"),
893 }
894
895 match second {
896 TypeExpr::Array { element, .. } => match *element {
897 TypeExpr::Named { name, .. } => assert_eq!(name, "f64"),
898 other => panic!("expected the second element type to be named, got {other:?}"),
899 },
900 other => panic!("expected the second prefix to be an array type, got {other:?}"),
901 }
902 }
903
904 #[test]
905 fn parse_type_expr_str_still_rejects_trailing_tokens() {
906 let mut parser = CELParser::new(OpLookup::new());
907 let err = parser
908 .parse_type_expr_str("[i32] [f64]")
909 .expect_err("whole-input helpers must reject trailing tokens");
910
911 assert!(
912 err.message().contains("unexpected token"),
913 "got: {}",
914 err.message()
915 );
916 }
917
918 #[test]
919 fn parse_recursive_array_type_exprs() {
920 let mut parser = CELParser::new(OpLookup::new());
921 let expr = parser.parse_type_expr_str("[[i32]]").unwrap();
922
923 match expr {
924 TypeExpr::Array { element, span } => {
925 assert_eq!(span.start.source_text().as_deref(), Some("[[i32]]"));
926 assert_eq!(span.end.source_text().as_deref(), Some("[[i32]]"));
927 match *element {
928 TypeExpr::Array { element, .. } => match *element {
929 TypeExpr::Named { name, .. } => assert_eq!(name, "i32"),
930 other => panic!("expected the inner-most type to be named, got {other:?}"),
931 },
932 other => panic!("expected a nested array type, got {other:?}"),
933 }
934 }
935 other => panic!("expected an array type expression, got {other:?}"),
936 }
937 }
938
939 #[test]
940 fn parse_tuple_type_exprs_and_reuse_them_inside_array_syntax() {
941 let mut parser = CELParser::new(OpLookup::new());
942 let grouped = parser.parse_type_expr_str("([i32])").unwrap();
943 let tuple = parser.parse_type_expr_str("(i32, f64)").unwrap();
944 let array = parser.parse_type_expr_str("[(i32, f64)]").unwrap();
945
946 match grouped {
947 TypeExpr::Array { span, .. } => {
948 assert_eq!(span.start.source_text().as_deref(), Some("([i32])"));
949 assert_eq!(span.end.source_text().as_deref(), Some("([i32])"));
950 }
951 other => panic!("expected grouped array syntax to stay an array type, got {other:?}"),
952 }
953
954 match tuple {
955 TypeExpr::Tuple { elements, span } => {
956 assert_eq!(span.start.source_text().as_deref(), Some("(i32, f64)"));
957 assert_eq!(span.end.source_text().as_deref(), Some("(i32, f64)"));
958 assert_eq!(elements.len(), 2);
959 }
960 other => panic!("expected a tuple type expression, got {other:?}"),
961 }
962
963 match array {
964 TypeExpr::Array { element, .. } => match *element {
965 TypeExpr::Tuple { elements, .. } => assert_eq!(elements.len(), 2),
966 other => panic!("expected tuple syntax inside an array type, got {other:?}"),
967 },
968 other => panic!("expected an array type expression, got {other:?}"),
969 }
970 }
971
972 #[test]
973 fn parse_type_expr_reports_missing_closing_delimiters() {
974 let mut parser = CELParser::new(OpLookup::new());
975 let err = parser
976 .parse_type_expr_str("[(i32, f64)")
977 .expect_err("a missing closing bracket must fail");
978
979 assert!(
980 err.message().contains("closing") || err.message().contains("matching `]`"),
981 "got: {}",
982 err.message()
983 );
984 }
985
986 #[test]
987 fn resolve_unknown_named_type_expr_reports_the_leaf_name() {
988 let mut parser = CELParser::new(OpLookup::new());
989 let expr = parser.parse_type_expr_str("[does_not_exist]").unwrap();
990 let err = parser
991 .resolve_type_expr(&expr)
992 .expect_err("unknown names must fail through the resolver");
993
994 assert!(
995 err.message().contains("unknown type `does_not_exist`"),
996 "got: {}",
997 err.message()
998 );
999 }
1000
1001 #[test]
1002 fn resolve_type_expr_uses_a_custom_registry_resolver_recursively() {
1003 let resolver = [(
1004 "RegistryType",
1005 ResolvedLeafType::new("RegistryType", ValueType::leaf::<RegistryType>()),
1006 )];
1007 let mut parser = CELParser::with_type_resolver(OpLookup::new(), resolver);
1008 let expr = parser.parse_type_expr_str("[[RegistryType]]").unwrap();
1009 let resolved = parser.resolve_type_expr(&expr).unwrap();
1010
1011 match resolved {
1012 ResolvedType::Array { element } => match *element {
1013 ResolvedType::Array { element } => match *element {
1014 ResolvedType::Scalar(leaf) => {
1015 assert_eq!(leaf.type_id(), TypeId::of::<RegistryType>());
1016 assert_eq!(leaf.type_name(), "RegistryType");
1017 }
1018 other => panic!("expected the nested leaf to resolve, got {other:?}"),
1019 },
1020 other => panic!("expected a nested array resolution, got {other:?}"),
1021 },
1022 other => panic!("expected an array resolution, got {other:?}"),
1023 }
1024 }
1025
1026 #[test]
1027 fn resolve_type_expr_passes_resolved_args_to_the_resolver() {
1028 struct GenericResolver;
1029 impl TypeResolver for GenericResolver {
1030 fn resolve_named_type(
1031 &self,
1032 name: &str,
1033 args: &[ResolvedType],
1034 ) -> Option<ResolvedLeafType> {
1035 match (name, args) {
1036 ("Wrapper", [ResolvedType::Scalar(inner)]) => Some(ResolvedLeafType::new(
1037 format!("Wrapper({})", inner.type_name()),
1038 ValueType::leaf::<i32>(),
1039 )),
1040 ("i32", []) => Some(ResolvedLeafType::new("i32", ValueType::leaf::<i32>())),
1041 _ => None,
1042 }
1043 }
1044 }
1045
1046 let mut parser = CELParser::with_type_resolver(OpLookup::new(), GenericResolver);
1047 let expr = parser.parse_type_expr_str("Wrapper(i32)").unwrap();
1048 let resolved = parser.resolve_type_expr(&expr).unwrap();
1049
1050 match resolved {
1051 ResolvedType::Scalar(leaf) => assert_eq!(leaf.type_name(), "Wrapper(i32)"),
1052 other => panic!("expected a resolved scalar, got {other:?}"),
1053 }
1054 }
1055
1056 #[test]
1057 fn resolve_builtin_generic_type_expr_end_to_end() {
1058 let mut parser = CELParser::new(OpLookup::new());
1059 let expr = parser.parse_type_expr_str("RangeInclusive(f64)").unwrap();
1060 let resolved = parser.resolve_type_expr(&expr).unwrap();
1061
1062 match resolved {
1063 ResolvedType::Scalar(leaf) => {
1064 assert_eq!(leaf.type_name(), "RangeInclusive(f64)");
1065 assert_eq!(
1066 leaf.type_id(),
1067 TypeId::of::<std::ops::RangeInclusive<f64>>()
1068 );
1069 }
1070 other => panic!("expected a resolved scalar, got {other:?}"),
1071 }
1072 }
1073
1074 #[test]
1075 fn resolve_builtin_generic_type_as_array_element() {
1076 let mut parser = CELParser::new(OpLookup::new());
1077 let expr = parser.parse_type_expr_str("[RangeInclusive(f64)]").unwrap();
1078 let resolved = parser.resolve_type_expr(&expr).unwrap();
1079 let array = crate::ResolvedArrayType::from_resolved_type(resolved, expr.span()).unwrap();
1080
1081 assert_eq!(
1082 array.element_type().type_id(),
1083 TypeId::of::<std::ops::RangeInclusive<f64>>()
1084 );
1085 }
1086
1087 #[test]
1088 fn resolve_range_full_generic_type_expr() {
1089 let mut parser = CELParser::new(OpLookup::new());
1090 let expr = parser.parse_type_expr_str("RangeFull()").unwrap();
1091 let resolved = parser.resolve_type_expr(&expr).unwrap();
1092
1093 match resolved {
1094 ResolvedType::Scalar(leaf) => assert_eq!(leaf.type_name(), "RangeFull"),
1095 other => panic!("expected a resolved scalar, got {other:?}"),
1096 }
1097 }
1098
1099 #[test]
1100 fn resolve_type_expr_reports_unknown_type_when_args_are_unrecognized() {
1101 let mut parser = CELParser::new(OpLookup::new());
1102 let expr = parser.parse_type_expr_str("Wrapper(f64)").unwrap();
1103 let err = parser
1104 .resolve_type_expr(&expr)
1105 .expect_err("no resolver registers Wrapper as a built-in generic type");
1106
1107 assert!(
1108 err.message().contains("unknown type `Wrapper`"),
1109 "got: {}",
1110 err.message()
1111 );
1112 }
1113
1114 #[test]
1115 fn parse_named_type_expr_with_one_type_argument() {
1116 let mut parser = CELParser::new(OpLookup::new());
1117 let expr = parser.parse_type_expr_str("RangeInclusive(f64)").unwrap();
1118
1119 match expr {
1120 TypeExpr::Named { name, args, span } => {
1121 assert_eq!(name, "RangeInclusive");
1122 assert_eq!(span.start.source_text().as_deref(), Some("RangeInclusive"));
1123 assert_eq!(span.end.source_text().as_deref(), Some("(f64)"));
1124 assert_eq!(args.len(), 1);
1125 match &args[0] {
1126 TypeExpr::Named { name, args, .. } => {
1127 assert_eq!(name, "f64");
1128 assert!(args.is_empty());
1129 }
1130 other => panic!("expected a named type argument, got {other:?}"),
1131 }
1132 }
1133 other => panic!("expected a named type expression, got {other:?}"),
1134 }
1135 }
1136
1137 #[test]
1138 fn parse_named_type_expr_with_zero_type_arguments() {
1139 let mut parser = CELParser::new(OpLookup::new());
1140 let expr = parser.parse_type_expr_str("RangeFull()").unwrap();
1141
1142 match expr {
1143 TypeExpr::Named { name, args, span } => {
1144 assert_eq!(name, "RangeFull");
1145 assert_eq!(span.end.source_text().as_deref(), Some("()"));
1146 assert!(args.is_empty());
1147 }
1148 other => panic!("expected a named type expression, got {other:?}"),
1149 }
1150 }
1151
1152 #[test]
1153 fn parse_named_type_expr_with_multiple_and_nested_type_arguments() {
1154 let mut parser = CELParser::new(OpLookup::new());
1155 let expr = parser
1156 .parse_type_expr_str("Pair(RangeInclusive(f64), i32)")
1157 .unwrap();
1158
1159 match expr {
1160 TypeExpr::Named { name, args, .. } => {
1161 assert_eq!(name, "Pair");
1162 assert_eq!(args.len(), 2);
1163 match &args[0] {
1164 TypeExpr::Named { name, args, .. } => {
1165 assert_eq!(name, "RangeInclusive");
1166 assert_eq!(args.len(), 1);
1167 }
1168 other => panic!("expected the first argument to be named, got {other:?}"),
1169 }
1170 match &args[1] {
1171 TypeExpr::Named { name, args, .. } => {
1172 assert_eq!(name, "i32");
1173 assert!(args.is_empty());
1174 }
1175 other => panic!("expected the second argument to be named, got {other:?}"),
1176 }
1177 }
1178 other => panic!("expected a named type expression, got {other:?}"),
1179 }
1180 }
1181
1182 #[test]
1183 fn parse_named_type_expr_with_no_parens_still_parses_a_bare_name() {
1184 let mut parser = CELParser::new(OpLookup::new());
1185 let expr = parser.parse_type_expr_str("i32").unwrap();
1186
1187 match expr {
1188 TypeExpr::Named { name, args, .. } => {
1189 assert_eq!(name, "i32");
1190 assert!(args.is_empty());
1191 }
1192 other => panic!("expected a named type expression, got {other:?}"),
1193 }
1194 }
1195
1196 #[test]
1197 fn parse_named_type_expr_rejects_a_trailing_comma_in_the_argument_list() {
1198 let mut parser = CELParser::new(OpLookup::new());
1199 let err = parser
1200 .parse_type_expr_str("RangeInclusive(f64,)")
1201 .expect_err("a trailing comma is not a valid type-argument list");
1202
1203 assert!(
1204 err.message().contains("expected a type name"),
1205 "got: {}",
1206 err.message()
1207 );
1208 }
1209
1210 #[test]
1211 fn parse_named_type_expr_rejects_a_missing_closing_paren() {
1212 let mut parser = CELParser::new(OpLookup::new());
1217 let err = parser
1218 .parse_type_expr_str("RangeInclusive(f64")
1219 .expect_err("an unclosed type-argument list must be rejected");
1220
1221 assert!(
1222 err.message().contains("unclosed delimiter"),
1223 "got: {}",
1224 err.message()
1225 );
1226 }
1227
1228 #[test]
1229 fn parse_named_type_expr_rejects_arguments_missing_a_separating_comma() {
1230 let mut parser = CELParser::new(OpLookup::new());
1231 let err = parser
1232 .parse_type_expr_str("Pair(i32 f64)")
1233 .expect_err("adjacent type arguments without a comma must be rejected");
1234
1235 assert!(
1236 err.message()
1237 .contains("expected ',' or closing ')' in type argument list"),
1238 "got: {}",
1239 err.message()
1240 );
1241 }
1242
1243 #[test]
1244 fn parse_named_type_expr_supports_deeply_nested_generic_arguments() {
1245 let mut parser = CELParser::new(OpLookup::new());
1246 let expr = parser
1247 .parse_type_expr_str("Outer(Middle(Inner(f64)))")
1248 .unwrap();
1249
1250 match expr {
1251 TypeExpr::Named { name, args, .. } => {
1252 assert_eq!(name, "Outer");
1253 match &args[..] {
1254 [
1255 TypeExpr::Named {
1256 name: middle_name,
1257 args: middle_args,
1258 ..
1259 },
1260 ] => {
1261 assert_eq!(middle_name, "Middle");
1262 match &middle_args[..] {
1263 [
1264 TypeExpr::Named {
1265 name: inner_name,
1266 args: inner_args,
1267 ..
1268 },
1269 ] => {
1270 assert_eq!(inner_name, "Inner");
1271 match &inner_args[..] {
1272 [TypeExpr::Named { name, args, .. }] => {
1273 assert_eq!(name, "f64");
1274 assert!(args.is_empty());
1275 }
1276 other => panic!("expected one named argument, got {other:?}"),
1277 }
1278 }
1279 other => panic!("expected one named argument, got {other:?}"),
1280 }
1281 }
1282 other => panic!("expected one named argument, got {other:?}"),
1283 }
1284 }
1285 other => panic!("expected a named type expression, got {other:?}"),
1286 }
1287 }
1288
1289 #[test]
1290 fn builtin_type_resolver_resolves_scalars_and_generics() {
1291 let resolver = crate::builtin_type_resolver();
1292
1293 let scalar = resolver.resolve_named_type("i32", &[]).unwrap();
1294 assert_eq!(scalar.type_name(), "i32");
1295
1296 let arg = ResolvedType::Scalar(resolver.resolve_named_type("f64", &[]).unwrap());
1297 let generic = resolver
1298 .resolve_named_type("RangeInclusive", std::slice::from_ref(&arg))
1299 .unwrap();
1300 assert_eq!(generic.type_name(), "RangeInclusive(f64)");
1301
1302 assert!(resolver.resolve_named_type("not_a_type", &[]).is_none());
1303 }
1304
1305 #[test]
1306 fn resolve_builtin_named_type_matches_the_boxed_resolver() {
1307 let boxed = crate::builtin_type_resolver();
1308 let scalar = crate::resolve_builtin_named_type("i32", &[]).unwrap();
1309 assert_eq!(scalar.type_name(), "i32");
1310 assert!(
1311 scalar
1312 .element_type()
1313 .same_shape(boxed.resolve_named_type("i32", &[]).unwrap().element_type())
1314 );
1315
1316 let arg = ResolvedType::Scalar(crate::resolve_builtin_named_type("f64", &[]).unwrap());
1317 let generic =
1318 crate::resolve_builtin_named_type("RangeInclusive", std::slice::from_ref(&arg))
1319 .unwrap();
1320 assert_eq!(generic.type_name(), "RangeInclusive(f64)");
1321 assert!(
1322 generic.element_type().same_shape(
1323 boxed
1324 .resolve_named_type("RangeInclusive", std::slice::from_ref(&arg))
1325 .unwrap()
1326 .element_type()
1327 )
1328 );
1329
1330 assert!(crate::resolve_builtin_named_type("not_a_type", &[]).is_none());
1331 assert!(boxed.resolve_named_type("not_a_type", &[]).is_none());
1332 }
1333}