use crate::Context;
use crate::service::Service;
use crate::validator::Validator;
pub trait Handler<Req, Resp> {
type Error: std::error::Error;
fn handle(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error>;
}
#[derive(Clone)]
pub struct HandlerFn<F, Resp, E>
where
E: std::error::Error,
{
f: F,
_marker: std::marker::PhantomData<(Resp, E)>,
}
impl<F, Resp, E> HandlerFn<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> Handler<Req, Resp> for HandlerFn<F, Resp, E>
where
F: Fn(&Context, Req) -> Result<Resp, E>,
E: std::error::Error,
{
type Error = E;
fn handle(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
(self.f)(ctx, req)
}
}
#[derive(Debug)]
pub enum ValidatingHandlerError<VE, SE> {
Validation(VE),
Service(SE),
}
impl<VE, SE> std::fmt::Display for ValidatingHandlerError<VE, SE>
where
VE: std::fmt::Display,
SE: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Validation(e) => write!(f, "validation error: {e}"),
Self::Service(e) => write!(f, "service error: {e}"),
}
}
}
impl<VE, SE> std::error::Error for ValidatingHandlerError<VE, SE>
where
VE: std::error::Error + 'static,
SE: std::error::Error + 'static,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Validation(e) => Some(e),
Self::Service(e) => Some(e),
}
}
}
pub struct ValidatingHandler<V, S, Req, Resp>
where
V: Validator<Req>,
S: Service<Req, Resp>,
{
pub validator: V,
pub service: S,
_marker: std::marker::PhantomData<(Req, Resp)>,
}
impl<V, S, Req, Resp> ValidatingHandler<V, S, Req, Resp>
where
V: Validator<Req>,
S: Service<Req, Resp>,
{
pub fn new(validator: V, service: S) -> Self {
Self {
validator,
service,
_marker: std::marker::PhantomData,
}
}
}
impl<V, S, Req, Resp> Handler<Req, Resp> for ValidatingHandler<V, S, Req, Resp>
where
V: Validator<Req>,
V::Error: 'static,
S: Service<Req, Resp>,
S::Error: 'static,
{
type Error = ValidatingHandlerError<V::Error, S::Error>;
fn handle(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
self.validator
.validate(ctx, &req)
.map_err(ValidatingHandlerError::Validation)?;
self.service
.execute(ctx, req)
.map_err(ValidatingHandlerError::Service)
}
}
#[cfg(feature = "async")]
use async_trait::async_trait;
#[cfg(feature = "async")]
#[async_trait]
pub trait AsyncHandler<Req, Resp>: Send + Sync
where
Req: Send,
Resp: Send,
{
type Error: std::error::Error + Send;
async fn handle(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error>;
}
#[cfg(feature = "async")]
#[async_trait]
impl<H, Req, Resp> AsyncHandler<Req, Resp> for H
where
H: Handler<Req, Resp> + Send + Sync,
H::Error: Send,
Req: Send + 'static,
Resp: Send,
{
type Error = H::Error;
async fn handle(&self, ctx: &Context, req: Req) -> Result<Resp, Self::Error> {
Handler::handle(self, ctx, req)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CodedError, ServiceFn, ValidatorFn};
#[test]
fn handler_fn_basic() {
let handler =
HandlerFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let result = Handler::handle(&handler, &Context::new(), 21);
assert_eq!(result.ok(), Some(42));
}
#[test]
fn validating_handler_with_valid_request() {
let validator = ValidatorFn::new(|_ctx: &Context, req: &i32| -> Result<(), CodedError> {
if *req > 0 {
Ok(())
} else {
Err(CodedError::new("INVALID"))
}
});
let service =
ServiceFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let handler = ValidatingHandler::new(validator, service);
let result = Handler::handle(&handler, &Context::new(), 21);
assert_eq!(result.ok(), Some(42));
}
#[test]
fn validating_handler_with_invalid_request() {
let validator = ValidatorFn::new(|_ctx: &Context, req: &i32| -> Result<(), CodedError> {
if *req > 0 {
Ok(())
} else {
Err(CodedError::new("INVALID"))
}
});
let service =
ServiceFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let handler = ValidatingHandler::new(validator, service);
let result = Handler::handle(&handler, &Context::new(), -1);
assert!(result.is_err());
}
#[test]
fn validating_handler_no_validator() {
let service =
ServiceFn::new(|_ctx: &Context, req: i32| -> Result<i32, CodedError> { Ok(req * 2) });
let handler = ValidatingHandler::new(crate::PassValidator, service);
let result = Handler::handle(&handler, &Context::new(), 21);
assert_eq!(result.ok(), Some(42));
}
}