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

Implementations on Foreign Types§

source§

impl<'a, Pl> ModelMarker for &'a Pl
where Pl: ModelMarker,

Implementors§

source§

impl ModelMarker for IdentityPlugin

source§

impl<Inner, F> ModelMarker for FilterByOperation<Inner, F>
where Inner: ModelMarker,

source§

impl<Inner, Outer> ModelMarker for PluginStack<Inner, Outer>
where Inner: ModelMarker, Outer: ModelMarker,

source§

impl<InnerPlugin> ModelMarker for ModelPlugins<InnerPlugin>
where InnerPlugin: ModelMarker,

source§

impl<L> ModelMarker for LayerPlugin<L>

source§

impl<Scope, Pl> ModelMarker for Scoped<Scope, Pl>
where Pl: ModelMarker,