Unverified Commit b17a8675 authored by Harry Barber's avatar Harry Barber Committed by GitHub
Browse files

Use a bespoke version of `Either` (#2441)

* Add Plugin impl for &Plugin

* Add bespoke Either implementation

* Use Either in FilterByOperationName

* Add CHANGELOG.next.toml

* Rename A and B to Left and Right

* Re-export Either
parent 9d31b6c4
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -245,3 +245,9 @@ message = "Remove unnecessary type parameter `B` from `Upgrade` service."
references = ["smithy-rs#2436"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" }
author = "hlbarber"

[[smithy-rs]]
message = "Fix `FilterByOperationName` plugin. This previous caused services with this applied to fail to compile due to mismatched bounds."
references = ["smithy-rs#2441"]
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" }
author = "hlbarber"
+132 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! Contains the [`Either`] enum.

use pin_project_lite::pin_project;
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};
use tower::{Layer, Service};

use crate::operation::Operation;

use super::Plugin;

pin_project! {
    /// Combine two different [`Future`]/[`Service`]/[`Layer`]/[`Plugin`] types into a single type.
    ///
    /// # Notes on [`Future`]
    ///
    /// The [`Future::Output`] must be identical.
    ///
    /// # Notes on [`Service`]
    ///
    /// The [`Service::Response`] and [`Service::Error`] must be identical.
    #[derive(Clone, Debug)]
    #[project = EitherProj]
    pub enum Either<L, R> {
        /// One type of backing [`Service`].
        Left { #[pin] value: L },
        /// The other type of backing [`Service`].
        Right { #[pin] value: R },
    }
}

impl<L, R> Future for Either<L, R>
where
    L: Future,
    R: Future<Output = L::Output>,
{
    type Output = L::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        match self.project() {
            EitherProj::Left { value } => value.poll(cx),
            EitherProj::Right { value } => value.poll(cx),
        }
    }
}

impl<L, R, Request> Service<Request> for Either<L, R>
where
    L: Service<Request>,
    R: Service<Request, Response = L::Response, Error = L::Error>,
{
    type Response = L::Response;
    type Error = L::Error;
    type Future = Either<L::Future, R::Future>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        use self::Either::*;

        match self {
            Left { value } => value.poll_ready(cx),
            Right { value } => value.poll_ready(cx),
        }
    }

    fn call(&mut self, request: Request) -> Self::Future {
        use self::Either::*;

        match self {
            Left { value } => Either::Left {
                value: value.call(request),
            },
            Right { value } => Either::Right {
                value: value.call(request),
            },
        }
    }
}

impl<S, L, R> Layer<S> for Either<L, R>
where
    L: Layer<S>,
    R: Layer<S>,
{
    type Service = Either<L::Service, R::Service>;

    fn layer(&self, inner: S) -> Self::Service {
        match self {
            Either::Left { value } => Either::Left {
                value: value.layer(inner),
            },
            Either::Right { value } => Either::Right {
                value: value.layer(inner),
            },
        }
    }
}

impl<P, Op, S, L, Le, Ri> Plugin<P, Op, S, L> for Either<Le, Ri>
where
    Le: Plugin<P, Op, S, L>,
    Ri: Plugin<P, Op, S, L>,
{
    type Service = Either<Le::Service, Ri::Service>;
    type Layer = Either<Le::Layer, Ri::Layer>;

    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
        match self {
            Either::Left { value } => {
                let Operation { inner, layer } = value.map(input);
                Operation {
                    inner: Either::Left { value: inner },
                    layer: Either::Left { value: layer },
                }
            }
            Either::Right { value } => {
                let Operation { inner, layer } = value.map(input);
                Operation {
                    inner: Either::Right { value: inner },
                    layer: Either::Right { value: layer },
                }
            }
        }
    }
}
+6 −12
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 * SPDX-License-Identifier: Apache-2.0
 */

use tower::util::Either;
use super::{either::Either, IdentityPlugin};

use crate::operation::{Operation, OperationShape};

@@ -60,17 +60,11 @@ where
    type Layer = Either<Inner::Layer, L>;

    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
        if (self.predicate)(Op::NAME) {
            let Operation { inner, layer } = self.inner.map(input);
            Operation {
                inner: Either::A(inner),
                layer: Either::A(layer),
            }
        let either_plugin = if (self.predicate)(Op::NAME) {
            Either::Left { value: &self.inner }
        } else {
            Operation {
                inner: Either::B(input.inner),
                layer: Either::B(input.layer),
            }
        }
            Either::Right { value: IdentityPlugin }
        };
        either_plugin.map(input)
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@
//!

mod closure;
mod either;
mod filter;
mod identity;
mod layer;
@@ -128,6 +129,7 @@ mod stack;
use crate::operation::Operation;

pub use closure::{plugin_from_operation_name_fn, OperationNameFn};
pub use either::Either;
pub use filter::{filter_by_operation_name, FilterByOperationName};
pub use identity::IdentityPlugin;
pub use layer::HttpLayer;
@@ -149,3 +151,15 @@ pub trait Plugin<Protocol, Op, S, L> {
    /// Maps an [`Operation`] to another.
    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer>;
}

impl<'a, P, Op, S, L, Pl> Plugin<P, Op, S, L> for &'a Pl
where
    Pl: Plugin<P, Op, S, L>,
{
    type Service = Pl::Service;
    type Layer = Pl::Layer;

    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
        <Pl as Plugin<P, Op, S, L>>::map(*self, input)
    }
}