Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PluginExt extension trait and FilterByOperationName plugin #1837

Merged
merged 6 commits into from
Oct 11, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Split into modules and add FilterByOperationName
Harry Barber committed Oct 11, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit eb013a4b93d9cc1d7b21bedf5c4d3895da026cb6
42 changes: 42 additions & 0 deletions rust-runtime/aws-smithy-http-server/src/plugin/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use tower::util::Either;

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

use super::Plugin;

pub struct FilterByOperationName<Inner, F> {
inner: Inner,
predicate: F,
}

impl<Inner, F> FilterByOperationName<Inner, F> {
/// Creates a new [`FilterByOperationName`].
pub 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(&'static 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),
}
}
}
}
15 changes: 15 additions & 0 deletions rust-runtime/aws-smithy-http-server/src/plugin/identity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::operation::Operation;

use super::Plugin;

/// 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
}
}
Original file line number Diff line number Diff line change
@@ -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,34 @@ 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
/// 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)
}
}

/// 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 }
/// Filters the application of the [`Plugin`] using a predicate over the [`OperationShape::NAME`].
///
/// # Example
///
/// ```rust
/// # use aws_smithy_http_server::plugin::{Plugin, PluginExt};
/// # struct Pl;
/// # impl Plugin<(), (), (), ()> for Pl {}
/// # let plugin = Pl;
/// // Prevents `plugin` from being applied to the `CheckHealth` operation.
/// let filtered_plugin = plugin.filter_by_operation_name(|name| name != "com.aws.example#CheckHealth");
/// ```
fn filter_by_operation_name<F>(self, predicate: F) -> FilterByOperationName<Self, F>
where
Self: Sized,
{
FilterByOperationName::new(self, predicate)
}
}

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)
}
}
impl<Pl, P, Op, S, L> PluginExt<P, Op, S, L> for Pl where Pl: Plugin<P, Op, S, L> {}
32 changes: 32 additions & 0 deletions rust-runtime/aws-smithy-http-server/src/plugin/stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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)
}
}