use std::{error, fmt, future::Future, io, pin::Pin, task::Context, task::Poll};
use ntex_service::{Ctx, Service};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Either<A, B> {
Left(A),
Right(B),
}
impl<A, B> Either<A, B> {
fn project(self: Pin<&mut Self>) -> Either<Pin<&mut A>, Pin<&mut B>> {
unsafe {
match self.get_unchecked_mut() {
Either::Left(a) => Either::Left(Pin::new_unchecked(a)),
Either::Right(b) => Either::Right(Pin::new_unchecked(b)),
}
}
}
#[inline]
pub fn is_left(&self) -> bool {
match *self {
Either::Left(_) => true,
Either::Right(_) => false,
}
}
#[inline]
pub fn is_right(&self) -> bool {
!self.is_left()
}
#[inline]
pub fn left(self) -> Option<A> {
match self {
Either::Left(l) => Some(l),
Either::Right(_) => None,
}
}
#[inline]
pub fn right(self) -> Option<B> {
match self {
Either::Left(_) => None,
Either::Right(r) => Some(r),
}
}
#[inline]
pub fn as_ref(&self) -> Either<&A, &B> {
match *self {
Either::Left(ref inner) => Either::Left(inner),
Either::Right(ref inner) => Either::Right(inner),
}
}
#[inline]
pub fn as_mut(&mut self) -> Either<&mut A, &mut B> {
match *self {
Either::Left(ref mut inner) => Either::Left(inner),
Either::Right(ref mut inner) => Either::Right(inner),
}
}
}
impl<T> Either<T, T> {
#[inline]
pub fn into_inner(self) -> T {
match self {
Either::Left(x) | Either::Right(x) => x,
}
}
}
impl<A, B> error::Error for Either<A, B>
where
A: error::Error,
B: error::Error,
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Either::Left(a) => a.source(),
Either::Right(b) => b.source(),
}
}
}
impl<A, B> fmt::Display for Either<A, B>
where
A: fmt::Display,
B: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Either::Left(a) => a.fmt(f),
Either::Right(b) => b.fmt(f),
}
}
}
impl<A, B> Future for Either<A, B>
where
A: Future,
B: Future<Output = A::Output>,
{
type Output = A::Output;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project() {
Either::Left(x) => x.poll(cx),
Either::Right(x) => x.poll(cx),
}
}
}
impl<E: error::Error> From<Either<E, io::Error>> for io::Error {
fn from(err: Either<E, io::Error>) -> Self {
match err {
Either::Left(e) => io::Error::other(format!("{e:?}")),
Either::Right(e) => e,
}
}
}
impl<A, B, St, Req> Service<St, Req> for Either<A, B>
where
A: Service<St, Req>,
B: Service<St, Req, Res = A::Res, Error = A::Error>,
{
type Res = A::Res;
type Error = A::Error;
async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
match self {
Either::Left(svc) => ctx.call(svc, req).await,
Either::Right(svc) => ctx.call(svc, req).await,
}
}
async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
match self {
Either::Left(svc) => ctx.ready(svc).await,
Either::Right(svc) => ctx.ready(svc).await,
}
}
async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
match self {
Either::Left(svc) => ctx.shutdown(svc).await,
Either::Right(svc) => ctx.shutdown(svc).await,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[allow(clippy::unit_cmp)]
fn either() {
let mut e = Either::<(), ()>::Left(());
assert!(e.is_left());
assert!(!e.is_right());
assert!(e.left().is_some());
assert!(e.right().is_none());
e.as_ref();
e.as_mut();
let mut e = Either::<(), ()>::Right(());
assert!(!e.is_left());
assert!(e.is_right());
assert!(e.left().is_none());
assert!(e.right().is_some());
e.as_ref();
e.as_mut();
assert_eq!(Either::<(), ()>::Left(()).into_inner(), ());
assert_eq!(Either::<(), ()>::Right(()).into_inner(), ());
assert_eq!(
format!("{}", Either::<_, &'static str>::Left("test")),
"test"
);
assert_eq!(
format!("{}", Either::<&'static str, _>::Right("test")),
"test"
);
}
}