cala_cel_interpreter/context/
mod.rs

1mod decimal;
2mod package;
3mod timestamp;
4
5use std::{borrow::Cow, collections::HashMap};
6
7use crate::{builtins, cel_type::CelType, error::*, value::*};
8
9use package::CelPackage;
10
11const SELF_PACKAGE_NAME: Cow<'static, str> = Cow::Borrowed("self");
12
13type CelFunction = Box<dyn Fn(Vec<CelValue>) -> Result<CelValue, CelError> + Sync>;
14pub(crate) type CelMemberFunction =
15    Box<dyn Fn(&CelValue, Vec<CelValue>) -> Result<CelValue, CelError> + Sync>;
16
17#[derive(Debug)]
18pub struct CelContext {
19    idents: HashMap<Cow<'static, str>, ContextItem>,
20}
21
22impl CelContext {
23    pub fn add_variable(&mut self, name: impl Into<Cow<'static, str>>, value: impl Into<CelValue>) {
24        self.idents
25            .insert(name.into(), ContextItem::Value(value.into()));
26    }
27
28    /// Returns a debug representation of all context variables with their values
29    /// Useful for tracing/debugging - excludes built-in functions and packages
30    pub fn debug_context(&self) -> String {
31        let vars: Vec<_> = self
32            .idents
33            .iter()
34            .filter_map(|(name, item)| match item {
35                ContextItem::Value(val) => Some(format!("{}={:?}", name, val)),
36                _ => None,
37            })
38            .collect();
39
40        if vars.is_empty() {
41            String::new()
42        } else {
43            vars.join(", ")
44        }
45    }
46
47    pub fn new() -> Self {
48        let mut idents = HashMap::new();
49        idents.insert(
50            Cow::Borrowed("date"),
51            ContextItem::Function(Box::new(builtins::date)),
52        );
53        idents.insert(
54            Cow::Borrowed("uuid"),
55            ContextItem::Function(Box::new(builtins::uuid)),
56        );
57        idents.insert(
58            Cow::Borrowed("decimal"),
59            ContextItem::Package(&decimal::CEL_PACKAGE),
60        );
61
62        idents.insert(
63            Cow::Borrowed("timestamp"),
64            ContextItem::Package(&timestamp::CEL_PACKAGE),
65        );
66
67        Self { idents }
68    }
69
70    pub(crate) fn lookup_ident(&self, name: &str) -> Result<&ContextItem, CelError> {
71        self.idents
72            .get(name)
73            .ok_or_else(|| CelError::UnknownIdent(name.to_string()))
74    }
75
76    pub(crate) fn lookup_member_fn(
77        &self,
78        value: &CelValue,
79        name: &str,
80    ) -> Result<&CelMemberFunction, CelError> {
81        let package_name = CelType::from(value).package_name();
82        let package = if let Some(ContextItem::Package(package)) = self.idents.get(package_name) {
83            package
84        } else {
85            return Err(CelError::UnknownPackage(package_name));
86        };
87
88        package.lookup_member(value, name)
89    }
90}
91impl Default for CelContext {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97pub(crate) enum ContextItem {
98    Value(CelValue),
99    Function(CelFunction),
100    Package(&'static CelPackage),
101}
102
103impl std::fmt::Debug for ContextItem {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            ContextItem::Value(val) => write!(f, "Value({val:?})"),
107            ContextItem::Function(_) => write!(f, "Function"),
108            ContextItem::Package(_) => write!(f, "Package"),
109        }
110    }
111}