use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::envelope::EventEnvelope;
#[derive(Clone, Debug, thiserror::Error)]
#[error("({status}) {message}")]
pub struct AppError {
status: i32,
message: String,
}
impl AppError {
pub fn new(status: i32, message: impl Into<String>) -> Self {
AppError {
status,
message: message.into(),
}
}
pub fn status(&self) -> i32 {
self.status
}
pub fn message(&self) -> &str {
&self.message
}
}
#[async_trait]
pub trait ComposableFunction: Send + Sync {
async fn handle_event(
&self,
headers: HashMap<String, String>,
input: EventEnvelope,
instance: usize,
) -> Result<EventEnvelope, AppError>;
}
#[async_trait]
pub trait TypedFunction<I, O>: Send + Sync
where
I: DeserializeOwned + Send,
O: Serialize,
{
async fn handle_event(
&self,
headers: HashMap<String, String>,
input: I,
instance: usize,
) -> Result<O, AppError>;
}
pub struct TypedAdapter<T, I, O> {
inner: T,
_marker: PhantomData<fn(I) -> O>,
}
impl<T, I, O> TypedAdapter<T, I, O> {
pub fn new(inner: T) -> Self {
TypedAdapter {
inner,
_marker: PhantomData,
}
}
pub fn arc(inner: T) -> Arc<dyn ComposableFunction>
where
T: TypedFunction<I, O> + 'static,
I: DeserializeOwned + Send + Sync + 'static,
O: Serialize + Send + Sync + 'static,
{
Arc::new(TypedAdapter::new(inner))
}
}
#[async_trait]
impl<T, I, O> ComposableFunction for TypedAdapter<T, I, O>
where
T: TypedFunction<I, O>,
I: DeserializeOwned + Send + Sync,
O: Serialize + Send + Sync,
{
async fn handle_event(
&self,
headers: HashMap<String, String>,
input: EventEnvelope,
instance: usize,
) -> Result<EventEnvelope, AppError> {
let typed_input: I = input
.body_as()
.map_err(|e| AppError::new(400, format!("unable to map input: {e}")))?;
let output = self
.inner
.handle_event(headers, typed_input, instance)
.await?;
EventEnvelope::new().set_body(output)
}
}
pub struct NoOpFunction;
#[async_trait]
impl ComposableFunction for NoOpFunction {
async fn handle_event(
&self,
headers: HashMap<String, String>,
input: EventEnvelope,
_instance: usize,
) -> Result<EventEnvelope, AppError> {
let mut response = EventEnvelope::new();
for (key, value) in &headers {
response = response.set_header(key, value);
}
Ok(response.set_raw_body(input.body().clone()))
}
}