mod decimal;
mod package;
mod timestamp;
use std::{borrow::Cow, collections::HashMap};
use es_entity::clock::{Clock, ClockHandle};
use crate::{builtins, cel_type::CelType, error::*, value::*};
use package::CelPackage;
const SELF_PACKAGE_NAME: Cow<'static, str> = Cow::Borrowed("self");
type CelFunction = Box<dyn Fn(&CelContext, Vec<CelValue>) -> Result<CelValue, CelError> + Sync>;
pub(crate) type CelMemberFunction =
Box<dyn Fn(&CelValue, Vec<CelValue>) -> Result<CelValue, CelError> + Sync>;
pub struct CelContext {
idents: HashMap<Cow<'static, str>, ContextItem>,
clock: ClockHandle,
}
impl CelContext {
pub fn add_variable(&mut self, name: impl Into<Cow<'static, str>>, value: impl Into<CelValue>) {
self.idents
.insert(name.into(), ContextItem::Value(value.into()));
}
pub(crate) fn clock(&self) -> &ClockHandle {
&self.clock
}
pub fn debug_context(&self) -> String {
let vars: Vec<_> = self
.idents
.iter()
.filter_map(|(name, item)| match item {
ContextItem::Value(val) => Some(format!("{}={:?}", name, val)),
_ => None,
})
.collect();
if vars.is_empty() {
String::new()
} else {
vars.join(", ")
}
}
pub fn new() -> Self {
Self::new_with_clock(Clock::handle().clone())
}
pub fn new_with_clock(clock: ClockHandle) -> Self {
let mut idents = HashMap::new();
idents.insert(
Cow::Borrowed("date"),
ContextItem::Function(Box::new(|ctx, args| builtins::date(ctx, args))),
);
idents.insert(
Cow::Borrowed("uuid"),
ContextItem::Function(Box::new(|_ctx, args| builtins::uuid(args))),
);
idents.insert(
Cow::Borrowed("decimal"),
ContextItem::Package(&decimal::CEL_PACKAGE),
);
idents.insert(
Cow::Borrowed("timestamp"),
ContextItem::Package(×tamp::CEL_PACKAGE),
);
Self { idents, clock }
}
pub(crate) fn lookup_ident(&self, name: &str) -> Result<&ContextItem, CelError> {
self.idents
.get(name)
.ok_or_else(|| CelError::UnknownIdent(name.to_string()))
}
pub(crate) fn lookup_member_fn(
&self,
value: &CelValue,
name: &str,
) -> Result<&CelMemberFunction, CelError> {
let package_name = CelType::from(value).package_name();
let package = if let Some(ContextItem::Package(package)) = self.idents.get(package_name) {
package
} else {
return Err(CelError::UnknownPackage(package_name));
};
package.lookup_member(value, name)
}
}
impl Default for CelContext {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for CelContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CelContext")
.field("idents", &self.idents)
.field("clock", &self.clock)
.finish()
}
}
pub(crate) enum ContextItem {
Value(CelValue),
Function(CelFunction),
Package(&'static CelPackage),
}
impl std::fmt::Debug for ContextItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ContextItem::Value(val) => write!(f, "Value({val:?})"),
ContextItem::Function(_) => write!(f, "Function"),
ContextItem::Package(_) => write!(f, "Package"),
}
}
}