use crate::common::types::{CelBool, CelBytes, CelDouble, CelInt, CelNull, CelString, CelUInt};
#[cfg(feature = "chrono")]
use crate::common::types::{CelDuration, CelTimestamp};
use crate::common::value::{CowVal, FromVal as DowncastFrom, Val};
use crate::macros::{impl_conversions, impl_handler};
use crate::objects::Opaque;
use crate::resolvers::AllArguments;
use crate::{ExecutionError, FunctionContext, Value};
use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
impl_conversions!(
i64 => Value::Int as CelInt,
u64 => Value::UInt as CelUInt,
f64 => Value::Float as CelDouble,
Arc<String> => Value::String as CelString,
Arc<Vec<u8>> => Value::Bytes as CelBytes,
bool => Value::Bool as CelBool,
);
#[cfg(feature = "chrono")]
impl_conversions!(
chrono::Duration => Value::Duration as CelDuration,
chrono::DateTime<chrono::FixedOffset> => Value::Timestamp as CelTimestamp,
);
impl From<i32> for Value {
fn from(value: i32) -> Self {
Value::Int(value as i64)
}
}
impl From<u32> for Value {
fn from(value: u32) -> Self {
Value::UInt(value as u64)
}
}
impl From<f32> for Value {
fn from(value: f32) -> Self {
Value::Float(value as f64)
}
}
pub(crate) trait FromVal: Sized {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError>;
}
fn downcast_or_unexpected<'a, 'v, T: DowncastFrom<'a, 'v>>(
value: &'a (dyn Val + 'v),
want: &str,
) -> Result<&'a T, ExecutionError> {
value
.downcast_ref::<T>()
.ok_or_else(|| ExecutionError::UnexpectedType {
got: format!("{value:?}"),
want: want.to_string(),
})
}
impl FromVal for Value {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
value.try_into()
}
}
impl FromVal for i64 {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(*downcast_or_unexpected::<CelInt>(value, "i64")?.inner())
}
}
impl FromVal for u64 {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(*downcast_or_unexpected::<CelUInt>(value, "u64")?.inner())
}
}
impl FromVal for f64 {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(*downcast_or_unexpected::<CelDouble>(value, "f64")?.inner())
}
}
impl FromVal for bool {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(*downcast_or_unexpected::<CelBool>(value, "bool")?.inner())
}
}
impl FromVal for Arc<String> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(Arc::new(
downcast_or_unexpected::<CelString>(value, "Arc<String>")?
.inner()
.to_string(),
))
}
}
impl FromVal for Arc<Vec<u8>> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(Arc::new(
downcast_or_unexpected::<CelBytes>(value, "Arc<Vec<u8>>")?
.inner()
.to_vec(),
))
}
}
#[cfg(feature = "chrono")]
impl FromVal for chrono::Duration {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(*downcast_or_unexpected::<CelDuration>(value, "chrono::Duration")?.inner())
}
}
#[cfg(feature = "chrono")]
impl FromVal for chrono::DateTime<chrono::FixedOffset> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
Ok(
*downcast_or_unexpected::<CelTimestamp>(
value,
"chrono::DateTime<chrono::FixedOffset>",
)?
.inner(),
)
}
}
impl FromVal for Arc<Vec<Value>> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
match Value::from_val(value)? {
Value::List(list) => Ok(list),
_ => Err(ExecutionError::UnexpectedType {
got: format!("{value:?}"),
want: "Arc<Vec<Value>>".to_string(),
}),
}
}
}
impl FromVal for Arc<dyn Opaque> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
match Value::from_val(value)? {
Value::Opaque(opaque) => Ok(opaque),
_ => Err(ExecutionError::UnexpectedType {
got: format!("{value:?}"),
want: "Arc<dyn Opaque>".to_string(),
}),
}
}
}
impl<T: FromVal> FromVal for Option<T> {
fn from_val(value: &dyn Val) -> Result<Self, ExecutionError> {
if value.downcast_ref::<CelNull>().is_some() {
Ok(None)
} else {
T::from_val(value).map(Some)
}
}
}
impl From<Arc<Vec<Value>>> for Value {
fn from(value: Arc<Vec<Value>>) -> Self {
Value::List(value)
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for Arc<Vec<Value>> {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
Value::List(self).into_resolve_result()
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call>
for Result<Arc<Vec<Value>>, ExecutionError>
{
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
self?.into_resolve_result()
}
}
impl<'a, 'context, 'call> FromContext<'a, 'context, 'call> for Arc<Vec<Value>> {
fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
where
Self: Sized,
{
arg_val_from_context(ctx).and_then(|v| FromVal::from_val(v.as_ref()))
}
}
impl From<Arc<dyn Opaque>> for Value {
fn from(value: Arc<dyn Opaque>) -> Self {
Value::Opaque(value)
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for Arc<dyn Opaque> {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
Value::Opaque(self).into_resolve_result()
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call>
for Result<Arc<dyn Opaque>, ExecutionError>
{
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
self?.into_resolve_result()
}
}
impl<'a, 'context, 'call> FromContext<'a, 'context, 'call> for Arc<dyn Opaque> {
fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
where
Self: Sized,
{
arg_val_from_context(ctx).and_then(|v| FromVal::from_val(v.as_ref()))
}
}
pub trait IntoResolveResult<'context, 'call> {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError>;
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for String {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
Ok(CowVal::owned(CelString::from(self)))
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for Value {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
Ok(CowVal::Owned(self.try_into()?))
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for Result<Value, ExecutionError> {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
self?.into_resolve_result()
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call> for CowVal<'context, 'call> {
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
Ok(self)
}
}
impl<'context, 'call> IntoResolveResult<'context, 'call>
for Result<CowVal<'context, 'call>, ExecutionError>
{
fn into_resolve_result(self) -> Result<CowVal<'context, 'call>, ExecutionError> {
self
}
}
pub(crate) trait FromContext<'a, 'context, 'call> {
fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
where
Self: Sized;
}
pub struct This<T>(pub T);
impl<'a, 'context, 'call, T> FromContext<'a, 'context, 'call> for This<T>
where
T: FromVal,
{
fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
where
Self: Sized,
{
if let Some(ref this) = ctx.this {
Ok(This(T::from_val(this.as_ref())?))
} else {
let arg = arg_val_from_context(ctx)
.map_err(|_| ExecutionError::missing_argument_or_target())?;
Ok(This(T::from_val(arg.as_ref())?))
}
}
}
#[derive(Clone)]
pub struct Identifier(pub Arc<String>);
impl From<&Identifier> for String {
fn from(value: &Identifier) -> Self {
value.0.to_string()
}
}
impl From<Identifier> for String {
fn from(value: Identifier) -> Self {
value.0.as_ref().clone()
}
}
#[derive(Clone)]
pub struct Arguments(pub Arc<Vec<Value>>);
impl<'a> FromContext<'a, '_, '_> for Arguments {
fn from_context(ctx: &'a mut FunctionContext) -> Result<Self, ExecutionError>
where
Self: Sized,
{
match ctx.resolve(AllArguments)? {
Value::List(list) => Ok(Arguments(list.clone())),
_ => todo!(),
}
}
}
impl<'a, 'context, 'call> FromContext<'a, 'context, 'call> for Value {
fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
where
Self: Sized,
{
arg_val_from_context(ctx).and_then(|v| FromVal::from_val(v.as_ref()))
}
}
pub(crate) fn arg_val_from_context<'context, 'call>(
ctx: &mut FunctionContext<'context, 'call>,
) -> Result<CowVal<'context, 'call>, ExecutionError> {
let idx = ctx.arg_idx;
ctx.arg_idx += 1;
ctx.args
.get(idx)
.cloned()
.ok_or_else(|| ExecutionError::invalid_argument_count(idx + 1, ctx.args.len()))
}
pub struct WithFunctionContext;
impl_handler!();
impl_handler!(C1);
impl_handler!(C1, C2);
impl_handler!(C1, C2, C3);
impl_handler!(C1, C2, C3, C4);
impl_handler!(C1, C2, C3, C4, C5);
impl_handler!(C1, C2, C3, C4, C5, C6);
impl_handler!(C1, C2, C3, C4, C5, C6, C7);
impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8);
impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8, C9);
#[derive(Default)]
pub struct FunctionRegistry {
functions: BTreeMap<String, Function>,
namespaces: BTreeSet<String>,
}
impl FunctionRegistry {
pub(crate) fn add<F, T>(&mut self, name: &str, function: F)
where
F: IntoFunction<T> + 'static + Send + Sync,
T: 'static,
{
self.functions
.insert(name.to_string(), function.into_function());
if let Some((namespace, _)) = name.split_once('.') {
self.namespaces.insert(namespace.to_owned());
}
}
pub(crate) fn has_namespace(&self, namespace: &str) -> bool {
self.namespaces.contains(namespace)
}
#[allow(dead_code)]
pub(crate) fn get(&self, name: &str) -> Option<&Function> {
self.functions.get(name)
}
}
pub type Function = Box<
dyn for<'context, 'call> Fn(
&mut FunctionContext<'context, 'call>,
) -> Result<CowVal<'context, 'call>, ExecutionError>
+ Send
+ Sync,
>;
pub trait IntoFunction<T> {
fn into_function(self) -> Function;
}
impl IntoFunction<Function> for Function {
fn into_function(self) -> Function {
self
}
}