-
Notifications
You must be signed in to change notification settings - Fork 197
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add PluginExt extension trait and FilterByOperationName plugin (#1837)
* Make Operation fields public * Split into modules and add FilterByOperationName
- Loading branch information
Showing
6 changed files
with
205 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* 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 changes: 20 additions & 0 deletions
20
rust-runtime/aws-smithy-http-server/src/plugin/identity.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* 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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* 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. | ||
/// | ||
/// As [`Plugin`]s modify the way in which [`Operation`]s are [`upgraded`](crate::operation::Upgradable) we can use | ||
/// [`Pluggable`] as a foundation to write extension traits which are implemented for all service builders. | ||
/// | ||
/// # Example | ||
/// | ||
/// ``` | ||
/// # struct PrintPlugin; | ||
/// # use aws_smithy_http_server::plugin::Pluggable; | ||
/// trait PrintExt: Pluggable<PrintPlugin> { | ||
/// fn print(self) -> Self::Output where Self: Sized { | ||
/// self.apply(PrintPlugin) | ||
/// } | ||
/// } | ||
/// | ||
/// impl<Builder> PrintExt for Builder where Builder: Pluggable<PrintPlugin> {} | ||
/// ``` | ||
pub trait Pluggable<NewPlugin> { | ||
type Output; | ||
|
||
/// Applies a [`Plugin`] to the service builder. | ||
fn apply(self, plugin: NewPlugin) -> Self::Output; | ||
} | ||
|
||
/// A mapping from one [`Operation`] to another. Used to modify the behavior of | ||
/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder, | ||
/// | ||
/// The generics `Protocol` and `Op` allow the behavior to be parameterized. | ||
/// | ||
/// Every service builder enjoys [`Pluggable`] and therefore can be provided with a [`Plugin`] using | ||
/// [`Pluggable::apply`]. | ||
pub trait Plugin<Protocol, Op, S, L> { | ||
type Service; | ||
type Layer; | ||
|
||
/// Maps an [`Operation`] to another. | ||
fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer>; | ||
} | ||
|
||
/// 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) | ||
} | ||
|
||
/// 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 | ||
Self: Sized, | ||
F: Fn(&str) -> bool, | ||
{ | ||
FilterByOperationName::new(self, predicate) | ||
} | ||
} | ||
|
||
impl<Pl, P, Op, S, L> PluginExt<P, Op, S, L> for Pl where Pl: Plugin<P, Op, S, L> {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* 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) | ||
} | ||
} |