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)]
28pub enum TypeExpr {
29 Named {
32 name: String,
34 args: Vec<TypeExpr>,
36 span: ExprSpan,
38 },
39 Array {
41 element: Box<TypeExpr>,
43 span: ExprSpan,
45 },
46 Tuple {
48 elements: Vec<TypeExpr>,
50 span: ExprSpan,
52 },
53}
54
55impl TypeExpr {
56 #[must_use]
58 pub fn span(&self) -> ExprSpan {
59 match self {
60 TypeExpr::Named { span, .. }
61 | TypeExpr::Array { span, .. }
62 | TypeExpr::Tuple { span, .. } => *span,
63 }
64 }
65
66 pub fn resolve(&self, resolver: &dyn TypeResolver) -> Result<ResolvedType> {
75 match self {
76 TypeExpr::Named { name, args, span } => {
77 let resolved_args = args
78 .iter()
79 .map(|arg| arg.resolve(resolver))
80 .collect::<Result<Vec<_>>>()?;
81 if name == "Fn" {
82 let mut resolved_args = resolved_args.into_iter();
83 return match (
84 resolved_args.next(),
85 resolved_args.next(),
86 resolved_args.next(),
87 ) {
88 (Some(ResolvedType::Tuple { elements }), Some(result), None) => {
89 Ok(ResolvedType::Callable {
90 parameters: elements,
91 result: Box::new(result),
92 })
93 }
94 _ => Err(ParseError::new_range(
95 "Fn requires a parameter tuple and a result type",
96 span.start,
97 span.end,
98 )),
99 };
100 }
101 resolver
102 .resolve_named_type(name, &resolved_args)
103 .map_or_else(
104 || {
105 Err(ParseError::new_range(
106 format!("unknown type `{name}`"),
107 span.start,
108 span.end,
109 ))
110 },
111 |leaf| Ok(ResolvedType::Scalar(leaf)),
112 )
113 }
114 TypeExpr::Array { element, .. } => Ok(ResolvedType::Array {
115 element: Box::new(element.resolve(resolver)?),
116 }),
117 TypeExpr::Tuple { elements, .. } => Ok(ResolvedType::Tuple {
118 elements: elements
119 .iter()
120 .map(|element| element.resolve(resolver))
121 .collect::<Result<Vec<_>>>()?,
122 }),
123 }
124 }
125}
126
127pub trait TypeResolver: Send + Sync {
132 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType>;
135}
136
137impl<F> TypeResolver for F
138where
139 F: Fn(&str, &[ResolvedType]) -> Option<ResolvedLeafType> + Send + Sync,
140{
141 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
142 self(name, args)
143 }
144}
145
146impl<const N: usize> TypeResolver for [(&'static str, ResolvedLeafType); N] {
147 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
148 if !args.is_empty() {
149 return None;
150 }
151 self.iter()
152 .find(|(registered_name, _)| *registered_name == name)
153 .map(|(_, leaf)| leaf.clone())
154 }
155}
156
157#[derive(Clone, Copy, Default)]
158pub(crate) struct BuiltinTypeResolver;
159
160impl TypeResolver for BuiltinTypeResolver {
161 fn resolve_named_type(&self, name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
162 let builtin = match args {
163 [] => op_table::builtin_scalar_type(name)
164 .or_else(|| op_table::builtin_generic_type_0(name)),
165 [ResolvedType::Scalar(arg_leaf)] => {
166 op_table::builtin_generic_type(name, arg_leaf.type_name())
167 }
168 _ => None,
169 }?;
170 Some(ResolvedLeafType::new(
171 builtin.type_name,
172 (builtin.value_type)(),
173 ))
174 }
175}
176
177#[must_use]
193pub fn builtin_type_resolver() -> Box<dyn TypeResolver> {
194 Box::new(BuiltinTypeResolver)
195}
196
197#[must_use]
204pub fn resolve_builtin_named_type(name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
205 BuiltinTypeResolver.resolve_named_type(name, args)
206}
207
208#[derive(Clone, Debug)]
219pub struct ResolvedLeafType {
220 type_id: TypeId,
221 type_name: Cow<'static, str>,
222 complete: ValueType,
223}
224
225impl PartialEq for ResolvedLeafType {
226 fn eq(&self, other: &Self) -> bool {
230 self.type_id == other.type_id
231 && self.type_name == other.type_name
232 && self.complete.same_shape(&other.complete)
233 }
234}
235
236impl Eq for ResolvedLeafType {}
237
238impl ResolvedLeafType {
239 pub fn leaf<T: 'static>(type_name: impl Into<Cow<'static, str>>) -> anyhow::Result<Self> {
257 anyhow::ensure!(
258 TypeId::of::<T>() != TypeId::of::<cel_runtime::OwnedValue>(),
259 "whole-value owners require an explicit representation witness"
260 );
261 Ok(Self::new(type_name, ValueType::leaf::<T>()))
262 }
263
264 #[must_use]
281 pub fn new(type_name: impl Into<Cow<'static, str>>, element_type: ValueType) -> Self {
282 let type_name = type_name.into();
283 debug_assert!(matches!(
284 element_type.kind(),
285 cel_runtime::ValueKind::Leaf(_)
286 ));
287 Self {
288 type_id: element_type.type_id(),
289 complete: element_type.with_type_name(type_name.clone()),
290 type_name,
291 }
292 }
293
294 pub fn with_complete_value_type(
314 mut self,
315 complete: cel_runtime::value_type::ValueType,
316 ) -> anyhow::Result<Self> {
317 complete.validate_opaque_leaf(self.type_id)?;
318 anyhow::ensure!(
319 complete.layout().size() == self.complete.size()
320 && complete.layout().align() == self.complete.align(),
321 "complete leaf layout mismatch"
322 );
323 self.complete = complete.with_type_name(self.type_name.clone());
324 Ok(self)
325 }
326
327 pub fn complete_value_type(&self) -> anyhow::Result<&cel_runtime::value_type::ValueType> {
341 anyhow::ensure!(
342 self.type_id != TypeId::of::<cel_runtime::OwnedValue>(),
343 "whole-value owners require an explicit representation witness"
344 );
345 Ok(&self.complete)
346 }
347
348 #[must_use]
350 pub fn type_id(&self) -> TypeId {
351 self.type_id
352 }
353
354 #[must_use]
356 pub fn type_name(&self) -> &str {
357 &self.type_name
358 }
359
360 #[must_use]
362 pub fn element_type(&self) -> &ValueType {
363 &self.complete
364 }
365}
366
367#[derive(Clone, Debug, PartialEq)]
369pub enum ResolvedType {
370 Scalar(ResolvedLeafType),
372 Array {
374 element: Box<ResolvedType>,
376 },
377 Tuple {
379 elements: Vec<ResolvedType>,
381 },
382 Callable {
384 parameters: Vec<ResolvedType>,
386 result: Box<ResolvedType>,
388 },
389}
390
391impl From<ResolvedLeafType> for ResolvedType {
392 fn from(leaf: ResolvedLeafType) -> Self {
394 Self::Scalar(leaf)
395 }
396}
397
398impl ResolvedType {
399 pub fn into_value_type(self) -> anyhow::Result<cel_runtime::ValueType> {
412 self.complete_value_type()
413 }
414
415 pub fn complete_value_type(&self) -> anyhow::Result<cel_runtime::value_type::ValueType> {
434 use cel_runtime::value_type::{AssociatedType, ValueType};
435 match self {
436 Self::Scalar(leaf) => Ok(leaf.complete_value_type()?.clone()),
437 Self::Array { element } => Ok(ValueType::array(element.complete_value_type()?)),
438 Self::Tuple { elements } => ValueType::tuple(
439 elements
440 .iter()
441 .map(|element| {
442 Ok(AssociatedType {
443 offset: 0,
444 value_type: element.complete_value_type()?,
445 })
446 })
447 .collect::<anyhow::Result<Vec<_>>>()?,
448 ),
449 Self::Callable { parameters, result } => Ok(ValueType::callable(
450 parameters
451 .iter()
452 .map(Self::complete_value_type)
453 .collect::<anyhow::Result<_>>()?,
454 result.complete_value_type()?,
455 )),
456 }
457 }
458
459 #[must_use]
463 pub fn display_name(&self) -> String {
464 match self {
465 ResolvedType::Scalar(leaf) => leaf.type_name().to_string(),
466 ResolvedType::Array { element } => format!("[{}]", element.display_name()),
467 ResolvedType::Tuple { elements } => {
468 let parts: Vec<String> = elements.iter().map(Self::display_name).collect();
469 format!(
470 "({}{})",
471 parts.join(", "),
472 if parts.len() == 1 { "," } else { "" }
473 )
474 }
475 Self::Callable { parameters, result } => format!(
476 "Fn(({}{}), {})",
477 parameters
478 .iter()
479 .map(Self::display_name)
480 .collect::<Vec<_>>()
481 .join(", "),
482 if parameters.len() == 1 { "," } else { "" },
483 result.display_name()
484 ),
485 }
486 }
487}
488
489#[derive(Clone, Debug)]
491pub struct ResolvedArrayType {
492 element_type: cel_runtime::ValueType,
493}
494
495impl PartialEq for ResolvedArrayType {
496 fn eq(&self, other: &Self) -> bool {
500 self.element_type.same_shape(&other.element_type)
501 }
502}
503
504impl ResolvedArrayType {
505 #[must_use]
522 pub fn from_element_type(element_type: ValueType) -> Self {
523 Self { element_type }
524 }
525
526 pub fn from_resolved_type(resolved: ResolvedType, span: ExprSpan) -> Result<Self> {
532 match resolved {
533 ResolvedType::Array { element } => Ok(Self {
534 element_type: element.into_value_type().map_err(|error| {
535 ParseError::new_range(error.to_string(), span.start, span.end)
536 })?,
537 }),
538 other => Err(ParseError::new_range(
539 format!(
540 "array annotations must name a complete array type, found `{}`",
541 other.display_name()
542 ),
543 span.start,
544 span.end,
545 )),
546 }
547 }
548
549 #[must_use]
551 pub fn element_type(&self) -> &cel_runtime::ValueType {
552 &self.element_type
553 }
554
555 #[must_use]
557 pub fn into_element_type(self) -> cel_runtime::ValueType {
558 self.element_type
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use std::any::TypeId;
565 use std::str::FromStr;
566
567 use cel_runtime::ValueType;
568 use proc_macro2::TokenStream;
569
570 use crate::{CELParser, OpLookup};
571
572 use super::{ResolvedLeafType, ResolvedType, TypeExpr, TypeResolver};
573
574 #[derive(Clone)]
575 struct RegistryType;
576
577 #[test]
579 fn complete_factory_foreign_leaf_preserves_optional_capabilities() {
580 use cel_runtime::{
581 owned_value::OwnedValue,
582 value_type::{Leaf, ValueType},
583 };
584 let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path").unwrap();
585 let descriptor = leaf.complete_value_type().unwrap().clone();
586 let owner = OwnedValue::new::<Leaf<std::path::PathBuf>>("path".into())
587 .with_value_type(descriptor)
588 .unwrap();
589 assert!(owner.try_clone().is_err());
590 assert!(owner.try_eq(&owner).is_err());
591 assert!(owner.try_debug().is_err());
592 let descriptor = ValueType::leaf::<std::path::PathBuf>()
593 .with_equality::<Leaf<std::path::PathBuf>>()
594 .unwrap();
595 let leaf = leaf.with_complete_value_type(descriptor).unwrap();
596 let owner = owner
597 .with_value_type(leaf.complete_value_type().unwrap().clone())
598 .unwrap();
599 assert!(owner.try_eq(&owner).unwrap());
600 assert!(owner.try_clone().is_err());
601 assert!(owner.try_debug().is_err());
602 assert!(
603 leaf.with_complete_value_type(ValueType::leaf::<String>())
604 .is_err()
605 );
606 for (descriptor, cloneable, comparable, debuggable) in [
607 (
608 ValueType::cloneable_leaf::<std::path::PathBuf>(),
609 true,
610 false,
611 false,
612 ),
613 (
614 ValueType::leaf::<std::path::PathBuf>()
615 .with_debug::<Leaf<std::path::PathBuf>>()
616 .unwrap(),
617 false,
618 false,
619 true,
620 ),
621 ] {
622 let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path")
623 .unwrap()
624 .with_complete_value_type(descriptor)
625 .unwrap();
626 let owner = OwnedValue::new::<Leaf<std::path::PathBuf>>("path".into())
627 .with_value_type(leaf.complete_value_type().unwrap().clone())
628 .unwrap();
629 assert_eq!(owner.try_clone().is_ok(), cloneable);
630 assert_eq!(owner.try_eq(&owner).is_ok(), comparable);
631 assert_eq!(owner.try_debug().is_ok(), debuggable);
632 }
633 assert!(ResolvedLeafType::leaf::<cel_runtime::OwnedValue>("Owner").is_err());
634 assert!(
635 ResolvedLeafType::leaf::<i32>("Integer")
636 .unwrap()
637 .with_complete_value_type(ValueType::tuple(vec![]).unwrap())
638 .is_err()
639 );
640 assert!(
641 ResolvedLeafType::leaf::<i32>("Integer")
642 .unwrap()
643 .with_complete_value_type(ValueType::leaf::<u32>())
644 .is_err()
645 );
646 }
647
648 #[test]
650 fn complete_factory_does_not_require_capability_traits() {
651 use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
652 struct MoveOnly(String);
654 let leaf = ResolvedLeafType::leaf::<MoveOnly>("MoveOnly").unwrap();
655 let owner = OwnedValue::new::<Leaf<MoveOnly>>(MoveOnly(String::from("owned")))
656 .with_value_type(leaf.complete_value_type().unwrap().clone())
657 .unwrap();
658 assert!(owner.try_clone().is_err());
659 assert!(owner.try_eq(&owner).is_err());
660 assert!(owner.try_debug().is_err());
661 assert_eq!(owner.try_into_typed::<Leaf<MoveOnly>>().unwrap().0, "owned");
662 }
663
664 #[test]
666 fn complete_factory_resolved_recursive_shapes_retain_fields() {
667 use cel_runtime::value_type::{ArrayOf, Cons, Leaf, Nil, ValueRepr};
668 let resolved = ResolvedType::Array {
669 element: Box::new(ResolvedType::Tuple {
670 elements: vec![
671 ResolvedType::Scalar(ResolvedLeafType::leaf::<i32>("Integer").unwrap()),
672 ResolvedType::Array {
673 element: Box::new(ResolvedType::Scalar(
674 ResolvedLeafType::leaf::<String>("Text").unwrap(),
675 )),
676 },
677 ],
678 }),
679 };
680 assert!(
681 resolved
682 .complete_value_type()
683 .unwrap()
684 .same_shape(
685 &ArrayOf::<Cons<Leaf<i32>, Cons<ArrayOf<Leaf<String>>, Nil>>>::value_type()
686 )
687 );
688 let unresolved = ResolvedType::Array {
689 element: Box::new(ResolvedType::Scalar(ResolvedLeafType::new(
690 "Erased",
691 ValueType::leaf::<i32>(),
692 ))),
693 };
694 assert!(unresolved.complete_value_type().is_ok());
695 }
696
697 #[test]
699 fn complete_factory_resolved_source_unit_is_not_host_unit() {
700 use cel_runtime::value_type::{Nil, ValueRepr};
701 let host = ResolvedLeafType::leaf::<()>("HostUnit").unwrap();
702 let source = ResolvedType::Tuple { elements: vec![] };
703 let complete = source.complete_value_type().unwrap();
704 assert!(complete.same_shape(&Nil::value_type()));
705 assert!(!complete.same_shape(host.complete_value_type().unwrap()));
706 assert!(
707 ResolvedLeafType::new("old", ValueType::leaf::<i32>())
708 .complete_value_type()
709 .is_ok()
710 );
711 }
712
713 #[test]
715 fn complete_factory_builtin_capabilities_are_available() {
716 use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
717 let leaf = super::resolve_builtin_named_type("i32", &[]).unwrap();
718 let owner = OwnedValue::new::<Leaf<i32>>(7)
719 .with_value_type(leaf.complete_value_type().unwrap().clone())
720 .unwrap();
721 assert_eq!(
722 owner
723 .try_clone()
724 .unwrap()
725 .try_into_typed::<Leaf<i32>>()
726 .unwrap(),
727 7
728 );
729 assert!(owner.try_eq(&owner).unwrap());
730 assert_eq!(owner.try_debug().unwrap(), "7");
731 for (name, args) in [
732 ("RangeFull", vec![]),
733 ("RangeInclusive", vec![ResolvedType::Scalar(leaf)]),
734 ] {
735 assert!(
736 super::resolve_builtin_named_type(name, &args)
737 .unwrap()
738 .complete_value_type()
739 .is_ok()
740 );
741 }
742 }
743
744 #[test]
745 fn parse_named_type_expr_preserves_its_name_and_span() {
746 let mut parser = CELParser::new(OpLookup::new());
747 let expr = parser.parse_type_expr_str("i32").unwrap();
748
749 match expr {
750 TypeExpr::Named { name, args, span } => {
751 assert_eq!(name, "i32");
752 assert!(args.is_empty());
753 assert_eq!(span.start.source_text().as_deref(), Some("i32"));
754 assert_eq!(span.end.source_text().as_deref(), Some("i32"));
755 }
756 other => panic!("expected a named type expression, got {other:?}"),
757 }
758 }
759
760 #[test]
761 fn parse_type_expr_consumes_one_prefix_and_leaves_following_tokens() {
762 let mut parser = CELParser::new(OpLookup::new());
763 let input = TokenStream::from_str("[i32] [f64]").unwrap();
764 parser.set_tokens(input.into_iter());
765
766 let first = parser.parse_type_expr().unwrap();
767 let second = parser.parse_type_expr().unwrap();
768
769 match first {
770 TypeExpr::Array { element, .. } => match *element {
771 TypeExpr::Named { name, .. } => assert_eq!(name, "i32"),
772 other => panic!("expected the first element type to be named, got {other:?}"),
773 },
774 other => panic!("expected the first prefix to be an array type, got {other:?}"),
775 }
776
777 match second {
778 TypeExpr::Array { element, .. } => match *element {
779 TypeExpr::Named { name, .. } => assert_eq!(name, "f64"),
780 other => panic!("expected the second element type to be named, got {other:?}"),
781 },
782 other => panic!("expected the second prefix to be an array type, got {other:?}"),
783 }
784 }
785
786 #[test]
787 fn parse_type_expr_str_still_rejects_trailing_tokens() {
788 let mut parser = CELParser::new(OpLookup::new());
789 let err = parser
790 .parse_type_expr_str("[i32] [f64]")
791 .expect_err("whole-input helpers must reject trailing tokens");
792
793 assert!(
794 err.message().contains("unexpected token"),
795 "got: {}",
796 err.message()
797 );
798 }
799
800 #[test]
801 fn parse_recursive_array_type_exprs() {
802 let mut parser = CELParser::new(OpLookup::new());
803 let expr = parser.parse_type_expr_str("[[i32]]").unwrap();
804
805 match expr {
806 TypeExpr::Array { element, span } => {
807 assert_eq!(span.start.source_text().as_deref(), Some("[[i32]]"));
808 assert_eq!(span.end.source_text().as_deref(), Some("[[i32]]"));
809 match *element {
810 TypeExpr::Array { element, .. } => match *element {
811 TypeExpr::Named { name, .. } => assert_eq!(name, "i32"),
812 other => panic!("expected the inner-most type to be named, got {other:?}"),
813 },
814 other => panic!("expected a nested array type, got {other:?}"),
815 }
816 }
817 other => panic!("expected an array type expression, got {other:?}"),
818 }
819 }
820
821 #[test]
822 fn parse_tuple_type_exprs_and_reuse_them_inside_array_syntax() {
823 let mut parser = CELParser::new(OpLookup::new());
824 let grouped = parser.parse_type_expr_str("([i32])").unwrap();
825 let tuple = parser.parse_type_expr_str("(i32, f64)").unwrap();
826 let array = parser.parse_type_expr_str("[(i32, f64)]").unwrap();
827
828 match grouped {
829 TypeExpr::Array { span, .. } => {
830 assert_eq!(span.start.source_text().as_deref(), Some("([i32])"));
831 assert_eq!(span.end.source_text().as_deref(), Some("([i32])"));
832 }
833 other => panic!("expected grouped array syntax to stay an array type, got {other:?}"),
834 }
835
836 match tuple {
837 TypeExpr::Tuple { elements, span } => {
838 assert_eq!(span.start.source_text().as_deref(), Some("(i32, f64)"));
839 assert_eq!(span.end.source_text().as_deref(), Some("(i32, f64)"));
840 assert_eq!(elements.len(), 2);
841 }
842 other => panic!("expected a tuple type expression, got {other:?}"),
843 }
844
845 match array {
846 TypeExpr::Array { element, .. } => match *element {
847 TypeExpr::Tuple { elements, .. } => assert_eq!(elements.len(), 2),
848 other => panic!("expected tuple syntax inside an array type, got {other:?}"),
849 },
850 other => panic!("expected an array type expression, got {other:?}"),
851 }
852 }
853
854 #[test]
855 fn parse_type_expr_reports_missing_closing_delimiters() {
856 let mut parser = CELParser::new(OpLookup::new());
857 let err = parser
858 .parse_type_expr_str("[(i32, f64)")
859 .expect_err("a missing closing bracket must fail");
860
861 assert!(
862 err.message().contains("closing") || err.message().contains("matching `]`"),
863 "got: {}",
864 err.message()
865 );
866 }
867
868 #[test]
869 fn resolve_unknown_named_type_expr_reports_the_leaf_name() {
870 let mut parser = CELParser::new(OpLookup::new());
871 let expr = parser.parse_type_expr_str("[does_not_exist]").unwrap();
872 let err = parser
873 .resolve_type_expr(&expr)
874 .expect_err("unknown names must fail through the resolver");
875
876 assert!(
877 err.message().contains("unknown type `does_not_exist`"),
878 "got: {}",
879 err.message()
880 );
881 }
882
883 #[test]
884 fn resolve_type_expr_uses_a_custom_registry_resolver_recursively() {
885 let resolver = [(
886 "RegistryType",
887 ResolvedLeafType::new("RegistryType", ValueType::leaf::<RegistryType>()),
888 )];
889 let mut parser = CELParser::with_type_resolver(OpLookup::new(), resolver);
890 let expr = parser.parse_type_expr_str("[[RegistryType]]").unwrap();
891 let resolved = parser.resolve_type_expr(&expr).unwrap();
892
893 match resolved {
894 ResolvedType::Array { element } => match *element {
895 ResolvedType::Array { element } => match *element {
896 ResolvedType::Scalar(leaf) => {
897 assert_eq!(leaf.type_id(), TypeId::of::<RegistryType>());
898 assert_eq!(leaf.type_name(), "RegistryType");
899 }
900 other => panic!("expected the nested leaf to resolve, got {other:?}"),
901 },
902 other => panic!("expected a nested array resolution, got {other:?}"),
903 },
904 other => panic!("expected an array resolution, got {other:?}"),
905 }
906 }
907
908 #[test]
909 fn resolve_type_expr_passes_resolved_args_to_the_resolver() {
910 struct GenericResolver;
911 impl TypeResolver for GenericResolver {
912 fn resolve_named_type(
913 &self,
914 name: &str,
915 args: &[ResolvedType],
916 ) -> Option<ResolvedLeafType> {
917 match (name, args) {
918 ("Wrapper", [ResolvedType::Scalar(inner)]) => Some(ResolvedLeafType::new(
919 format!("Wrapper({})", inner.type_name()),
920 ValueType::leaf::<i32>(),
921 )),
922 ("i32", []) => Some(ResolvedLeafType::new("i32", ValueType::leaf::<i32>())),
923 _ => None,
924 }
925 }
926 }
927
928 let mut parser = CELParser::with_type_resolver(OpLookup::new(), GenericResolver);
929 let expr = parser.parse_type_expr_str("Wrapper(i32)").unwrap();
930 let resolved = parser.resolve_type_expr(&expr).unwrap();
931
932 match resolved {
933 ResolvedType::Scalar(leaf) => assert_eq!(leaf.type_name(), "Wrapper(i32)"),
934 other => panic!("expected a resolved scalar, got {other:?}"),
935 }
936 }
937
938 #[test]
939 fn resolve_builtin_generic_type_expr_end_to_end() {
940 let mut parser = CELParser::new(OpLookup::new());
941 let expr = parser.parse_type_expr_str("RangeInclusive(f64)").unwrap();
942 let resolved = parser.resolve_type_expr(&expr).unwrap();
943
944 match resolved {
945 ResolvedType::Scalar(leaf) => {
946 assert_eq!(leaf.type_name(), "RangeInclusive(f64)");
947 assert_eq!(
948 leaf.type_id(),
949 TypeId::of::<std::ops::RangeInclusive<f64>>()
950 );
951 }
952 other => panic!("expected a resolved scalar, got {other:?}"),
953 }
954 }
955
956 #[test]
957 fn resolve_builtin_generic_type_as_array_element() {
958 let mut parser = CELParser::new(OpLookup::new());
959 let expr = parser.parse_type_expr_str("[RangeInclusive(f64)]").unwrap();
960 let resolved = parser.resolve_type_expr(&expr).unwrap();
961 let array = crate::ResolvedArrayType::from_resolved_type(resolved, expr.span()).unwrap();
962
963 assert_eq!(
964 array.element_type().type_id(),
965 TypeId::of::<std::ops::RangeInclusive<f64>>()
966 );
967 }
968
969 #[test]
970 fn resolve_range_full_generic_type_expr() {
971 let mut parser = CELParser::new(OpLookup::new());
972 let expr = parser.parse_type_expr_str("RangeFull()").unwrap();
973 let resolved = parser.resolve_type_expr(&expr).unwrap();
974
975 match resolved {
976 ResolvedType::Scalar(leaf) => assert_eq!(leaf.type_name(), "RangeFull"),
977 other => panic!("expected a resolved scalar, got {other:?}"),
978 }
979 }
980
981 #[test]
982 fn resolve_type_expr_reports_unknown_type_when_args_are_unrecognized() {
983 let mut parser = CELParser::new(OpLookup::new());
984 let expr = parser.parse_type_expr_str("Wrapper(f64)").unwrap();
985 let err = parser
986 .resolve_type_expr(&expr)
987 .expect_err("no resolver registers Wrapper as a built-in generic type");
988
989 assert!(
990 err.message().contains("unknown type `Wrapper`"),
991 "got: {}",
992 err.message()
993 );
994 }
995
996 #[test]
997 fn parse_named_type_expr_with_one_type_argument() {
998 let mut parser = CELParser::new(OpLookup::new());
999 let expr = parser.parse_type_expr_str("RangeInclusive(f64)").unwrap();
1000
1001 match expr {
1002 TypeExpr::Named { name, args, span } => {
1003 assert_eq!(name, "RangeInclusive");
1004 assert_eq!(span.start.source_text().as_deref(), Some("RangeInclusive"));
1005 assert_eq!(span.end.source_text().as_deref(), Some("(f64)"));
1006 assert_eq!(args.len(), 1);
1007 match &args[0] {
1008 TypeExpr::Named { name, args, .. } => {
1009 assert_eq!(name, "f64");
1010 assert!(args.is_empty());
1011 }
1012 other => panic!("expected a named type argument, got {other:?}"),
1013 }
1014 }
1015 other => panic!("expected a named type expression, got {other:?}"),
1016 }
1017 }
1018
1019 #[test]
1020 fn parse_named_type_expr_with_zero_type_arguments() {
1021 let mut parser = CELParser::new(OpLookup::new());
1022 let expr = parser.parse_type_expr_str("RangeFull()").unwrap();
1023
1024 match expr {
1025 TypeExpr::Named { name, args, span } => {
1026 assert_eq!(name, "RangeFull");
1027 assert_eq!(span.end.source_text().as_deref(), Some("()"));
1028 assert!(args.is_empty());
1029 }
1030 other => panic!("expected a named type expression, got {other:?}"),
1031 }
1032 }
1033
1034 #[test]
1035 fn parse_named_type_expr_with_multiple_and_nested_type_arguments() {
1036 let mut parser = CELParser::new(OpLookup::new());
1037 let expr = parser
1038 .parse_type_expr_str("Pair(RangeInclusive(f64), i32)")
1039 .unwrap();
1040
1041 match expr {
1042 TypeExpr::Named { name, args, .. } => {
1043 assert_eq!(name, "Pair");
1044 assert_eq!(args.len(), 2);
1045 match &args[0] {
1046 TypeExpr::Named { name, args, .. } => {
1047 assert_eq!(name, "RangeInclusive");
1048 assert_eq!(args.len(), 1);
1049 }
1050 other => panic!("expected the first argument to be named, got {other:?}"),
1051 }
1052 match &args[1] {
1053 TypeExpr::Named { name, args, .. } => {
1054 assert_eq!(name, "i32");
1055 assert!(args.is_empty());
1056 }
1057 other => panic!("expected the second argument to be named, got {other:?}"),
1058 }
1059 }
1060 other => panic!("expected a named type expression, got {other:?}"),
1061 }
1062 }
1063
1064 #[test]
1065 fn parse_named_type_expr_with_no_parens_still_parses_a_bare_name() {
1066 let mut parser = CELParser::new(OpLookup::new());
1067 let expr = parser.parse_type_expr_str("i32").unwrap();
1068
1069 match expr {
1070 TypeExpr::Named { name, args, .. } => {
1071 assert_eq!(name, "i32");
1072 assert!(args.is_empty());
1073 }
1074 other => panic!("expected a named type expression, got {other:?}"),
1075 }
1076 }
1077
1078 #[test]
1079 fn parse_named_type_expr_rejects_a_trailing_comma_in_the_argument_list() {
1080 let mut parser = CELParser::new(OpLookup::new());
1081 let err = parser
1082 .parse_type_expr_str("RangeInclusive(f64,)")
1083 .expect_err("a trailing comma is not a valid type-argument list");
1084
1085 assert!(
1086 err.message().contains("expected a type name"),
1087 "got: {}",
1088 err.message()
1089 );
1090 }
1091
1092 #[test]
1093 fn parse_named_type_expr_rejects_a_missing_closing_paren() {
1094 let mut parser = CELParser::new(OpLookup::new());
1099 let err = parser
1100 .parse_type_expr_str("RangeInclusive(f64")
1101 .expect_err("an unclosed type-argument list must be rejected");
1102
1103 assert!(
1104 err.message().contains("unclosed delimiter"),
1105 "got: {}",
1106 err.message()
1107 );
1108 }
1109
1110 #[test]
1111 fn parse_named_type_expr_rejects_arguments_missing_a_separating_comma() {
1112 let mut parser = CELParser::new(OpLookup::new());
1113 let err = parser
1114 .parse_type_expr_str("Pair(i32 f64)")
1115 .expect_err("adjacent type arguments without a comma must be rejected");
1116
1117 assert!(
1118 err.message()
1119 .contains("expected ',' or closing ')' in type argument list"),
1120 "got: {}",
1121 err.message()
1122 );
1123 }
1124
1125 #[test]
1126 fn parse_named_type_expr_supports_deeply_nested_generic_arguments() {
1127 let mut parser = CELParser::new(OpLookup::new());
1128 let expr = parser
1129 .parse_type_expr_str("Outer(Middle(Inner(f64)))")
1130 .unwrap();
1131
1132 match expr {
1133 TypeExpr::Named { name, args, .. } => {
1134 assert_eq!(name, "Outer");
1135 match &args[..] {
1136 [
1137 TypeExpr::Named {
1138 name: middle_name,
1139 args: middle_args,
1140 ..
1141 },
1142 ] => {
1143 assert_eq!(middle_name, "Middle");
1144 match &middle_args[..] {
1145 [
1146 TypeExpr::Named {
1147 name: inner_name,
1148 args: inner_args,
1149 ..
1150 },
1151 ] => {
1152 assert_eq!(inner_name, "Inner");
1153 match &inner_args[..] {
1154 [TypeExpr::Named { name, args, .. }] => {
1155 assert_eq!(name, "f64");
1156 assert!(args.is_empty());
1157 }
1158 other => panic!("expected one named argument, got {other:?}"),
1159 }
1160 }
1161 other => panic!("expected one named argument, got {other:?}"),
1162 }
1163 }
1164 other => panic!("expected one named argument, got {other:?}"),
1165 }
1166 }
1167 other => panic!("expected a named type expression, got {other:?}"),
1168 }
1169 }
1170
1171 #[test]
1172 fn builtin_type_resolver_resolves_scalars_and_generics() {
1173 let resolver = crate::builtin_type_resolver();
1174
1175 let scalar = resolver.resolve_named_type("i32", &[]).unwrap();
1176 assert_eq!(scalar.type_name(), "i32");
1177
1178 let arg = ResolvedType::Scalar(resolver.resolve_named_type("f64", &[]).unwrap());
1179 let generic = resolver
1180 .resolve_named_type("RangeInclusive", std::slice::from_ref(&arg))
1181 .unwrap();
1182 assert_eq!(generic.type_name(), "RangeInclusive(f64)");
1183
1184 assert!(resolver.resolve_named_type("not_a_type", &[]).is_none());
1185 }
1186
1187 #[test]
1188 fn resolve_builtin_named_type_matches_the_boxed_resolver() {
1189 let scalar = crate::resolve_builtin_named_type("i32", &[]).unwrap();
1190 assert_eq!(scalar.type_name(), "i32");
1191
1192 let arg = ResolvedType::Scalar(crate::resolve_builtin_named_type("f64", &[]).unwrap());
1193 let generic =
1194 crate::resolve_builtin_named_type("RangeInclusive", std::slice::from_ref(&arg))
1195 .unwrap();
1196 assert_eq!(generic.type_name(), "RangeInclusive(f64)");
1197
1198 assert!(crate::resolve_builtin_named_type("not_a_type", &[]).is_none());
1199 }
1200}