use crate::Context;
pub trait Service<Req, Resp> {
type Error: std::error::Error;
fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error>;
}
pub struct ServiceFn<F, Resp, E>
where
E: std::error::Error,
{
f: F,
_marker: std::marker::PhantomData<(Resp, E)>,
}
impl<F, Resp, E> ServiceFn<F, Resp, E>
where
E: std::error::Error,
{
pub fn new<Req>(f: F) -> Self
where
F: Fn(&Context, Req) -> Result<Resp, E>,
{
Self {
f,
_marker: std::marker::PhantomData,
}
}
}
impl<F, Req, Resp, E> Service<Req, Resp> for ServiceFn<F, Resp, E>
where
F: Fn(&Context, Req) -> Result<Resp, E>,
E: std::error::Error,
{
type Error = E;
fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
(self.f)(ctx, req)
}
}
#[cfg(feature = "async")]
use async_trait::async_trait;
#[cfg(feature = "async")]
#[async_trait]
pub trait AsyncService<Req, Resp>: Send + Sync
where
Req: Send,
Resp: Send,
{
type Error: std::error::Error + Send;
async fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error>;
}
#[cfg(feature = "async")]
#[async_trait]
impl<S, Req, Resp> AsyncService<Req, Resp> for S
where
S: Service<Req, Resp> + Send + Sync,
S::Error: Send,
Req: Send + 'static,
Resp: Send,
{
type Error = S::Error;
async fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
Service::execute(self, ctx, req)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CodedError;
#[test]
fn service_fn_basic() {
let service =
ServiceFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let result = Service::execute(&service, &Context::new(), 21);
assert_eq!(result.ok(), Some(42));
}
#[test]
fn service_fn_error() {
let service = ServiceFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> {
if req < 0 {
Err(CodedError::new("NEGATIVE_VALUE"))
} else {
Ok(req)
}
});
let result = Service::execute(&service, &Context::new(), -1);
assert!(result.is_err());
}
#[test]
fn service_fn_with_context() {
let service = ServiceFn::new(|ctx: &Context, _req: ()| -> Result<bool, CodedError> {
Ok(ctx.is_done())
});
let ctx = Context::new();
let result = Service::execute(&service, &ctx, ());
assert_eq!(result.ok(), Some(false));
}
}