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

Add PluginExt extension trait and FilterByOperationName plugin (#1837)

* Make Operation fields public

* Split into modules and add FilterByOperationName
parent 046edd54
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -193,8 +193,11 @@ pub use upgrade::*;
///
/// The `L` is held and applied lazily during [`Upgradable::upgrade`].
pub struct Operation<S, L = Identity> {
    inner: S,
    layer: L,
    /// The inner [`Service`](tower::Service) representing the logic of the operation.
    pub inner: S,
    /// The [`Layer`](tower::Layer) applied to the HTTP [`Service`](tower::Service) after `S` has been wrapped in
    /// [`Upgrade`].
    pub layer: L,
}

impl<S, L> Operation<S, L> {
+50 −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
 */

use tower::util::Either;

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

use super::Plugin;

/// A [`Plugin`] used to filter [`Plugin::map`] application using a predicate over the [`OperationShape::NAME`].
///
/// See [`PluginExt::filter_by_operation_name`](super::PluginExt::filter_by_operation_name) for more information.
pub struct FilterByOperationName<Inner, F> {
    inner: Inner,
    predicate: F,
}

impl<Inner, F> FilterByOperationName<Inner, F> {
    /// Creates a new [`FilterByOperationName`].
    pub(crate) fn new(inner: Inner, predicate: F) -> Self {
        Self { inner, predicate }
    }
}

impl<P, Op, S, L, Inner, F> Plugin<P, Op, S, L> for FilterByOperationName<Inner, F>
where
    F: Fn(&str) -> bool,
    Inner: Plugin<P, Op, S, L>,
    Op: OperationShape,
{
    type Service = Either<Inner::Service, S>;
    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),
            }
        } else {
            Operation {
                inner: Either::B(input.inner),
                layer: Either::B(input.layer),
            }
        }
    }
}
+20 −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
 */

use crate::operation::Operation;

use super::Plugin;

/// A [`Plugin`] that maps an `input` [`Operation`] to itself.
pub struct IdentityPlugin;

impl<P, Op, S, L> Plugin<P, Op, S, L> for IdentityPlugin {
    type Service = S;
    type Layer = L;

    fn map(&self, input: Operation<S, L>) -> Operation<S, L> {
        input
    }
}
+93 −0
Original line number Diff line number Diff line
@@ -3,8 +3,16 @@
 * SPDX-License-Identifier: Apache-2.0
 */

mod filter;
mod identity;
mod stack;

use crate::operation::Operation;

pub use filter::*;
pub use identity::*;
pub use stack::*;

/// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for
/// all builders.
///
@@ -46,41 +54,40 @@ pub trait Plugin<Protocol, Op, S, L> {
    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer>;
}

/// An [`Plugin`] that maps an `input` [`Operation`] to itself.
pub struct IdentityPlugin;

impl<P, Op, S, L> Plugin<P, Op, S, L> for IdentityPlugin {
    type Service = S;
    type Layer = L;

    fn map(&self, input: Operation<S, L>) -> Operation<S, L> {
        input
    }
}

/// A wrapper struct which composes an `Inner` and an `Outer` [`Plugin`].
pub struct PluginStack<Inner, Outer> {
    inner: Inner,
    outer: Outer,
}

impl<Inner, Outer> PluginStack<Inner, Outer> {
    /// Creates a new [`PluginStack`].
    pub fn new(inner: Inner, outer: Outer) -> Self {
        PluginStack { inner, outer }
    }
/// An extension trait for [`Plugin`].
pub trait PluginExt<P, Op, S, L>: Plugin<P, Op, S, L> {
    /// Stacks another [`Plugin`], running them sequentially.
    fn stack<Other>(self, other: Other) -> PluginStack<Self, Other>
    where
        Self: Sized,
    {
        PluginStack::new(self, other)
    }

impl<P, Op, S, L, Inner, Outer> Plugin<P, Op, S, L> for PluginStack<Inner, Outer>
    /// Filters the application of the [`Plugin`] using a predicate over the
    /// [`OperationShape::NAME`](crate::operation::OperationShape).
    ///
    /// # Example
    ///
    /// ```rust
    /// # use aws_smithy_http_server::{plugin::{Plugin, PluginExt}, operation::{Operation, OperationShape}};
    /// # struct Pl;
    /// # struct CheckHealth;
    /// # impl OperationShape for CheckHealth { const NAME: &'static str = ""; type Input = (); type Output = (); type Error = (); }
    /// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }}
    /// # let plugin = Pl;
    /// # let operation = Operation { inner: (), layer: () };
    /// // Prevents `plugin` from being applied to the `CheckHealth` operation.
    /// let filtered_plugin = plugin.filter_by_operation_name(|name| name != CheckHealth::NAME);
    /// let new_operation = filtered_plugin.map(operation);
    /// ```
    fn filter_by_operation_name<F>(self, predicate: F) -> FilterByOperationName<Self, F>
    where
    Inner: Plugin<P, Op, S, L>,
    Outer: Plugin<P, Op, Inner::Service, Inner::Layer>,
        Self: Sized,
        F: Fn(&str) -> bool,
    {
    type Service = Outer::Service;
    type Layer = Outer::Layer;

    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
        let inner = self.inner.map(input);
        self.outer.map(inner)
        FilterByOperationName::new(self, predicate)
    }
}

impl<Pl, P, Op, S, L> PluginExt<P, Op, S, L> for Pl where Pl: Plugin<P, Op, S, L> {}
+37 −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
 */

use crate::operation::Operation;

use super::Plugin;

/// A wrapper struct which composes an `Inner` and an `Outer` [`Plugin`].
///
/// The `Inner::map` is run _then_ the `Outer::map`.
pub struct PluginStack<Inner, Outer> {
    inner: Inner,
    outer: Outer,
}

impl<Inner, Outer> PluginStack<Inner, Outer> {
    /// Creates a new [`PluginStack`].
    pub fn new(inner: Inner, outer: Outer) -> Self {
        PluginStack { inner, outer }
    }
}

impl<P, Op, S, L, Inner, Outer> Plugin<P, Op, S, L> for PluginStack<Inner, Outer>
where
    Inner: Plugin<P, Op, S, L>,
    Outer: Plugin<P, Op, Inner::Service, Inner::Layer>,
{
    type Service = Outer::Service;
    type Layer = Outer::Layer;

    fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
        let inner = self.inner.map(input);
        self.outer.map(inner)
    }
}
Loading