Skip to main content

cel_core/
env.rs

1//! Unified environment for CEL expression processing.
2//!
3//! The `Env` struct coordinates parse, check, and (future) eval operations,
4//! following the cel-go architecture pattern.
5
6use std::collections::HashMap;
7use std::fmt;
8use std::sync::Arc;
9
10/// Error when creating abbreviations.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum AbbrevError {
13    /// Two qualified names map to the same short name.
14    Conflict {
15        short_name: String,
16        existing: String,
17        new: String,
18    },
19    /// The qualified name is empty or invalid.
20    InvalidName(String),
21}
22
23impl fmt::Display for AbbrevError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            AbbrevError::Conflict {
27                short_name,
28                existing,
29                new,
30            } => {
31                write!(
32                    f,
33                    "abbreviation conflict: '{}' already maps to '{}', cannot map to '{}'",
34                    short_name, existing, new
35                )
36            }
37            AbbrevError::InvalidName(name) => {
38                write!(f, "invalid qualified name: '{}'", name)
39            }
40        }
41    }
42}
43
44impl std::error::Error for AbbrevError {}
45
46/// A validated set of abbreviations mapping short names to fully-qualified names.
47///
48/// Abbreviations allow short names to be used instead of fully-qualified
49/// type names in CEL expressions. This is useful when working with protobuf
50/// types from multiple packages.
51///
52/// # Example
53///
54/// ```
55/// use cel_core::Abbreviations;
56///
57/// let abbrevs = Abbreviations::new()
58///     .with_abbreviation("my.package.Foo").unwrap()     // "Foo" -> "my.package.Foo"
59///     .with_abbreviation("other.package.Bar").unwrap(); // "Bar" -> "other.package.Bar"
60///
61/// assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
62/// assert_eq!(abbrevs.resolve("Bar"), Some("other.package.Bar"));
63/// ```
64#[derive(Debug, Clone, Default)]
65pub struct Abbreviations {
66    map: HashMap<String, String>, // short_name -> fully_qualified
67}
68
69impl Abbreviations {
70    /// Create an empty abbreviations set.
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    /// Add an abbreviation for a qualified name.
76    ///
77    /// The short name is derived from the last segment of the qualified name.
78    /// Returns an error if the short name conflicts with an existing abbreviation.
79    ///
80    /// # Example
81    ///
82    /// ```
83    /// use cel_core::Abbreviations;
84    ///
85    /// let abbrevs = Abbreviations::new()
86    ///     .with_abbreviation("my.package.Foo").unwrap();
87    ///
88    /// assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
89    /// ```
90    pub fn with_abbreviation(mut self, qualified_name: &str) -> Result<Self, AbbrevError> {
91        if qualified_name.is_empty() {
92            return Err(AbbrevError::InvalidName(qualified_name.to_string()));
93        }
94
95        let short_name = qualified_name
96            .rsplit('.')
97            .next()
98            .unwrap_or(qualified_name)
99            .to_string();
100
101        if short_name.is_empty() {
102            return Err(AbbrevError::InvalidName(qualified_name.to_string()));
103        }
104
105        if let Some(existing) = self.map.get(&short_name) {
106            if existing != qualified_name {
107                return Err(AbbrevError::Conflict {
108                    short_name,
109                    existing: existing.clone(),
110                    new: qualified_name.to_string(),
111                });
112            }
113            // Same mapping already exists - idempotent, no error
114        } else {
115            self.map.insert(short_name, qualified_name.to_string());
116        }
117
118        Ok(self)
119    }
120
121    /// Create abbreviations from a slice of qualified names.
122    ///
123    /// # Example
124    ///
125    /// ```
126    /// use cel_core::Abbreviations;
127    ///
128    /// let abbrevs = Abbreviations::from_qualified_names(&[
129    ///     "my.package.Foo",
130    ///     "other.package.Bar",
131    /// ]).unwrap();
132    /// ```
133    pub fn from_qualified_names(names: &[&str]) -> Result<Self, AbbrevError> {
134        let mut abbrevs = Self::new();
135        for name in names {
136            abbrevs = abbrevs.with_abbreviation(name)?;
137        }
138        Ok(abbrevs)
139    }
140
141    /// Get the underlying map of short names to fully-qualified names.
142    pub fn as_map(&self) -> &HashMap<String, String> {
143        &self.map
144    }
145
146    /// Resolve a short name to its fully qualified name.
147    ///
148    /// Returns `None` if the short name is not in the abbreviations set.
149    pub fn resolve(&self, short_name: &str) -> Option<&str> {
150        self.map.get(short_name).map(|s| s.as_str())
151    }
152
153    /// Check if the abbreviations set is empty.
154    pub fn is_empty(&self) -> bool {
155        self.map.is_empty()
156    }
157
158    /// Get the number of abbreviations.
159    pub fn len(&self) -> usize {
160        self.map.len()
161    }
162}
163
164use crate::ast::Ast;
165use crate::checker::{
166    check, check_with_abbreviations, check_with_type_resolver,
167    check_with_type_resolver_and_abbreviations, CheckError, CheckResult, STANDARD_LIBRARY,
168};
169use crate::eval::{Function, FunctionRegistry, Overload, Program, ProtoRegistry};
170use crate::ext;
171use crate::parser::{self, ParseError, ParseResult};
172use crate::types::{CelType, FunctionDecl, SpannedExpr};
173
174/// Error from compiling a CEL expression.
175#[derive(Debug, Clone)]
176pub enum CompileError {
177    /// Parse errors occurred.
178    Parse(Vec<ParseError>),
179    /// Type checking errors occurred.
180    Check(Vec<CheckError>),
181    /// No AST was produced (should not happen normally).
182    NoAst,
183}
184
185impl std::fmt::Display for CompileError {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        match self {
188            CompileError::Parse(errors) => {
189                write!(f, "parse errors: ")?;
190                for (i, e) in errors.iter().enumerate() {
191                    if i > 0 {
192                        write!(f, "; ")?;
193                    }
194                    write!(f, "{}", e.message)?;
195                }
196                Ok(())
197            }
198            CompileError::Check(errors) => {
199                write!(f, "check errors: ")?;
200                for (i, e) in errors.iter().enumerate() {
201                    if i > 0 {
202                        write!(f, "; ")?;
203                    }
204                    write!(f, "{}", e.message())?;
205                }
206                Ok(())
207            }
208            CompileError::NoAst => write!(f, "no AST produced"),
209        }
210    }
211}
212
213impl std::error::Error for CompileError {}
214
215/// Unified environment for CEL expression processing.
216///
217/// The `Env` wraps the parser and checker, providing a high-level API
218/// for working with CEL expressions. It manages:
219/// - Variable declarations with their types
220/// - Function declarations (standard library + extensions)
221/// - Container namespace for qualified name resolution
222/// - Abbreviations for type name shortcuts
223///
224/// # Example
225///
226/// ```
227/// use cel_core::Env;
228/// use cel_core::CelType;
229///
230/// let env = Env::with_standard_library()
231///     .with_variable("x", CelType::Int);
232///
233/// let result = env.compile("x + 1");
234/// assert!(result.is_ok());
235/// ```
236#[derive(Debug, Clone)]
237pub struct Env {
238    /// Variable declarations (name -> type).
239    variables: HashMap<String, CelType>,
240    /// Function declarations indexed by name.
241    functions: HashMap<String, FunctionDecl>,
242    /// Container namespace for qualified name resolution.
243    container: String,
244    /// Type registry for resolving protobuf types.
245    proto_registry: Option<Arc<dyn ProtoRegistry>>,
246    /// Abbreviations for qualified name shortcuts.
247    abbreviations: Abbreviations,
248    /// Whether to use strong enum typing (default: true).
249    /// When false, enum values are returned as plain integers (legacy behavior).
250    strong_enums: bool,
251}
252
253impl Env {
254    /// Create a new empty environment.
255    ///
256    /// This environment has no functions or variables defined.
257    /// Use `with_standard_library()` for a fully-featured environment.
258    pub fn new() -> Self {
259        Self {
260            variables: HashMap::new(),
261            functions: HashMap::new(),
262            container: String::new(),
263            proto_registry: None,
264            abbreviations: Abbreviations::new(),
265            strong_enums: true,
266        }
267    }
268
269    /// Create a new environment with the CEL standard library.
270    ///
271    /// This includes all standard operators, functions, and type constants.
272    pub fn with_standard_library() -> Self {
273        let mut env = Self::new();
274
275        // Add standard library functions
276        for func in STANDARD_LIBRARY.iter() {
277            env.functions.insert(func.name.clone(), func.clone());
278        }
279
280        // Add type constants (type values for type checking)
281        env.add_type_constant("bool", CelType::Bool);
282        env.add_type_constant("int", CelType::Int);
283        env.add_type_constant("uint", CelType::UInt);
284        env.add_type_constant("double", CelType::Double);
285        env.add_type_constant("string", CelType::String);
286        env.add_type_constant("bytes", CelType::Bytes);
287        env.add_type_constant("list", CelType::list(CelType::Dyn));
288        env.add_type_constant("map", CelType::map(CelType::Dyn, CelType::Dyn));
289        env.add_type_constant("null_type", CelType::Null);
290        env.add_type_constant("type", CelType::type_of(CelType::Dyn));
291        env.add_type_constant("dyn", CelType::Dyn);
292
293        env
294    }
295
296    /// Add a type constant to the environment.
297    fn add_type_constant(&mut self, name: &str, cel_type: CelType) {
298        self.variables
299            .insert(name.to_string(), CelType::type_of(cel_type));
300    }
301
302    /// Add a variable to the environment (builder pattern).
303    ///
304    /// # Example
305    ///
306    /// ```
307    /// use cel_core::Env;
308    /// use cel_core::CelType;
309    ///
310    /// let env = Env::with_standard_library()
311    ///     .with_variable("x", CelType::Int)
312    ///     .with_variable("y", CelType::String);
313    /// ```
314    pub fn with_variable(mut self, name: impl Into<String>, cel_type: CelType) -> Self {
315        self.variables.insert(name.into(), cel_type);
316        self
317    }
318
319    /// Add a variable to the environment (mutable).
320    pub fn add_variable(&mut self, name: impl Into<String>, cel_type: CelType) {
321        self.variables.insert(name.into(), cel_type);
322    }
323
324    /// Add a function declaration to the environment (builder pattern).
325    pub fn with_function(mut self, decl: FunctionDecl) -> Self {
326        self.add_function(decl);
327        self
328    }
329
330    /// Add a function declaration to the environment (mutable).
331    ///
332    /// If a function with the same name already exists, overloads are merged.
333    pub fn add_function(&mut self, decl: FunctionDecl) {
334        if let Some(existing) = self.functions.get_mut(&decl.name) {
335            // Merge overloads
336            existing.overloads.extend(decl.overloads);
337        } else {
338            self.functions.insert(decl.name.clone(), decl);
339        }
340    }
341
342    /// Set the container namespace (builder pattern).
343    ///
344    /// The container is used for qualified name resolution.
345    pub fn with_container(mut self, container: impl Into<String>) -> Self {
346        self.container = container.into();
347        self
348    }
349
350    /// Set the container namespace (mutable).
351    pub fn set_container(&mut self, container: impl Into<String>) {
352        self.container = container.into();
353    }
354
355    /// Get the container namespace.
356    pub fn container(&self) -> &str {
357        &self.container
358    }
359
360    /// Set the proto registry (builder pattern).
361    ///
362    /// The proto registry is used for resolving protobuf types during type checking
363    /// and evaluation. Pass an `Arc<dyn ProtoRegistry>` implementation such as
364    /// `ProstProtoRegistry` from `cel-core-proto`.
365    pub fn with_proto_registry(mut self, registry: Arc<dyn ProtoRegistry>) -> Self {
366        self.proto_registry = Some(registry);
367        self
368    }
369
370    /// Get the proto registry.
371    pub fn proto_registry(&self) -> Option<&dyn ProtoRegistry> {
372        self.proto_registry.as_ref().map(|r| r.as_ref())
373    }
374
375    /// Set abbreviations for qualified name resolution (builder pattern).
376    ///
377    /// Abbreviations allow short names to be used instead of fully-qualified
378    /// type names in expressions. This is useful when working with protobuf
379    /// types from multiple packages.
380    ///
381    /// # Example
382    ///
383    /// ```
384    /// use cel_core::{Env, Abbreviations};
385    ///
386    /// let abbrevs = Abbreviations::new()
387    ///     .with_abbreviation("google.protobuf.Duration").unwrap();
388    ///
389    /// let env = Env::with_standard_library()
390    ///     .with_abbreviations(abbrevs);
391    /// ```
392    pub fn with_abbreviations(mut self, abbreviations: Abbreviations) -> Self {
393        self.abbreviations = abbreviations;
394        self
395    }
396
397    /// Get the abbreviations.
398    pub fn abbreviations(&self) -> &Abbreviations {
399        &self.abbreviations
400    }
401
402    /// Use legacy (weak) enum mode where enum values are returned as plain integers.
403    ///
404    /// By default, strong enum typing is enabled and enum values carry their
405    /// fully-qualified type name. In legacy mode, enum values are represented
406    /// as `Value::Int`, matching older CEL behavior.
407    pub fn with_legacy_enums(mut self) -> Self {
408        self.strong_enums = false;
409        self
410    }
411
412    /// Check if strong enum typing is enabled.
413    pub fn strong_enums(&self) -> bool {
414        self.strong_enums
415    }
416
417    /// Add an extension library to the environment (builder pattern).
418    ///
419    /// Extensions provide additional functions beyond the standard library.
420    /// Each extension is a collection of `FunctionDecl` values.
421    ///
422    /// # Example
423    ///
424    /// ```
425    /// use cel_core::Env;
426    /// use cel_core::ext::string_extension;
427    ///
428    /// let env = Env::with_standard_library()
429    ///     .with_extension(string_extension());
430    /// ```
431    pub fn with_extension(mut self, extension: impl IntoIterator<Item = FunctionDecl>) -> Self {
432        for decl in extension {
433            self.add_function(decl);
434        }
435        self
436    }
437
438    /// Add all available extension libraries to the environment (builder pattern).
439    ///
440    /// This is a convenience method that adds all standard extensions:
441    /// - String extension (`charAt`, `indexOf`, `substring`, etc.)
442    /// - Math extension (`math.greatest`, `math.least`, `math.abs`, etc.)
443    /// - Encoders extension (`base64.encode`, `base64.decode`)
444    /// - Optionals extension (`optional.of`, `optional.none`, `hasValue`, etc.)
445    ///
446    /// # Example
447    ///
448    /// ```
449    /// use cel_core::Env;
450    ///
451    /// let env = Env::with_standard_library()
452    ///     .with_all_extensions();
453    /// ```
454    pub fn with_all_extensions(mut self) -> Self {
455        self = self
456            .with_extension(ext::string_extension())
457            .with_extension(ext::math_extension())
458            .with_extension(ext::encoders_extension())
459            .with_extension(ext::optionals_extension());
460
461        // Add optional_type type constant for the optionals extension
462        self.add_type_constant("optional_type", CelType::optional(CelType::Dyn));
463
464        self
465    }
466
467    /// Get the variables map.
468    pub fn variables(&self) -> &HashMap<String, CelType> {
469        &self.variables
470    }
471
472    /// Get the functions map.
473    pub fn functions(&self) -> &HashMap<String, FunctionDecl> {
474        &self.functions
475    }
476
477    /// Get all member functions compatible with a given receiver type.
478    ///
479    /// Returns (function_name, overload) pairs where at least one member overload
480    /// has a receiver type that is assignable from the given type.
481    /// Handles generic type parameters (e.g., `list(T)`) by treating them as wildcards.
482    pub fn methods_for_type(&self, receiver: &CelType) -> Vec<(&str, &crate::types::OverloadDecl)> {
483        let mut results = Vec::new();
484        for func in self.functions.values() {
485            if !func.has_member_overloads() {
486                continue;
487            }
488            for overload in &func.overloads {
489                if !overload.is_member {
490                    continue;
491                }
492                if let Some(recv_type) = overload.receiver_type() {
493                    if type_compatible_for_completion(recv_type, receiver) {
494                        results.push((func.name.as_str(), overload));
495                        break; // One match per function is enough
496                    }
497                }
498            }
499        }
500        results
501    }
502
503    /// Get all standalone (non-operator) function names.
504    ///
505    /// Filters out internal operator functions (like `_+_`, `_-_`) and other
506    /// internal functions that start with `_` or contain `@`.
507    pub fn standalone_functions(&self) -> Vec<&str> {
508        self.functions
509            .values()
510            .filter(|f| {
511                f.has_standalone_overloads() && !f.name.starts_with('_') && !f.name.contains('@')
512            })
513            .map(|f| f.name.as_str())
514            .collect()
515    }
516
517    /// Parse a CEL expression.
518    ///
519    /// This delegates to the parser. The returned `ParseResult` may contain
520    /// both a partial AST and errors if parsing partially succeeded.
521    pub fn parse(&self, source: &str) -> ParseResult {
522        parser::parse(source)
523    }
524
525    /// Type-check a parsed expression.
526    ///
527    /// This delegates to the checker with the environment's variables,
528    /// functions, container, and abbreviations.
529    pub fn check(&self, expr: &SpannedExpr) -> CheckResult {
530        let has_proto_registry = self.proto_registry.is_some();
531        let has_abbreviations = !self.abbreviations.is_empty();
532
533        match (has_proto_registry, has_abbreviations) {
534            (true, true) => check_with_type_resolver_and_abbreviations(
535                expr,
536                &self.variables,
537                &self.functions,
538                &self.container,
539                self.proto_registry.as_ref().unwrap().as_ref(),
540                self.abbreviations.as_map(),
541            ),
542            (true, false) => check_with_type_resolver(
543                expr,
544                &self.variables,
545                &self.functions,
546                &self.container,
547                self.proto_registry.as_ref().unwrap().as_ref(),
548            ),
549            (false, true) => check_with_abbreviations(
550                expr,
551                &self.variables,
552                &self.functions,
553                &self.container,
554                self.abbreviations.as_map(),
555            ),
556            (false, false) => check(expr, &self.variables, &self.functions, &self.container),
557        }
558    }
559
560    /// Parse and type-check a CEL expression, returning a checked Ast.
561    ///
562    /// This is the primary entry point for compiling CEL expressions.
563    /// Returns a checked `Ast` that can be used for evaluation.
564    ///
565    /// # Example
566    ///
567    /// ```
568    /// use cel_core::Env;
569    /// use cel_core::CelType;
570    ///
571    /// let env = Env::with_standard_library()
572    ///     .with_variable("x", CelType::Int);
573    ///
574    /// let ast = env.compile("x + 1").unwrap();
575    /// assert!(ast.is_checked());
576    /// assert_eq!(ast.result_type(), Some(&CelType::Int));
577    /// ```
578    pub fn compile(&self, source: &str) -> Result<Ast, CompileError> {
579        let parse_result = self.parse(source);
580
581        if !parse_result.errors.is_empty() {
582            return Err(CompileError::Parse(parse_result.errors));
583        }
584
585        let expr = parse_result.ast.ok_or(CompileError::NoAst)?;
586        let check_result = self.check(&expr);
587
588        if !check_result.errors.is_empty() {
589            return Err(CompileError::Check(check_result.errors));
590        }
591
592        Ok(Ast::new_checked(expr, source, check_result))
593    }
594
595    /// Parse a CEL expression without type-checking, returning an unchecked Ast.
596    ///
597    /// This is useful when you want to parse an expression but don't need
598    /// type information, or when you want to defer type-checking.
599    ///
600    /// # Example
601    ///
602    /// ```
603    /// use cel_core::Env;
604    ///
605    /// let env = Env::with_standard_library();
606    ///
607    /// let ast = env.parse_only("1 + 2").unwrap();
608    /// assert!(!ast.is_checked());
609    /// assert_eq!(ast.to_cel_string(), "1 + 2");
610    /// ```
611    pub fn parse_only(&self, source: &str) -> Result<Ast, CompileError> {
612        let parse_result = self.parse(source);
613
614        if !parse_result.errors.is_empty() {
615            return Err(CompileError::Parse(parse_result.errors));
616        }
617
618        let expr = parse_result.ast.ok_or(CompileError::NoAst)?;
619        Ok(Ast::new_unchecked(expr, source))
620    }
621
622    /// Create a program from a compiled AST.
623    ///
624    /// The program contains the AST and a function registry with implementations
625    /// for all functions registered in this environment.
626    ///
627    /// # Example
628    ///
629    /// ```
630    /// use cel_core::{Env, CelType};
631    /// use cel_core::{Value, MapActivation, Activation};
632    ///
633    /// let env = Env::with_standard_library()
634    ///     .with_variable("x", CelType::Int);
635    ///
636    /// let ast = env.compile("x + 1").unwrap();
637    /// let program = env.program(&ast).unwrap();
638    ///
639    /// let mut activation = MapActivation::new();
640    /// activation.insert("x", Value::Int(41));
641    ///
642    /// let result = program.eval(&activation);
643    /// assert_eq!(result, Value::Int(42));
644    /// ```
645    pub fn program(&self, ast: &Ast) -> Result<Program, CompileError> {
646        let registry = self.build_function_registry();
647        let has_proto_registry = self.proto_registry.is_some();
648        let has_abbreviations = !self.abbreviations.is_empty();
649
650        let mut program = match (has_proto_registry, has_abbreviations) {
651            (true, true) => Program::with_proto_registry_and_abbreviations(
652                Arc::new(ast.clone()),
653                Arc::new(registry),
654                Arc::clone(self.proto_registry.as_ref().unwrap()),
655                self.abbreviations.as_map().clone(),
656            ),
657            (true, false) => Program::with_proto_registry(
658                Arc::new(ast.clone()),
659                Arc::new(registry),
660                Arc::clone(self.proto_registry.as_ref().unwrap()),
661            ),
662            (false, true) => Program::with_abbreviations(
663                Arc::new(ast.clone()),
664                Arc::new(registry),
665                self.abbreviations.as_map().clone(),
666            ),
667            (false, false) => Program::new(Arc::new(ast.clone()), Arc::new(registry)),
668        };
669
670        if !self.strong_enums {
671            program = program.with_legacy_enums();
672        }
673
674        Ok(program)
675    }
676
677    /// Build the function registry from this environment's function declarations.
678    fn build_function_registry(&self) -> FunctionRegistry {
679        let mut registry = FunctionRegistry::new();
680
681        for func_decl in self.functions.values() {
682            let mut function = Function::new(&func_decl.name);
683
684            for overload_decl in &func_decl.overloads {
685                // Only add overloads that have implementations
686                if let Some(ref impl_fn) = overload_decl.implementation {
687                    let overload = Overload::new(
688                        &overload_decl.id,
689                        overload_decl.is_member,
690                        overload_decl.params.len(),
691                        impl_fn.clone(),
692                    );
693                    function = function.with_overload(overload);
694                }
695            }
696
697            // Only register functions that have at least one implemented overload
698            if !function.overloads.is_empty() {
699                registry.register(function);
700            }
701        }
702
703        registry
704    }
705}
706
707/// Check if a declared type (which may contain TypeParams) is compatible
708/// with a concrete receiver type for completion purposes.
709///
710/// This treats TypeParam as a wildcard that matches any type, unlike
711/// `is_assignable_from` which requires explicit substitution.
712fn type_compatible_for_completion(declared: &CelType, concrete: &CelType) -> bool {
713    // Regular assignability covers most cases
714    if declared.is_assignable_from(concrete) {
715        return true;
716    }
717    match (declared, concrete) {
718        // TypeParam matches anything (it's a generic placeholder)
719        (CelType::TypeParam(_), _) | (_, CelType::TypeParam(_)) => true,
720        // Recurse into list element types
721        (CelType::List(d), CelType::List(c)) => type_compatible_for_completion(d, c),
722        // Recurse into map key/value types
723        (CelType::Map(dk, dv), CelType::Map(ck, cv)) => {
724            type_compatible_for_completion(dk, ck) && type_compatible_for_completion(dv, cv)
725        }
726        // Recurse into optional inner types
727        (CelType::Optional(d), CelType::Optional(c)) => type_compatible_for_completion(d, c),
728        _ => false,
729    }
730}
731
732impl Default for Env {
733    fn default() -> Self {
734        Self::new()
735    }
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741    use crate::checker::CheckErrorKind;
742    use crate::types::OverloadDecl;
743
744    #[test]
745    fn test_new_env() {
746        let env = Env::new();
747        assert!(env.variables().is_empty());
748        assert!(env.functions().is_empty());
749    }
750
751    #[test]
752    fn test_with_standard_library() {
753        let env = Env::with_standard_library();
754
755        // Should have standard functions
756        assert!(env.functions().contains_key("_+_"));
757        assert!(env.functions().contains_key("size"));
758        assert!(env.functions().contains_key("contains"));
759
760        // Should have type constants
761        assert!(env.variables().contains_key("bool"));
762        assert!(env.variables().contains_key("int"));
763    }
764
765    #[test]
766    fn test_with_variable() {
767        let env = Env::with_standard_library().with_variable("x", CelType::Int);
768
769        assert!(env.variables().contains_key("x"));
770        assert_eq!(env.variables().get("x"), Some(&CelType::Int));
771    }
772
773    #[test]
774    fn test_parse() {
775        let env = Env::new();
776        let result = env.parse("1 + 2");
777
778        assert!(result.ast.is_some());
779        assert!(result.errors.is_empty());
780    }
781
782    #[test]
783    fn test_check() {
784        let env = Env::with_standard_library().with_variable("x", CelType::Int);
785
786        let parse_result = env.parse("x + 1");
787        let ast = parse_result.ast.unwrap();
788
789        let check_result = env.check(&ast);
790        assert!(check_result.is_ok());
791    }
792
793    #[test]
794    fn test_check_undefined_variable() {
795        let env = Env::with_standard_library();
796
797        let parse_result = env.parse("x + 1");
798        let ast = parse_result.ast.unwrap();
799
800        let check_result = env.check(&ast);
801        assert!(!check_result.is_ok());
802        assert!(check_result.errors.iter().any(|e| matches!(
803            &e.kind,
804            CheckErrorKind::UndeclaredReference { name, .. } if name == "x"
805        )));
806    }
807
808    #[test]
809    fn test_compile_success() {
810        let env = Env::with_standard_library().with_variable("x", CelType::Int);
811
812        let ast = env.compile("x + 1").unwrap();
813        assert!(ast.is_checked());
814    }
815
816    #[test]
817    fn test_compile_parse_error() {
818        let env = Env::with_standard_library();
819
820        let result = env.compile("1 +");
821        assert!(result.is_err());
822    }
823
824    #[test]
825    fn test_container() {
826        let env = Env::with_standard_library().with_container("google.protobuf");
827
828        assert_eq!(env.container(), "google.protobuf");
829    }
830
831    #[test]
832    fn test_add_function() {
833        let mut env = Env::new();
834
835        let func = FunctionDecl::new("custom").with_overload(OverloadDecl::function(
836            "custom_int",
837            vec![CelType::Int],
838            CelType::Bool,
839        ));
840
841        env.add_function(func);
842
843        assert!(env.functions().contains_key("custom"));
844    }
845
846    #[test]
847    fn test_with_extension() {
848        let env = Env::with_standard_library().with_extension(ext::string_extension());
849
850        // String extension should add charAt function
851        assert!(env.functions().contains_key("charAt"));
852        assert!(env.functions().contains_key("indexOf"));
853        assert!(env.functions().contains_key("substring"));
854    }
855
856    #[test]
857    fn test_with_all_extensions() {
858        let env = Env::with_standard_library().with_all_extensions();
859
860        // String extension functions
861        assert!(env.functions().contains_key("charAt"));
862        assert!(env.functions().contains_key("indexOf"));
863        assert!(env.functions().contains_key("join"));
864        assert!(env.functions().contains_key("strings.quote"));
865
866        // Math extension functions
867        assert!(env.functions().contains_key("math.greatest"));
868        assert!(env.functions().contains_key("math.least"));
869        assert!(env.functions().contains_key("math.abs"));
870        assert!(env.functions().contains_key("math.bitAnd"));
871
872        // Encoders extension functions
873        assert!(env.functions().contains_key("base64.encode"));
874        assert!(env.functions().contains_key("base64.decode"));
875
876        // Optionals extension functions
877        assert!(env.functions().contains_key("optional.of"));
878        assert!(env.functions().contains_key("optional.none"));
879        assert!(env.functions().contains_key("optional.ofNonZeroValue"));
880        assert!(env.functions().contains_key("hasValue"));
881        assert!(env.functions().contains_key("value"));
882        assert!(env.functions().contains_key("or"));
883        assert!(env.functions().contains_key("orValue"));
884    }
885
886    #[test]
887    fn test_encoders_extension_base64() {
888        let env = Env::with_standard_library()
889            .with_all_extensions()
890            .with_variable("data", CelType::Bytes)
891            .with_variable("encoded", CelType::String);
892
893        // base64.encode(bytes) -> string
894        let result = env.compile("base64.encode(data)");
895        assert!(result.is_ok(), "base64.encode should compile: {:?}", result);
896
897        // base64.decode(string) -> bytes
898        let result = env.compile("base64.decode(encoded)");
899        assert!(result.is_ok(), "base64.decode should compile: {:?}", result);
900    }
901
902    #[test]
903    fn test_string_extension_char_at() {
904        let env = Env::with_standard_library()
905            .with_all_extensions()
906            .with_variable("s", CelType::String);
907
908        let result = env.compile("s.charAt(0)");
909        assert!(result.is_ok(), "charAt should compile: {:?}", result);
910    }
911
912    #[test]
913    fn test_string_extension_index_of() {
914        let env = Env::with_standard_library()
915            .with_all_extensions()
916            .with_variable("s", CelType::String);
917
918        // Basic indexOf
919        assert!(env.compile("s.indexOf(\"a\")").is_ok());
920
921        // indexOf with offset
922        assert!(env.compile("s.indexOf(\"a\", 2)").is_ok());
923    }
924
925    #[test]
926    fn test_string_extension_substring() {
927        let env = Env::with_standard_library()
928            .with_all_extensions()
929            .with_variable("s", CelType::String);
930
931        // substring with one arg
932        assert!(env.compile("s.substring(1)").is_ok());
933
934        // substring with two args
935        assert!(env.compile("s.substring(1, 5)").is_ok());
936    }
937
938    #[test]
939    fn test_string_extension_split() {
940        let env = Env::with_standard_library()
941            .with_all_extensions()
942            .with_variable("s", CelType::String);
943
944        let ast = env.compile("s.split(\",\")").unwrap();
945        assert!(ast.is_checked());
946
947        // Result should be list<string>
948        // The root expr type should be list<string>
949    }
950
951    #[test]
952    fn test_string_extension_join() {
953        let env = Env::with_standard_library()
954            .with_all_extensions()
955            .with_variable("parts", CelType::list(CelType::String));
956
957        // join without separator
958        assert!(env.compile("parts.join()").is_ok());
959
960        // join with separator
961        assert!(env.compile("parts.join(\",\")").is_ok());
962    }
963
964    #[test]
965    fn test_math_extension_greatest() {
966        let env = Env::with_standard_library().with_all_extensions();
967
968        // Binary
969        assert!(env.compile("math.greatest(1, 2)").is_ok());
970
971        // Ternary
972        assert!(env.compile("math.greatest(1, 2, 3)").is_ok());
973
974        // With list
975        assert!(env.compile("math.greatest([1, 2, 3])").is_ok());
976    }
977
978    #[test]
979    fn test_math_extension_least() {
980        let env = Env::with_standard_library().with_all_extensions();
981        assert!(env.compile("math.least(1, 2)").is_ok());
982    }
983
984    #[test]
985    fn test_math_extension_abs() {
986        let env = Env::with_standard_library()
987            .with_all_extensions()
988            .with_variable("x", CelType::Int);
989
990        assert!(env.compile("math.abs(x)").is_ok());
991    }
992
993    #[test]
994    fn test_math_extension_bit_operations() {
995        let env = Env::with_standard_library()
996            .with_all_extensions()
997            .with_variable("a", CelType::Int)
998            .with_variable("b", CelType::Int);
999
1000        assert!(env.compile("math.bitAnd(a, b)").is_ok());
1001        assert!(env.compile("math.bitOr(a, b)").is_ok());
1002        assert!(env.compile("math.bitNot(a)").is_ok());
1003    }
1004
1005    #[test]
1006    fn test_optionals_extension_of_and_none() {
1007        let env = Env::with_standard_library()
1008            .with_all_extensions()
1009            .with_variable("x", CelType::Int);
1010
1011        // optional.of(x)
1012        let result = env.compile("optional.of(x)");
1013        assert!(result.is_ok(), "optional.of should compile: {:?}", result);
1014
1015        // optional.none()
1016        let result = env.compile("optional.none()");
1017        assert!(result.is_ok(), "optional.none should compile: {:?}", result);
1018
1019        // optional.ofNonZeroValue(x)
1020        let result = env.compile("optional.ofNonZeroValue(x)");
1021        assert!(
1022            result.is_ok(),
1023            "optional.ofNonZeroValue should compile: {:?}",
1024            result
1025        );
1026    }
1027
1028    #[test]
1029    fn test_cel_bind_macro() {
1030        let env = Env::with_standard_library().with_all_extensions();
1031
1032        // cel.bind(x, 10, x + 1) should work - bind x to 10, return x + 1
1033        let result = env.compile("cel.bind(x, 10, x + 1)");
1034        assert!(result.is_ok(), "cel.bind should compile: {:?}", result);
1035
1036        // cel.bind with string
1037        let result = env.compile("cel.bind(msg, \"hello\", msg + msg)");
1038        assert!(
1039            result.is_ok(),
1040            "cel.bind with string should compile: {:?}",
1041            result
1042        );
1043
1044        // Nested cel.bind
1045        let result = env.compile("cel.bind(x, 1, cel.bind(y, 2, x + y))");
1046        assert!(
1047            result.is_ok(),
1048            "nested cel.bind should compile: {:?}",
1049            result
1050        );
1051    }
1052
1053    #[test]
1054    fn test_optionals_extension_methods() {
1055        let env = Env::with_standard_library()
1056            .with_all_extensions()
1057            .with_variable("opt", CelType::optional(CelType::Int))
1058            .with_variable("opt2", CelType::optional(CelType::Int));
1059
1060        // opt.hasValue()
1061        let result = env.compile("opt.hasValue()");
1062        assert!(result.is_ok(), "hasValue should compile: {:?}", result);
1063
1064        // opt.value()
1065        let result = env.compile("opt.value()");
1066        assert!(result.is_ok(), "value should compile: {:?}", result);
1067
1068        // opt.or(opt2)
1069        let result = env.compile("opt.or(opt2)");
1070        assert!(result.is_ok(), "or should compile: {:?}", result);
1071
1072        // opt.orValue(42)
1073        let result = env.compile("opt.orValue(42)");
1074        assert!(result.is_ok(), "orValue should compile: {:?}", result);
1075    }
1076
1077    // =====================================================================
1078    // Abbreviations tests
1079    // =====================================================================
1080
1081    #[test]
1082    fn test_abbreviations_new() {
1083        let abbrevs = Abbreviations::new();
1084        assert!(abbrevs.is_empty());
1085        assert_eq!(abbrevs.len(), 0);
1086    }
1087
1088    #[test]
1089    fn test_abbreviations_add() {
1090        let abbrevs = Abbreviations::new()
1091            .with_abbreviation("my.package.Foo")
1092            .unwrap();
1093
1094        assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
1095        assert_eq!(abbrevs.len(), 1);
1096    }
1097
1098    #[test]
1099    fn test_abbreviations_multiple() {
1100        let abbrevs = Abbreviations::new()
1101            .with_abbreviation("my.package.Foo")
1102            .unwrap()
1103            .with_abbreviation("other.package.Bar")
1104            .unwrap();
1105
1106        assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
1107        assert_eq!(abbrevs.resolve("Bar"), Some("other.package.Bar"));
1108        assert_eq!(abbrevs.len(), 2);
1109    }
1110
1111    #[test]
1112    fn test_abbreviations_unqualified_name() {
1113        // Single-segment name is valid
1114        let abbrevs = Abbreviations::new().with_abbreviation("Foo").unwrap();
1115        assert_eq!(abbrevs.resolve("Foo"), Some("Foo"));
1116    }
1117
1118    #[test]
1119    fn test_abbreviations_conflict() {
1120        let result = Abbreviations::new()
1121            .with_abbreviation("my.package.Foo")
1122            .unwrap()
1123            .with_abbreviation("other.package.Foo"); // Same short name!
1124
1125        assert!(result.is_err());
1126        match result {
1127            Err(AbbrevError::Conflict {
1128                short_name,
1129                existing,
1130                new,
1131            }) => {
1132                assert_eq!(short_name, "Foo");
1133                assert_eq!(existing, "my.package.Foo");
1134                assert_eq!(new, "other.package.Foo");
1135            }
1136            _ => panic!("Expected Conflict error"),
1137        }
1138    }
1139
1140    #[test]
1141    fn test_abbreviations_idempotent() {
1142        // Adding the same mapping twice is OK (idempotent)
1143        let abbrevs = Abbreviations::new()
1144            .with_abbreviation("my.package.Foo")
1145            .unwrap()
1146            .with_abbreviation("my.package.Foo") // Same mapping again
1147            .unwrap();
1148
1149        assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
1150        assert_eq!(abbrevs.len(), 1);
1151    }
1152
1153    #[test]
1154    fn test_abbreviations_empty_name() {
1155        let result = Abbreviations::new().with_abbreviation("");
1156        assert!(result.is_err());
1157        match result {
1158            Err(AbbrevError::InvalidName(name)) => {
1159                assert_eq!(name, "");
1160            }
1161            _ => panic!("Expected InvalidName error"),
1162        }
1163    }
1164
1165    #[test]
1166    fn test_abbreviations_trailing_dot() {
1167        // A name ending with a dot has an empty last segment
1168        let result = Abbreviations::new().with_abbreviation("my.package.");
1169        assert!(result.is_err());
1170        match result {
1171            Err(AbbrevError::InvalidName(_)) => {}
1172            _ => panic!("Expected InvalidName error"),
1173        }
1174    }
1175
1176    #[test]
1177    fn test_abbreviations_from_qualified_names() {
1178        let abbrevs =
1179            Abbreviations::from_qualified_names(&["my.package.Foo", "other.package.Bar"]).unwrap();
1180
1181        assert_eq!(abbrevs.resolve("Foo"), Some("my.package.Foo"));
1182        assert_eq!(abbrevs.resolve("Bar"), Some("other.package.Bar"));
1183    }
1184
1185    #[test]
1186    fn test_abbreviations_resolve_nonexistent() {
1187        let abbrevs = Abbreviations::new()
1188            .with_abbreviation("my.package.Foo")
1189            .unwrap();
1190
1191        assert_eq!(abbrevs.resolve("Bar"), None);
1192    }
1193
1194    #[test]
1195    fn test_methods_for_type_string() {
1196        let env = Env::with_standard_library();
1197        let methods = env.methods_for_type(&CelType::String);
1198        let names: Vec<&str> = methods.iter().map(|(name, _)| *name).collect();
1199        assert!(names.contains(&"contains"));
1200        assert!(names.contains(&"startsWith"));
1201        assert!(names.contains(&"endsWith"));
1202        assert!(names.contains(&"matches"));
1203        assert!(names.contains(&"size"));
1204        // Operators should not appear
1205        assert!(!names.contains(&"_+_"));
1206        assert!(!names.contains(&"_==_"));
1207    }
1208
1209    #[test]
1210    fn test_methods_for_type_list() {
1211        let env = Env::with_standard_library();
1212        let methods = env.methods_for_type(&CelType::list(CelType::Int));
1213        let names: Vec<&str> = methods.iter().map(|(name, _)| *name).collect();
1214        assert!(names.contains(&"size"));
1215    }
1216
1217    #[test]
1218    fn test_standalone_functions() {
1219        let env = Env::with_standard_library();
1220        let funcs = env.standalone_functions();
1221        assert!(funcs.contains(&"size"));
1222        assert!(funcs.contains(&"int"));
1223        assert!(funcs.contains(&"string"));
1224        assert!(funcs.contains(&"bool"));
1225        // Operators should not appear
1226        assert!(!funcs.contains(&"_+_"));
1227        assert!(!funcs.contains(&"_-_"));
1228        assert!(!funcs.contains(&"_==_"));
1229    }
1230
1231    #[test]
1232    fn test_env_with_abbreviations() {
1233        let abbrevs = Abbreviations::new()
1234            .with_abbreviation("my.package.Foo")
1235            .unwrap();
1236
1237        let env = Env::with_standard_library().with_abbreviations(abbrevs);
1238
1239        assert!(!env.abbreviations().is_empty());
1240        assert_eq!(env.abbreviations().resolve("Foo"), Some("my.package.Foo"));
1241    }
1242
1243    #[test]
1244    fn test_abbrev_error_display() {
1245        let err = AbbrevError::Conflict {
1246            short_name: "Foo".to_string(),
1247            existing: "a.Foo".to_string(),
1248            new: "b.Foo".to_string(),
1249        };
1250        assert_eq!(
1251            err.to_string(),
1252            "abbreviation conflict: 'Foo' already maps to 'a.Foo', cannot map to 'b.Foo'"
1253        );
1254
1255        let err = AbbrevError::InvalidName("".to_string());
1256        assert_eq!(err.to_string(), "invalid qualified name: ''");
1257    }
1258}