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#[derive(Clone, Debug)]
28pub enum TypeExpr {
29    /// A single type name, optionally applying type arguments, resolved later through a
30    /// [`TypeResolver`].
31    Named {
32        /// The unresolved type name, exactly as written.
33        name: String,
34        /// Type arguments, e.g. `f64` in `RangeInclusive(f64)`. Empty for a plain name.
35        args: Vec<TypeExpr>,
36        /// The source span of the full name token, including any parenthesized argument list.
37        span: ExprSpan,
38    },
39    /// A recursively nested array type expression (`[T]`).
40    Array {
41        /// The element type expression.
42        element: Box<TypeExpr>,
43        /// The span of the whole bracketed type.
44        span: ExprSpan,
45    },
46    /// A recursively nested tuple type expression.
47    Tuple {
48        /// The tuple element type expressions, in source order.
49        elements: Vec<TypeExpr>,
50        /// The span of the whole parenthesized type.
51        span: ExprSpan,
52    },
53}
54
55impl TypeExpr {
56    /// Returns this type expression's source span.
57    #[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    /// Resolves every named leaf through `resolver`, preserving the recursive type shape.
67    ///
68    /// # Errors
69    ///
70    /// Returns `Err` if some named leaf is not recognized by `resolver`, or `Fn` lacks
71    /// exactly two arguments with a tuple of parameter types first.
72    ///
73    /// - Complexity: O(n) in the number of nodes in this type tree.
74    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
127/// Resolves one named leaf type for CEL type expressions.
128///
129/// Implementations return `None` when `name` is unknown. Recursive array and tuple composition is
130/// performed by [`TypeExpr::resolve`], so a resolver only needs to recognize leaf names.
131pub trait TypeResolver: Send + Sync {
132    /// Returns the registered leaf type named `name` applying `args` (already resolved), or
133    /// `None` if `name`/`args` is unrecognized. `args` is empty for a plain (non-generic) name.
134    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/// Returns `cel-parser`'s own built-in type resolver — every scalar name recognized by
178/// `builtin_scalar_type`, plus every built-in generic type recognized by `builtin_generic_type`
179/// (the `Range` family).
180///
181/// A host embedding `cel-parser` with its own [`TypeResolver`] (one that also knows
182/// host-specific custom types) can delegate any name it doesn't itself recognize to this
183/// resolver, so built-in types stay visible inside host-embedded CEL expressions.
184///
185/// The returned resolver is uniquely owned and zero-sized; its box does not allocate.
186///
187/// # Examples
188/// ```
189/// let resolver = cel_parser::builtin_type_resolver();
190/// assert!(resolver.resolve_named_type("u32", &[]).is_some());
191/// ```
192#[must_use]
193pub fn builtin_type_resolver() -> Box<dyn TypeResolver> {
194    Box::new(BuiltinTypeResolver)
195}
196
197/// Resolves `name`/`args` against `cel-parser`'s own built-in type resolver, without allocating
198/// a [`TypeResolver`] trait object.
199///
200/// Equivalent to `builtin_type_resolver().resolve_named_type(name, args)`, but for a host that
201/// only needs a one-off built-in fallback lookup (rather than a `TypeResolver` trait object to
202/// pass around), this avoids constructing a trait object.
203#[must_use]
204pub fn resolve_builtin_named_type(name: &str, args: &[ResolvedType]) -> Option<ResolvedLeafType> {
205    BuiltinTypeResolver.resolve_named_type(name, args)
206}
207
208/// One resolved scalar leaf type.
209///
210/// This pairs a human-readable type name with complete canonical leaf metadata.
211/// Hosts use [`ValueType::cloneable_leaf`] when this leaf may also appear in a closure
212/// annotation; the same descriptor then supplies its checked argument-cloning adapter.
213///
214/// Concrete element factories retain complete metadata. Use
215/// [`Self::with_complete_value_type`] to enrich its optional capabilities;
216/// metadata cannot be recovered from a `TypeId`.
217/// Equality compares registered identity, not optional capability availability.
218#[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    /// Compares registered leaf identities independently of optional capabilities.
227    ///
228    /// - Complexity: O(l) for name length l.
229    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    /// Registers an opaque concrete host leaf without optional capabilities.
240    ///
241    /// Foreign types require no trait implementation or wrapper.
242    ///
243    /// # Errors
244    ///
245    /// Rejects an erased whole-value owner, which is not a concrete CEL leaf.
246    ///
247    /// - Complexity: O(l) time and space for a source-facing name of length l.
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// use cel_parser::ResolvedLeafType;
253    /// let leaf = ResolvedLeafType::leaf::<std::path::PathBuf>("Path").unwrap();
254    /// assert!(leaf.complete_value_type().is_ok());
255    /// ```
256    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    /// Creates one resolved leaf from a user-facing type name and runtime element descriptor.
265    ///
266    /// The descriptor adopts `type_name`, so a runtime mismatch diagnostic against this leaf
267    /// reports the name the source annotation (or the host's registry) uses rather than
268    /// [`ValueType::leaf`]'s Rust type path.
269    ///
270    /// - Postcondition: the returned leaf's [`type_id`](Self::type_id) matches `element_type`,
271    ///   and its [`element_type`](Self::element_type) reports `type_name`.
272    /// - Precondition: `element_type` describes a concrete opaque leaf.
273    /// - Complexity: O(l) for name length l.
274    ///
275    /// # Examples
276    /// ```
277    /// let leaf = cel_parser::ResolvedLeafType::new("Integer", cel_runtime::ValueType::leaf::<i32>());
278    /// assert_eq!(leaf.type_name(), "Integer");
279    /// ```
280    #[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    /// Attaches a complete leaf descriptor built while its concrete type was known.
295    ///
296    /// Optional capabilities are retained unchanged and independently; attaching
297    /// metadata emits no interpreter instructions.
298    ///
299    /// # Errors
300    ///
301    /// Returns an error if either descriptor is structural or concrete identity
302    /// or layout differs.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// use cel_parser::ResolvedLeafType;
308    /// use cel_runtime::value_type::ValueType;
309    /// let leaf = ResolvedLeafType::leaf::<String>("Text").unwrap()
310    ///     .with_complete_value_type(ValueType::cloneable_leaf::<String>()).unwrap();
311    /// assert!(leaf.complete_value_type().is_ok());
312    /// ```
313    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    /// Borrows complete registration-time metadata without inventing erased adapters.
328    ///
329    /// # Errors
330    ///
331    /// Rejects an erased whole-value owner, which cannot be a registered leaf.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use cel_parser::ResolvedLeafType;
337    /// assert!(ResolvedLeafType::leaf::<()>("HostUnit").unwrap()
338    ///     .complete_value_type().is_ok());
339    /// ```
340    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    /// Returns this leaf type's concrete Rust [`TypeId`].
349    #[must_use]
350    pub fn type_id(&self) -> TypeId {
351        self.type_id
352    }
353
354    /// Returns this leaf type's human-readable name.
355    #[must_use]
356    pub fn type_name(&self) -> &str {
357        &self.type_name
358    }
359
360    /// Returns complete canonical metadata for this leaf.
361    #[must_use]
362    pub fn element_type(&self) -> &ValueType {
363        &self.complete
364    }
365}
366
367/// A resolved recursive CEL type expression.
368#[derive(Clone, Debug, PartialEq)]
369pub enum ResolvedType {
370    /// A resolved scalar leaf.
371    Scalar(ResolvedLeafType),
372    /// A resolved array type.
373    Array {
374        /// The resolved element type.
375        element: Box<ResolvedType>,
376    },
377    /// A resolved tuple type.
378    Tuple {
379        /// The resolved tuple element types, in source order.
380        elements: Vec<ResolvedType>,
381    },
382    /// A callable's source-ordered parameters and single result.
383    Callable {
384        /// The complete parameter types.
385        parameters: Vec<ResolvedType>,
386        /// The complete result type.
387        result: Box<ResolvedType>,
388    },
389}
390
391impl From<ResolvedLeafType> for ResolvedType {
392    /// Promotes a registered leaf without changing its identity.
393    fn from(leaf: ResolvedLeafType) -> Self {
394        Self::Scalar(leaf)
395    }
396}
397
398impl ResolvedType {
399    /// Consumes a complete source type and returns its canonical runtime descriptor.
400    ///
401    /// # Errors
402    /// Returns an error for unavailable leaf factories or unrepresentable tuple layouts.
403    ///
404    /// - Complexity: O(n) in recursive type nodes.
405    ///
406    /// # Examples
407    /// ```
408    /// use cel_parser::ResolvedType;
409    /// assert!(ResolvedType::Tuple { elements: vec![] }.into_value_type().is_ok());
410    /// ```
411    pub fn into_value_type(self) -> anyhow::Result<cel_runtime::ValueType> {
412        self.complete_value_type()
413    }
414
415    /// Builds canonical recursive metadata for interpreter storage.
416    ///
417    /// Empty tuples describe source unit, not an opaque host `()`.
418    ///
419    /// # Errors
420    ///
421    /// Returns an error for missing leaf factories or an unrepresentable tuple layout.
422    ///
423    /// - Complexity: O(n) time and output space for n recursive type nodes.
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use cel_parser::ResolvedType;
429    /// use cel_runtime::value_type::{Nil, ValueRepr};
430    /// assert!(ResolvedType::Tuple { elements: vec![] }
431    ///     .complete_value_type().unwrap().same_shape(&Nil::value_type()));
432    /// ```
433    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    /// Returns a human-readable name for this resolved type.
460    ///
461    /// - Complexity: O(n) in the number of nodes in this type tree.
462    #[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/// The resolved descriptor for one complete array type annotation.
490#[derive(Clone, Debug)]
491pub struct ResolvedArrayType {
492    element_type: cel_runtime::ValueType,
493}
494
495impl PartialEq for ResolvedArrayType {
496    /// Compares recursive identities independently of optional capabilities.
497    ///
498    /// - Complexity: O(n) in descriptor nodes.
499    fn eq(&self, other: &Self) -> bool {
500        self.element_type.same_shape(&other.element_type)
501    }
502}
503
504impl ResolvedArrayType {
505    /// Creates a resolved array type from its already-resolved element descriptor.
506    ///
507    /// - Precondition: `element_type` retains a concrete canonical factory.
508    ///
509    /// - Postcondition: [`element_type`](Self::element_type) returns `element_type` unchanged, so
510    ///   the result describes an array whose elements have exactly that type.
511    ///
512    /// # Examples
513    ///
514    /// ```rust
515    /// use cel_parser::ResolvedArrayType;
516    /// use cel_runtime::ValueType;
517    ///
518    /// let array = ResolvedArrayType::from_element_type(ValueType::leaf::<i32>());
519    /// assert_eq!(array.element_type().type_name(), "i32");
520    /// ```
521    #[must_use]
522    pub fn from_element_type(element_type: ValueType) -> Self {
523        Self { element_type }
524    }
525
526    /// Creates a resolved array type from a fully resolved recursive type expression.
527    ///
528    /// # Errors
529    ///
530    /// Returns `Err` if `resolved` is not an array type or its layout is unrepresentable.
531    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    /// Returns the recursive element descriptor this array annotation resolves to.
550    #[must_use]
551    pub fn element_type(&self) -> &cel_runtime::ValueType {
552        &self.element_type
553    }
554
555    /// Returns the recursive element descriptor, consuming `self`.
556    #[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    /// Registers foreign leaves without orphan impls or mandatory capabilities.
578    #[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    /// Stores and moves leaves that implement none of the optional capability traits.
649    #[test]
650    fn complete_factory_does_not_require_capability_traits() {
651        use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
652        /// Owns one move-only payload without clone, equality, or formatting.
653        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    /// Composes complete array-of-tuple metadata with nested arrays.
665    #[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    /// Keeps source unit distinct from an explicitly registered opaque host unit.
698    #[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    /// Built-ins retain cloning, equality, and formatting in complete metadata.
714    #[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        // `proc_macro2::TokenStream::from_str` tokenizes delimiters into a matched tree up
1095        // front, so a truly unclosed `(` is rejected at tokenization (a `LexError`), before
1096        // `parse_type_expression`'s own "expected ',' or closing ')'" check ever runs — see
1097        // `parse_named_type_expr_rejects_arguments_missing_a_separating_comma` for that case.
1098        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}