use crate::engine::error::{DataflowError, Result};
use crate::engine::task_context::TaskContext;
use crate::engine::task_outcome::TaskOutcome;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::any::Any;
pub mod config;
pub use config::{
BUILTIN_FUNCTION_NAMES, BuiltinKind, CompiledCustomInput, ConnectorName, DispatchableFunction,
FunctionConfig, builtin_function_kind, is_builtin_function,
};
pub mod validation;
pub use validation::{ValidationConfig, ValidationRule};
pub mod map;
pub use map::{MapConfig, MapMapping};
pub mod parse;
pub use parse::ParseConfig;
pub mod publish;
pub use publish::PublishConfig;
pub mod filter;
pub use filter::{FilterConfig, RejectAction};
pub mod log;
pub use log::{LogConfig, LogLevel};
pub mod integration;
pub use integration::{EnrichConfig, HttpCallConfig, HttpMethod, PublishKafkaConfig};
pub mod template;
pub use template::{Template, TemplateCompiler};
pub mod path_template;
pub use path_template::{ContextRoot, DataRoot, PathRoot, PathTemplate, ResolvedPath};
#[async_trait]
pub trait AsyncFunctionHandler: Send + Sync + 'static {
type Input: DeserializeOwned + Send + Sync + 'static;
fn parse_input(input: &Value) -> Result<Self::Input> {
serde_json::from_value(input.clone()).map_err(DataflowError::from_serde)
}
fn parse_input_with(&self, input: &Value) -> Result<Self::Input> {
<Self as AsyncFunctionHandler>::parse_input(input)
}
fn compile_input(_input: &mut Self::Input, _c: &TemplateCompiler) -> Result<()> {
Ok(())
}
fn compile_input_with(&self, input: &mut Self::Input, c: &TemplateCompiler) -> Result<()> {
<Self as AsyncFunctionHandler>::compile_input(input, c)
}
async fn execute(&self, ctx: &mut TaskContext<'_>, input: &Self::Input) -> Result<TaskOutcome>;
}
#[doc(hidden)]
#[async_trait]
pub trait DynAsyncFunctionHandler: Send + Sync + 'static {
fn parse_input_box(&self, input: &Value) -> Result<Box<dyn Any + Send + Sync>>;
fn compile_input_box(
&self,
_boxed: &mut (dyn Any + Send + Sync),
_c: &TemplateCompiler,
) -> Result<()> {
Ok(())
}
async fn dyn_execute(
&self,
ctx: &mut TaskContext<'_>,
input: &(dyn Any + Send + Sync),
) -> Result<TaskOutcome>;
}
#[async_trait]
impl<F: AsyncFunctionHandler> DynAsyncFunctionHandler for F {
fn parse_input_box(&self, input: &Value) -> Result<Box<dyn Any + Send + Sync>> {
let typed = <F as AsyncFunctionHandler>::parse_input_with(self, input)?;
Ok(Box::new(typed))
}
fn compile_input_box(
&self,
boxed: &mut (dyn Any + Send + Sync),
c: &TemplateCompiler,
) -> Result<()> {
let typed = boxed.downcast_mut::<F::Input>().ok_or_else(|| {
DataflowError::Validation(format!(
"Handler input type mismatch (expected {})",
std::any::type_name::<F::Input>()
))
})?;
<F as AsyncFunctionHandler>::compile_input_with(self, typed, c)
}
async fn dyn_execute(
&self,
ctx: &mut TaskContext<'_>,
input: &(dyn Any + Send + Sync),
) -> Result<TaskOutcome> {
let typed = input.downcast_ref::<F::Input>().ok_or_else(|| {
DataflowError::Validation(format!(
"Handler input type mismatch (expected {})",
std::any::type_name::<F::Input>()
))
})?;
AsyncFunctionHandler::execute(self, ctx, typed).await
}
}
pub type BoxedFunctionHandler = Box<dyn DynAsyncFunctionHandler + Send + Sync>;
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::compiler::LogicCompiler;
struct BothForms;
#[async_trait]
impl AsyncFunctionHandler for BothForms {
type Input = Value;
fn parse_input(_input: &Value) -> Result<Self::Input> {
Err(DataflowError::Validation(
"the associated parse_input must not be reached".to_string(),
))
}
fn parse_input_with(&self, input: &Value) -> Result<Self::Input> {
Ok(input.clone())
}
fn compile_input(_input: &mut Self::Input, _c: &TemplateCompiler) -> Result<()> {
Err(DataflowError::Validation(
"the associated compile_input must not be reached".to_string(),
))
}
fn compile_input_with(
&self,
_input: &mut Self::Input,
_c: &TemplateCompiler,
) -> Result<()> {
Ok(())
}
async fn execute(
&self,
_ctx: &mut TaskContext<'_>,
_input: &Self::Input,
) -> Result<TaskOutcome> {
Ok(TaskOutcome::Success)
}
}
#[test]
fn the_boxed_dispatch_calls_the_receiver_forms_and_never_the_associated_ones() {
let handler: BoxedFunctionHandler = Box::new(BothForms);
let compiler = TemplateCompiler::new(LogicCompiler::new().engine());
let mut parsed = handler
.parse_input_box(&Value::Null)
.expect("parse_input_box routes to parse_input_with");
handler
.compile_input_box(&mut *parsed, &compiler)
.expect("compile_input_box routes to compile_input_with");
}
}