Skip to main content

cel_parser/
type_expr.rs

1//! Recursive CEL type expressions plus the small resolver boundary they need.
2//!
3//! Parsing stays in `cel-parser`, while leaf-name lookup is delegated to a [`TypeResolver`].
4//! This keeps CEL independent of `adam-lang` while still allowing a host to register custom
5//! types for typed arrays and future typed CEL surfaces.
6
7use std::any::TypeId;
8use std::borrow::Cow;
9
10use cel_runtime::ValueType;
11
12use crate::{ExprSpan, ParseError, Result, op_table};
13
14/// `type_expression = identifier [ "(" [ type_expression { "," type_expression } ] ")" ]
15///                   | "[" type_expression "]"
16///                   | "(" [ type_expression ["," [ type_expression { "," type_expression } ]] ] ")".`
17///
18/// `()` is the empty tuple type (0 elements); `(T)` is grouping (same as bare `T`); `(T,)` is a
19/// 1-element tuple; `(T, U, ...)` is n-element, no trailing comma. `[T]` names a rank-one array
20/// whose elements have type `T`; nested brackets compose recursively. `Name(A, B)` names a
21/// parameterized (generic) type applying zero or more type arguments (`RangeInclusive(f64)`,
22/// `RangeFull()`); an identifier immediately followed by `(` has no other meaning in type
23/// position, so this is unambiguous with adjacent tuple-grouping syntax.
24/// `Fn((A, B), R)` reserves structural callable syntax: the first argument must be a tuple
25/// of parameter types and the second is the result. `Fn((), R)` has no parameters;
26/// `Fn((A,), R)` has one. This syntax is resolved before host generic-leaf lookup.
27///
28/// # Examples
29/// See [`Self::resolve`] for parsing and resolving syntax.
30#[derive(Clone, Debug)]
31pub enum TypeExpr {
32    /// A single type name, optionally applying type arguments, resolved later through a
33    /// [`TypeResolver`].
34    Named {
35        /// The unresolved type name, exactly as written.
36        name: String,
37        /// Type arguments, e.g. `f64` in `RangeInclusive(f64)`. Empty for a plain name.
38        args: Vec<TypeExpr>,
39        /// The source span of the full name token, including any parenthesized argument list.
40        span: ExprSpan,
41    },
42    /// A recursively nested array type expression (`[T]`).
43    Array {
44        /// The element type expression.
45        element: Box<TypeExpr>,
46        /// The span of the whole bracketed type.
47        span: ExprSpan,
48    },
49    /// A recursively nested tuple type expression.
50    Tuple {
51        /// The tuple element type expressions, in source order.
52        elements: Vec<TypeExpr>,
53        /// The span of the whole parenthesized type.
54        span: ExprSpan,
55    },
56}
57
58impl TypeExpr {
59    /// Returns this type expression's source span.
60    ///
61    /// # Examples
62    /// ```
63    /// use cel_parser::{CELParser, OpLookup};
64    /// let ty = CELParser::new(OpLookup::new()).parse_type_expr_str("i32").unwrap();
65    /// assert_eq!(ty.span().start.start().column, 0);
66    /// ```
67    #[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    /// Resolves every named leaf through `resolver`, preserving the recursive type shape.
77    ///
78    /// # Errors
79    ///
80    /// Returns `Err` if some named leaf is not recognized by `resolver`, or `Fn` lacks
81    /// exactly two arguments with a tuple of parameter types first.
82    ///
83    /// - Complexity: O(n) traversal and output nodes, plus resolver and descriptor-copy costs.
84    ///
85    /// # Examples
86    /// ```
87    /// use cel_parser::{CELParser, OpLookup, builtin_type_resolver};
88    /// let ty = CELParser::new(OpLookup::new()).parse_type_expr_str("[i32]").unwrap();
89    /// assert_eq!(ty.resolve(builtin_type_resolver().as_ref()).unwrap().display_name(), "[i32]");
90    /// ```
91    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
144/// Resolves one named leaf type for CEL type expressions.
145///
146/// Implementations return `None` when `name` is unknown. Recursive array and tuple composition is
147/// performed by [`TypeExpr::resolve`], so a resolver only needs to recognize leaf names.
148///
149/// # Examples
150/// ```
151/// use cel_parser::{TypeResolver, ResolvedLeafType};
152/// let resolver = [("Count", ResolvedLeafType::leaf::<u32>("Count").unwrap())];
153/// assert!(resolver.resolve_named_type("Count", &[]).is_some());
154/// assert!(resolver.resolve_named_type("Missing", &[]).is_none());
155/// ```
156pub trait TypeResolver: Send + Sync {
157    /// Returns the registered leaf type named `name` applying `args` (already resolved), or
158    /// `None` if `name`/`args` is unrecognized. `args` is empty for a plain (non-generic) name.
159    ///
160    /// # Examples
161    /// See the [`TypeResolver`] example.
162    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    /// Delegates leaf recognition without imposing additional name or argument policies.
170    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    /// Resolves an exact registered name only when no generic arguments are supplied.
177    ///
178    /// - Complexity: O(N * b + c) for N names of at most b bytes and descriptor-copy cost c.
179    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/// Resolves builtin scalar and range leaf names without storing mutable registry state.
190#[derive(Clone, Copy, Default)]
191pub(crate) struct BuiltinTypeResolver;
192
193impl TypeResolver for BuiltinTypeResolver {
194    /// Resolves zero- or one-argument builtin leaves and rejects unsupported shapes.
195    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/// Returns `cel-parser`'s own built-in type resolver — every scalar name recognized by
212/// `builtin_scalar_type`, plus every built-in generic type recognized by `builtin_generic_type`
213/// (the `Range` family).
214///
215/// A host embedding `cel-parser` with its own [`TypeResolver`] (one that also knows
216/// host-specific custom types) can delegate any name it doesn't itself recognize to this
217/// resolver, so built-in types stay visible inside host-embedded CEL expressions.
218///
219/// The returned resolver is uniquely owned and zero-sized; its box does not allocate.
220///
221/// # Examples
222/// ```
223/// let resolver = cel_parser::builtin_type_resolver();
224/// assert!(resolver.resolve_named_type("u32", &[]).is_some());
225/// ```
226#[must_use]
227pub fn builtin_type_resolver() -> Box<dyn TypeResolver> {
228    Box::new(BuiltinTypeResolver)
229}
230
231/// Resolves `name`/`args` against `cel-parser`'s own built-in type resolver, without allocating
232/// a [`TypeResolver`] trait object.
233///
234/// Equivalent to `builtin_type_resolver().resolve_named_type(name, args)`, but for a host that
235/// only needs a one-off built-in fallback lookup (rather than a `TypeResolver` trait object to
236/// pass around), this avoids constructing a trait object.
237///
238/// # Examples
239/// ```
240/// assert!(cel_parser::resolve_builtin_named_type("i32", &[]).is_some());
241/// assert!(cel_parser::resolve_builtin_named_type("Missing", &[]).is_none());
242/// ```
243#[must_use]
244pub fn resolve_builtin_named_type(name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
245    BuiltinTypeResolver.resolve_named_type(name, args)
246}
247
248/// One resolved scalar leaf type.
249///
250/// This pairs a human-readable type name with complete canonical leaf metadata.
251/// Hosts use [`ValueType::cloneable_leaf`] when this leaf may also appear in a closure
252/// annotation; the same descriptor then supplies its checked argument-cloning adapter.
253///
254/// Concrete element factories retain complete metadata. Use
255/// [`Self::with_complete_value_type`] to enrich its optional capabilities;
256/// metadata cannot be recovered from a `TypeId`.
257/// Equality compares registered identity, not optional capability availability.
258///
259/// # Examples
260/// See [`Self::leaf`] for concrete leaf registration.
261#[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    /// Compares registered leaf identities independently of optional capabilities.
270    ///
271    /// - Complexity: O(l) for name length l.
272    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    /// Registers an opaque concrete host leaf without optional capabilities.
283    ///
284    /// Foreign types require no trait implementation or wrapper.
285    ///
286    /// # Errors
287    ///
288    /// Rejects an erased whole-value owner, which is not a concrete CEL leaf.
289    ///
290    /// - Complexity: O(l) time and space for a source-facing name of length l.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// use cel_parser::ResolvedLeafType;
296    /// let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path").unwrap();
297    /// assert!(leaf.complete_value_type().is_ok());
298    /// ```
299    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    /// Creates one resolved leaf from a user-facing type name and runtime element descriptor.
308    ///
309    /// The descriptor adopts `type_name`, so a runtime mismatch diagnostic against this leaf
310    /// reports the name the source annotation (or the host's registry) uses rather than
311    /// [`ValueType::leaf`]'s Rust type path.
312    ///
313    /// - Postcondition: the returned leaf's [`type_id`](Self::type_id) matches `element_type`,
314    ///   and its [`element_type`](Self::element_type) reports `type_name`.
315    /// - Precondition: `element_type` describes a concrete opaque leaf.
316    /// - Complexity: O(l) for name length l.
317    ///
318    /// # Examples
319    /// ```
320    /// let leaf = cel_parser::ResolvedLeafType::new("Integer", cel_runtime::ValueType::leaf::<i32>());
321    /// assert_eq!(leaf.type_name(), "Integer");
322    /// ```
323    #[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    /// Attaches a complete leaf descriptor built while its concrete type was known.
338    ///
339    /// Optional capabilities are retained unchanged and independently; attaching
340    /// metadata emits no interpreter instructions.
341    ///
342    /// # Errors
343    ///
344    /// Returns an error if either descriptor is structural or concrete identity
345    /// or layout differs.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// use cel_parser::ResolvedLeafType;
351    /// use cel_runtime::value_type::ValueType;
352    /// let leaf = ResolvedLeafType::leaf::<String>("Text").unwrap()
353    ///     .with_complete_value_type(ValueType::cloneable_leaf::<String>()).unwrap();
354    /// assert!(leaf.complete_value_type().is_ok());
355    /// ```
356    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    /// Borrows complete registration-time metadata without inventing erased adapters.
371    ///
372    /// # Errors
373    ///
374    /// Rejects an erased whole-value owner, which cannot be a registered leaf.
375    ///
376    /// # Examples
377    ///
378    /// ```
379    /// use cel_parser::ResolvedLeafType;
380    /// assert!(ResolvedLeafType::leaf::<()>("HostUnit").unwrap()
381    ///     .complete_value_type().is_ok());
382    /// ```
383    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    /// Returns this leaf type's concrete Rust [`TypeId`].
392    ///
393    /// # Examples
394    /// ```
395    /// let leaf = cel_parser::ResolvedLeafType::leaf::<i32>("Integer").unwrap();
396    /// assert_eq!(leaf.type_id(), std::any::TypeId::of::<i32>());
397    /// ```
398    #[must_use]
399    pub fn type_id(&self) -> TypeId {
400        self.type_id
401    }
402
403    /// Returns this leaf type's human-readable name.
404    ///
405    /// # Examples
406    /// ```
407    /// let leaf = cel_parser::ResolvedLeafType::leaf::<i32>("Integer").unwrap();
408    /// assert_eq!(leaf.type_name(), "Integer");
409    /// ```
410    #[must_use]
411    pub fn type_name(&self) -> &str {
412        &self.type_name
413    }
414
415    /// Returns complete canonical metadata for this leaf.
416    ///
417    /// # Examples
418    /// ```
419    /// let leaf = cel_parser::ResolvedLeafType::leaf::<i32>("Integer").unwrap();
420    /// assert_eq!(leaf.element_type().type_id(), leaf.type_id());
421    /// ```
422    #[must_use]
423    pub fn element_type(&self) -> &ValueType {
424        &self.complete
425    }
426}
427
428/// A resolved recursive CEL type expression.
429///
430/// # Examples
431/// See [`Self::display_name`] for recursive type construction.
432#[derive(Clone, Debug, PartialEq)]
433pub enum ResolvedType {
434    /// A resolved scalar leaf.
435    Scalar(ResolvedLeafType),
436    /// A resolved array type.
437    Array {
438        /// The resolved element type.
439        element: Box<ResolvedType>,
440    },
441    /// A resolved tuple type.
442    Tuple {
443        /// The resolved tuple element types, in source order.
444        elements: Vec<ResolvedType>,
445    },
446    /// A callable's source-ordered parameters and single result.
447    Callable {
448        /// The complete parameter types.
449        parameters: Vec<ResolvedType>,
450        /// The complete result type.
451        result: Box<ResolvedType>,
452    },
453}
454
455impl From<ResolvedLeafType> for ResolvedType {
456    /// Promotes a registered leaf without changing its identity.
457    fn from(leaf: ResolvedLeafType) -> Self {
458        Self::Scalar(leaf)
459    }
460}
461
462impl ResolvedType {
463    /// Consumes a complete source type and returns its canonical runtime descriptor.
464    ///
465    /// # Errors
466    /// Returns an error for unavailable leaf factories or unrepresentable tuple layouts.
467    ///
468    /// - Complexity: O(n) in recursive type nodes.
469    ///
470    /// # Examples
471    /// ```
472    /// use cel_parser::ResolvedType;
473    /// assert!(ResolvedType::Tuple { elements: vec![] }.into_value_type().is_ok());
474    /// ```
475    pub fn into_value_type(self) -> anyhow::Result<cel_runtime::ValueType> {
476        self.complete_value_type()
477    }
478
479    /// Builds canonical recursive metadata for interpreter storage.
480    ///
481    /// Empty tuples describe source unit, not an opaque host `()`.
482    ///
483    /// # Errors
484    ///
485    /// Returns an error for missing leaf factories or an unrepresentable tuple layout.
486    ///
487    /// - Complexity: O(n) time and output space for n recursive type nodes.
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use cel_parser::ResolvedType;
493    /// use cel_runtime::value_type::{Nil, ValueRepr};
494    /// assert!(ResolvedType::Tuple { elements: vec![] }
495    ///     .complete_value_type().unwrap().same_shape(&Nil::value_type()));
496    /// ```
497    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    /// Returns a human-readable name for this resolved type.
524    ///
525    /// - Complexity: O(n + b) time and O(b + d) space for n nodes, b output bytes and depth d.
526    ///
527    /// # Examples
528    /// ```
529    /// use cel_parser::{ResolvedType, ResolvedLeafType};
530    /// use cel_runtime::ValueType;
531    /// let ty = ResolvedType::Array {
532    ///     element: Box::new(ResolvedLeafType::new("i32", ValueType::leaf::<i32>()).into())
533    /// };
534    /// assert_eq!(ty.display_name(), "[i32]");
535    /// ```
536    #[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    /// Appends a type name in O(n + b) time and O(d) stack space for n nodes, b bytes and depth d.
544    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    /// Appends reusable tuple syntax, including the required singleton comma.
566    ///
567    /// - Complexity: O(n + b) time and O(d) stack space for n descendant nodes, b bytes and depth d.
568    fn write_tuple_name(elements: &[Self], output: &mut String) {
569        output.push('(');
570        // Emit each element once, separating adjacent names.
571        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/// The resolved descriptor for one complete array type annotation.
585///
586/// # Examples
587/// See [`Self::from_resolved_type`] for conversion and element metadata.
588#[derive(Clone, Debug)]
589pub struct ResolvedArrayType {
590    element_type: cel_runtime::ValueType,
591}
592
593impl PartialEq for ResolvedArrayType {
594    /// Compares recursive identities independently of optional capabilities.
595    ///
596    /// - Complexity: O(n) in descriptor nodes.
597    fn eq(&self, other: &Self) -> bool {
598        self.element_type.same_shape(&other.element_type)
599    }
600}
601
602impl ResolvedArrayType {
603    /// Creates a resolved array type from its already-resolved element descriptor.
604    ///
605    /// - Precondition: `element_type` retains a concrete canonical factory.
606    ///
607    /// - Postcondition: [`element_type`](Self::element_type) returns `element_type` unchanged, so
608    ///   the result describes an array whose elements have exactly that type.
609    ///
610    /// # Examples
611    ///
612    /// ```rust
613    /// use cel_parser::ResolvedArrayType;
614    /// use cel_runtime::ValueType;
615    ///
616    /// let array = ResolvedArrayType::from_element_type(ValueType::leaf::<i32>());
617    /// assert_eq!(array.element_type().type_name(), "i32");
618    /// ```
619    #[must_use]
620    pub fn from_element_type(element_type: ValueType) -> Self {
621        Self { element_type }
622    }
623
624    /// Creates a resolved array type from a fully resolved recursive type expression.
625    ///
626    /// # Errors
627    ///
628    /// Returns `Err` if `resolved` is not an array type or its layout is unrepresentable.
629    ///
630    /// - Complexity: Matches recursive descriptor conversion, or diagnostic name construction
631    ///   when the resolved type is not an array.
632    ///
633    /// # Examples
634    /// ```
635    /// use cel_parser::{CELParser, OpLookup, ResolvedArrayType};
636    /// let mut parser = CELParser::new(OpLookup::new());
637    /// let syntax = parser.parse_type_expr_str("[i32]").unwrap();
638    /// let resolved = parser.resolve_type_expr(&syntax).unwrap();
639    /// let array = ResolvedArrayType::from_resolved_type(resolved, syntax.span()).unwrap();
640    /// assert_eq!(array.element_type().type_name(), "i32");
641    /// assert_eq!(array.into_element_type().type_id(), std::any::TypeId::of::<i32>());
642    /// ```
643    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    /// Returns the recursive element descriptor this array annotation resolves to.
662    ///
663    /// # Examples
664    /// See [`Self::from_resolved_type`].
665    #[must_use]
666    pub fn element_type(&self) -> &cel_runtime::ValueType {
667        &self.element_type
668    }
669
670    /// Returns the recursive element descriptor, consuming `self`.
671    ///
672    /// # Examples
673    /// See [`Self::from_resolved_type`] for transferring the descriptor.
674    #[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    /// Registers foreign leaves without orphan impls or mandatory capabilities.
696    #[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    /// Stores and moves leaves that implement none of the optional capability traits.
767    #[test]
768    fn complete_factory_does_not_require_capability_traits() {
769        use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
770        /// Owns one move-only payload without clone, equality, or formatting.
771        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    /// Composes complete array-of-tuple metadata with nested arrays.
783    #[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    /// Keeps source unit distinct from an explicitly registered opaque host unit.
816    #[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    /// Built-ins retain cloning, equality, and formatting in complete metadata.
832    #[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        // `proc_macro2::TokenStream::from_str` tokenizes delimiters into a matched tree up
1213        // front, so a truly unclosed `(` is rejected at tokenization (a `LexError`), before
1214        // `parse_type_expression`'s own "expected ',' or closing ')'" check ever runs — see
1215        // `parse_named_type_expr_rejects_arguments_missing_a_separating_comma` for that case.
1216        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}