use crate::{
error::{Error, Result},
value::Value,
};
use async_trait::async_trait;
use displaydoc::Display as DisplayDoc;
use std::{collections::HashMap, result};
#[async_trait]
pub trait UserFunction {
async fn call(&self, params: Value) -> FunctionResult;
}
#[derive(Debug, DisplayDoc, thiserror::Error)]
pub enum FunctionError {
InvalidParameter(Value, String),
Unspecified(#[from] anyhow::Error),
}
impl From<Error> for FunctionError {
fn from(error: Error) -> Self {
match error {
Error::UnexpectedValueType(value, expected) => {
FunctionError::InvalidParameter(value, expected)
}
err => FunctionError::Unspecified(err.into()),
}
}
}
pub type FunctionResult = result::Result<Value, FunctionError>;
#[derive(Default)]
pub struct UserFunctions {
functions: HashMap<String, Box<dyn UserFunction + Send + Sync>>,
}
impl UserFunctions {
pub async fn call(&self, name: &str, params: Value) -> Result<Value> {
match self.functions.get(name) {
Some(fun) => fun
.call(params)
.await
.map_err(|err| Error::UserFunctionError(name.to_owned(), err)),
None => Err(Error::UnknownUserFunction(name.to_owned())),
}
}
pub fn add<F: UserFunction + Send + Sync + 'static>(&mut self, name: &str, function: F) {
self.functions.insert(name.to_owned(), Box::new(function));
}
}
impl<'a> From<&'a UserFunctions> for FunctionContext<'a> {
fn from(functions: &'a UserFunctions) -> Self {
Self { functions }
}
}
pub(crate) struct FunctionContext<'a> {
functions: &'a UserFunctions,
}
impl<'a> FunctionContext<'a> {
pub(crate) async fn call(&mut self, function: &str, params: Value) -> Result<Value> {
self.functions.call(function, params).await
}
}