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)
    }
}