1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
//! The plugin system allows you to build middleware with an awareness of the operation it is applied to.
//!
//! The system centers around the [`Plugin`] trait. In addition, this module provides helpers for composing and
//! combining [`Plugin`]s.
//!
//! # Filtered application of a HTTP [`Layer`](tower::Layer)
//!
//! ```
//! # use aws_smithy_http_server::plugin::*;
//! # let layer = ();
//! # struct GetPokemonSpecies;
//! # impl GetPokemonSpecies { const NAME: &'static str = ""; };
//! // Create a `Plugin` from a HTTP `Layer`
//! let plugin = HttpLayer(layer);
//!
//! // Only apply the layer to operations with name "GetPokemonSpecies"
//! let plugin = filter_by_operation_name(plugin, |name| name == GetPokemonSpecies::NAME);
//! ```
//!
//! # Construct a [`Plugin`] from a closure that takes as input the operation name
//!
//! ```
//! # use aws_smithy_http_server::plugin::*;
//! // A `tower::Layer` which requires the operation name
//! struct PrintLayer {
//! name: &'static str
//! }
//!
//! // Create a `Plugin` using `PrintLayer`
//! let plugin = plugin_from_operation_name_fn(|name| PrintLayer { name });
//! ```
//!
//! # Combine [`Plugin`]s
//!
//! ```
//! # use aws_smithy_http_server::plugin::*;
//! # let a = (); let b = ();
//! // Combine `Plugin`s `a` and `b`
//! let plugin = PluginPipeline::new()
//! .push(a)
//! .push(b);
//! ```
//!
//! As noted in the [`PluginPipeline`] documentation, the plugins' runtime logic is executed in registration order,
//! meaning that `a` is run _before_ `b` in the example above.
//!
//! # Example Implementation
//!
//! ```rust
//! use aws_smithy_http_server::{
//! operation::{Operation, OperationShape},
//! plugin::{Plugin, PluginPipeline, PluginStack},
//! };
//! # use tower::{layer::util::Stack, Layer, Service};
//! # use std::task::{Context, Poll};
//!
//! /// A [`Service`] that adds a print log.
//! #[derive(Clone, Debug)]
//! pub struct PrintService<S> {
//! inner: S,
//! name: &'static str,
//! }
//!
//! impl<R, S> Service<R> for PrintService<S>
//! where
//! S: Service<R>,
//! {
//! type Response = S::Response;
//! type Error = S::Error;
//! type Future = S::Future;
//!
//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
//! self.inner.poll_ready(cx)
//! }
//!
//! fn call(&mut self, req: R) -> Self::Future {
//! println!("Hi {}", self.name);
//! self.inner.call(req)
//! }
//! }
//!
//! /// A [`Layer`] which constructs the [`PrintService`].
//! #[derive(Debug)]
//! pub struct PrintLayer {
//! name: &'static str,
//! }
//! impl<S> Layer<S> for PrintLayer {
//! type Service = PrintService<S>;
//!
//! fn layer(&self, service: S) -> Self::Service {
//! PrintService {
//! inner: service,
//! name: self.name,
//! }
//! }
//! }
//!
//! /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations.
//! #[derive(Debug)]
//! pub struct PrintPlugin;
//!
//! impl<P, Op, S, L> Plugin<P, Op, S, L> for PrintPlugin
//! where
//! Op: OperationShape,
//! {
//! type Service = S;
//! type Layer = Stack<L, PrintLayer>;
//!
//! fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
//! input.layer(PrintLayer { name: Op::NAME })
//! }
//! }
//! ```
//!
pub mod alb_health_check;
mod closure;
mod either;
mod filter;
mod identity;
mod layer;
mod pipeline;
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;
pub use pipeline::PluginPipeline;
pub use stack::PluginStack;
/// 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.
///
/// See [module](crate::plugin) documentation for more information.
pub trait Plugin<Protocol, Op, S, L> {
/// The type of the new [`Service`](tower::Service).
type Service;
/// The type of the new [`Layer`](tower::Layer).
type Layer;
/// 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)
}
}