-
Notifications
You must be signed in to change notification settings - Fork 197
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
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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; | ||
LukeMathWalker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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) | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These have become public in order to allow
Plugin
developers access to them.Is this too severe? Maybe we should document that the average customer should use the
from_handler
/from_service
methods?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want customers to be able to write their own plugins, along the same lines of what we are doing here with the filtering, I don't see many other options.