use core::{convert::Infallible, fmt};
use crate::error::BoxError;
use crate::std::{boxed::Box, sync::Arc};
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
mod concurrent;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[doc(inline)]
pub use concurrent::{ConcurrentCounter, ConcurrentPolicy, ConcurrentTracker, LimitReached};
mod matcher;
pub struct PolicyResult<Input, Guard, Error> {
pub input: Input,
pub output: PolicyOutput<Guard, Error>,
}
impl<Input: fmt::Debug, Guard: fmt::Debug, Error: fmt::Debug> fmt::Debug
for PolicyResult<Input, Guard, Error>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PolicyResult")
.field("input", &self.input)
.field("output", &self.output)
.finish()
}
}
pub enum PolicyOutput<Guard, Error> {
Ready(Guard),
Abort(Error),
Retry,
}
impl<Guard: fmt::Debug, Error: fmt::Debug> fmt::Debug for PolicyOutput<Guard, Error> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ready(guard) => write!(f, "PolicyOutput::Ready({guard:?})"),
Self::Abort(error) => write!(f, "PolicyOutput::Abort({error:?})"),
Self::Retry => write!(f, "PolicyOutput::Retry"),
}
}
}
pub trait Policy<Input>: Send + Sync + 'static {
type Guard: Send + 'static;
type Error: Send + 'static;
fn check(
&self,
input: Input,
) -> impl Future<Output = PolicyResult<Input, Self::Guard, Self::Error>> + Send + '_;
}
impl<Input, P> Policy<Input> for Option<P>
where
P: Policy<Input>,
Input: Send + 'static,
{
type Guard = Option<P::Guard>;
type Error = P::Error;
async fn check(&self, input: Input) -> PolicyResult<Input, Self::Guard, Self::Error> {
match self {
Some(policy) => {
let result = policy.check(input).await;
match result.output {
PolicyOutput::Ready(guard) => PolicyResult {
input: result.input,
output: PolicyOutput::Ready(Some(guard)),
},
PolicyOutput::Abort(err) => PolicyResult {
input: result.input,
output: PolicyOutput::Abort(err),
},
PolicyOutput::Retry => PolicyResult {
input: result.input,
output: PolicyOutput::Retry,
},
}
}
None => PolicyResult {
input,
output: PolicyOutput::Ready(None),
},
}
}
}
impl<Input, P> Policy<Input> for &'static P
where
P: Policy<Input>,
Input: Send + 'static,
{
type Guard = P::Guard;
type Error = P::Error;
#[inline(always)]
async fn check(&self, input: Input) -> PolicyResult<Input, Self::Guard, Self::Error> {
(**self).check(input).await
}
}
impl<Input, P> Policy<Input> for Arc<P>
where
P: Policy<Input>,
Input: Send + 'static,
{
type Guard = P::Guard;
type Error = P::Error;
async fn check(&self, input: Input) -> PolicyResult<Input, Self::Guard, Self::Error> {
self.as_ref().check(input).await
}
}
impl<Input, P> Policy<Input> for Box<P>
where
P: Policy<Input>,
Input: Send + 'static,
{
type Guard = P::Guard;
type Error = P::Error;
async fn check(&self, input: Input) -> PolicyResult<Input, Self::Guard, Self::Error> {
self.as_ref().check(input).await
}
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct UnlimitedPolicy;
impl UnlimitedPolicy {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl<Input> Policy<Input> for UnlimitedPolicy
where
Input: Send + 'static,
{
type Guard = ();
type Error = Infallible;
async fn check(&self, input: Input) -> PolicyResult<Input, Self::Guard, Self::Error> {
PolicyResult {
input,
output: PolicyOutput::Ready(()),
}
}
}
macro_rules! impl_limit_policy_either {
($id:ident, $($param:ident),+ $(,)?) => {
impl<$($param),+, Input> Policy<Input> for crate::combinators::$id<$($param),+>
where
$(
$param: Policy<Input>,
$param::Error: Into<BoxError>,
)+
Input: Send + 'static,
{
type Guard = crate::combinators::$id<$($param::Guard),+>;
type Error = BoxError;
async fn check(
&self,
req: Input,
) -> PolicyResult<Input, Self::Guard, Self::Error> {
match self {
$(
crate::combinators::$id::$param(policy) => {
let result = policy.check(req).await;
match result.output {
PolicyOutput::Ready(guard) => PolicyResult {
input: result.input,
output: PolicyOutput::Ready(crate::combinators::$id::$param(guard)),
},
PolicyOutput::Abort(err) => PolicyResult {
input: result.input,
output: PolicyOutput::Abort(err.into()),
},
PolicyOutput::Retry => PolicyResult {
input: result.input,
output: PolicyOutput::Retry,
},
}
}
)+
}
}
}
};
}
crate::combinators::impl_either!(impl_limit_policy_either);