Trait aws_smithy_http_server::plugin::ModelMarker
source · pub trait ModelMarker { }
Expand description
A model plugin is a plugin that acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized.
This trait is a marker trait to indicate that a plugin can be registered as a model plugin.
Compare with HttpMarker
in the module documentation.
§Example implementation of a model plugin
Model plugins are most useful when you really need to rely on the actual shape of your modeled
operation input, operation output, and/or operation errors. For this reason, most (but not all)
model plugins are operation-specific: somewhere in the type signature of their definition,
they’ll rely on a particular operation shape’s types. It is therefore important that you scope
application of model plugins to the operations they are meant to work on, via
Scoped
or filter_by_operation
.
Below is an example implementation of a model plugin that can only be applied to the
CheckHealth
operation: note how in the Service
trait implementation, we require access to
the operation’s input, where we log the health_info
field.
use std::marker::PhantomData;
use aws_smithy_http_server::{operation::OperationShape, plugin::{ModelMarker, Plugin}};
use tower::Service;
/// A model plugin that can only be applied to the `CheckHealth` operation.
pub struct CheckHealthPlugin<Exts> {
pub _exts: PhantomData<Exts>,
}
impl<Exts> CheckHealthPlugin<Exts> {
pub fn new() -> Self {
Self { _exts: PhantomData }
}
}
impl<T, Exts> Plugin<SimpleService, CheckHealth, T> for CheckHealthPlugin<Exts> {
type Output = CheckHealthService<T, Exts>;
fn apply(&self, input: T) -> Self::Output {
CheckHealthService {
inner: input,
_exts: PhantomData,
}
}
}
impl<Exts> ModelMarker for CheckHealthPlugin<Exts> { }
#[derive(Clone)]
pub struct CheckHealthService<S, Exts> {
inner: S,
_exts: PhantomData<Exts>,
}
impl<S, Exts> Service<(<CheckHealth as OperationShape>::Input, Exts)> for CheckHealthService<S, Exts>
where
S: Service<(<CheckHealth as OperationShape>::Input, Exts)>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: (<CheckHealth as OperationShape>::Input, Exts)) -> Self::Future {
let (input, _exts) = &req;
// We have access to `CheckHealth`'s modeled operation input!
dbg!(&input.health_info);
self.inner.call(req)
}
}
// In `main.rs` or wherever we register plugins, we have to make sure we only apply this plugin
// to the the only operation it can be applied to, the `CheckHealth` operation. If we apply the
// plugin to other operations, we will get a compilation error.
use aws_smithy_http_server::plugin::Scoped;
use aws_smithy_http_server::scope;
pub fn main() {
scope! {
struct OnlyCheckHealth {
includes: [CheckHealth],
excludes: [/* The rest of the operations go here */]
}
}
let model_plugin = CheckHealthPlugin::new();
// Scope the plugin to the `CheckHealth` operation.
let scoped_plugin = Scoped::new::<OnlyCheckHealth>(model_plugin);
}
If you are a service owner and don’t care about giving a name to the model plugin, you can simplify this down to:
use std::marker::PhantomData;
use aws_smithy_http_server::operation::OperationShape;
use tower::Service;
#[derive(Clone)]
pub struct CheckHealthService<S, Exts> {
inner: S,
_exts: PhantomData<Exts>,
}
impl<S, Exts> Service<(<CheckHealth as OperationShape>::Input, Exts)> for CheckHealthService<S, Exts>
where
S: Service<(<CheckHealth as OperationShape>::Input, Exts)>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: (<CheckHealth as OperationShape>::Input, Exts)) -> Self::Future {
let (input, _exts) = &req;
// We have access to `CheckHealth`'s modeled operation input!
dbg!(&input.health_info);
self.inner.call(req)
}
}
// In `main.rs`:
use aws_smithy_http_server::plugin::LayerPlugin;
use aws_smithy_http_server::plugin::Scoped;
use aws_smithy_http_server::scope;
fn new_check_health_service<S, Ext>(inner: S) -> CheckHealthService<S, Ext> {
CheckHealthService {
inner,
_exts: PhantomData,
}
}
pub fn main() {
scope! {
struct OnlyCheckHealth {
includes: [CheckHealth],
excludes: [/* The rest of the operations go here */]
}
}
let layer = tower::layer::layer_fn(new_check_health_service);
let model_plugin = LayerPlugin(layer);
// Scope the plugin to the `CheckHealth` operation.
let scoped_plugin = Scoped::new::<OnlyCheckHealth>(model_plugin);
}