//! Field guards use crate::{Context, FieldResult}; use serde::export::PhantomData; /// Field guard /// /// Guard is a pre-condition for a field that is resolved if `Ok(())` is returned, otherwise an error is returned. /// /// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro. #[async_trait::async_trait] pub trait Guard { /// Check whether the guard will allow access to the field. async fn check(&self, ctx: &Context<'_>) -> FieldResult<()>; } /// An extension trait for `Guard`. pub trait GuardExt: Guard + Sized { /// Merge the two guards. fn and(self, other: R) -> And { And(self, other) } } impl GuardExt for T {} /// Guard for [`GuardExt::and`](trait.GuardExt.html#method.and). pub struct And(A, B); #[async_trait::async_trait] impl Guard for And { async fn check(&self, ctx: &Context<'_>) -> FieldResult<()> { self.0.check(ctx).await?; self.1.check(ctx).await } } /// Field post guard /// /// This is a post-condition for a field that is resolved if `Ok(()` is returned, otherwise an error is returned. /// /// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro. #[async_trait::async_trait] pub trait PostGuard { /// Check whether to allow the result of the field through. async fn check(&self, ctx: &Context<'_>, result: &T) -> FieldResult<()>; } /// An extension trait for `PostGuard` pub trait PostGuardExt: PostGuard + Sized { /// Merge the two guards. fn and>(self, other: R) -> PostAnd { PostAnd(self, other, PhantomData) } } impl, R: Send + Sync> PostGuardExt for T {} /// PostGuard for [`PostGuardExt::and`](trait.PostGuardExt.html#method.and). pub struct PostAnd, B: PostGuard>(A, B, PhantomData); #[async_trait::async_trait] impl + Send + Sync, B: PostGuard + Send + Sync> PostGuard for PostAnd { async fn check(&self, ctx: &Context<'_>, result: &T) -> FieldResult<()> { self.0.check(ctx, result).await?; self.1.check(ctx, result).await } }