use crate::Context;
pub trait Provider<Req, Resp> {
type Error: std::error::Error;
fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error>;
}
pub struct ProviderFn<F, Resp, E>
where
E: std::error::Error,
{
f: F,
_marker: std::marker::PhantomData<(Resp, E)>,
}
impl<F, Resp, E> ProviderFn<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> Provider<Req, Resp> for ProviderFn<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 AsyncProvider<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<P, Req, Resp> AsyncProvider<Req, Resp> for P
where
P: Provider<Req, Resp> + Send + Sync,
P::Error: Send,
Req: Send + 'static,
Resp: Send,
{
type Error = P::Error;
async fn execute(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
Provider::execute(self, ctx, req)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CodedError;
#[test]
fn provider_fn_basic() {
let provider =
ProviderFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let result = Provider::execute(&provider, &Context::new(), 21);
assert_eq!(result.ok(), Some(42));
}
#[test]
fn provider_fn_error() {
let provider = ProviderFn::new(|_ctx: &Context, _req: ()| -> Result<(), CodedError> {
Err(CodedError::new("DB_ERROR").with_reason("connection failed"))
});
let result = Provider::execute(&provider, &Context::new(), ());
assert!(result.is_err());
}
#[test]
fn provider_fn_with_context_deadline() {
use std::time::Duration;
let provider = ProviderFn::new(|ctx: &Context, _req: ()| -> Result<bool, CodedError> {
Ok(ctx.deadline().is_some())
});
let ctx = Context::with_timeout(Duration::from_secs(30));
let result = Provider::execute(&provider, &ctx, ());
assert_eq!(result.ok(), Some(true));
}
}