aws_smithy_http_server/plugin/model_plugins.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6// If you make any updates to this file (including Rust docs), make sure you make them to
7// `http_plugins.rs` too!
8
9use crate::plugin::{IdentityPlugin, Plugin, PluginStack};
10
11use super::{LayerPlugin, ModelMarker};
12
13/// A wrapper struct for composing model plugins.
14/// It operates identically to [`HttpPlugins`](crate::plugin::HttpPlugins); see its documentation.
15#[derive(Debug)]
16pub struct ModelPlugins<P>(pub(crate) P);
17
18impl Default for ModelPlugins<IdentityPlugin> {
19 fn default() -> Self {
20 Self(IdentityPlugin)
21 }
22}
23
24impl ModelPlugins<IdentityPlugin> {
25 /// Create an empty [`ModelPlugins`].
26 ///
27 /// You can use [`ModelPlugins::push`] to add plugins to it.
28 pub fn new() -> Self {
29 Self::default()
30 }
31}
32
33impl<P> ModelPlugins<P> {
34 /// Apply a new model plugin after the ones that have already been registered.
35 ///
36 /// ```rust
37 /// use aws_smithy_http_server::plugin::ModelPlugins;
38 /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin;
39 /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin;
40 ///
41 /// let model_plugins = ModelPlugins::new().push(LoggingPlugin).push(MetricsPlugin);
42 /// ```
43 ///
44 /// The plugins' runtime logic is executed in registration order.
45 /// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last.
46 ///
47 /// ## Implementation notes
48 ///
49 /// Plugins are applied to the underlying [`Service`](tower::Service) in opposite order compared
50 /// to their registration order.
51 ///
52 /// As an example:
53 ///
54 /// ```rust,compile_fail
55 /// #[derive(Debug)]
56 /// pub struct PrintPlugin;
57 ///
58 /// impl<Ser, Op, S> Plugin<Ser, Op, T> for PrintPlugin
59 /// // [...]
60 /// {
61 /// // [...]
62 /// fn apply(&self, inner: T) -> Self::Service {
63 /// PrintService {
64 /// inner,
65 /// service_id: Ser::ID,
66 /// operation_id: Op::ID
67 /// }
68 /// }
69 /// }
70 /// ```
71 // We eagerly require `NewPlugin: ModelMarker`, despite not really needing it, because compiler
72 // errors get _substantially_ better if the user makes a mistake.
73 pub fn push<NewPlugin: ModelMarker>(self, new_plugin: NewPlugin) -> ModelPlugins<PluginStack<NewPlugin, P>> {
74 ModelPlugins(PluginStack::new(new_plugin, self.0))
75 }
76
77 /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized.
78 pub fn layer<L>(self, layer: L) -> ModelPlugins<PluginStack<LayerPlugin<L>, P>> {
79 ModelPlugins(PluginStack::new(LayerPlugin(layer), self.0))
80 }
81}
82
83impl<Ser, Op, T, InnerPlugin> Plugin<Ser, Op, T> for ModelPlugins<InnerPlugin>
84where
85 InnerPlugin: Plugin<Ser, Op, T>,
86{
87 type Output = InnerPlugin::Output;
88
89 fn apply(&self, input: T) -> Self::Output {
90 self.0.apply(input)
91 }
92}
93
94impl<InnerPlugin> ModelMarker for ModelPlugins<InnerPlugin> where InnerPlugin: ModelMarker {}