aws_smithy_http_server/operation/
mod.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! The shape of a [Smithy operation] is modelled by the [`OperationShape`] trait. Its associated types
7//! [`OperationShape::Input`], [`OperationShape::Output`], and [`OperationShape::Error`] map to the structures
8//! representing the Smithy inputs, outputs, and errors respectively. When an operation error is not specified
9//! [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible).
10//!
11//! We generate a marker struct for each Smithy operation and implement [`OperationShape`] on them. This
12//! is used as a helper - providing static methods and parameterizing other traits.
13//!
14//! The model
15//!
16//! ```smithy
17//! operation GetShopping {
18//!    input: CartIdentifier,
19//!    output: ShoppingCart,
20//!    errors: [...]
21//! }
22//! ```
23//!
24//! is identified with the implementation
25//!
26//! ```rust,no_run
27//! # use aws_smithy_http_server::shape_id::ShapeId;
28//! # use aws_smithy_http_server::operation::OperationShape;
29//! # pub struct CartIdentifier;
30//! # pub struct ShoppingCart;
31//! # pub enum GetShoppingError {}
32//! pub struct GetShopping;
33//!
34//! impl OperationShape for GetShopping {
35//!     const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping");
36//!
37//!     type Input = CartIdentifier;
38//!     type Output = ShoppingCart;
39//!     type Error = GetShoppingError;
40//! }
41//! ```
42//!
43//! The behavior of a Smithy operation is encoded by a [`Service`](tower::Service). The [`OperationShape`] types can
44//! be used to construct specific operations using [`OperationShapeExt::from_handler`] and
45//! [`OperationShapeExt::from_service`] methods. The [from_handler](OperationShapeExt::from_handler) constructor takes
46//! a [`Handler`] whereas the [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits
47//! serve a similar purpose - they provide a common interface over a class of structures.
48//!
49//! ## [`Handler`]
50//!
51//! The [`Handler`] trait is implemented by all async functions which accept [`OperationShape::Input`] as their first
52//! argument, the remaining arguments implement [`FromParts`](crate::request::FromParts), and return either
53//! [`OperationShape::Output`] when [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible) or
54//! [`Result`]<[`OperationShape::Output`],[`OperationShape::Error`]>. The following are examples of async functions which
55//! implement [`Handler`]:
56//!
57//! ```rust,no_run
58//! # use aws_smithy_http_server::Extension;
59//! # pub struct CartIdentifier;
60//! # pub struct ShoppingCart;
61//! # pub enum GetShoppingError {}
62//! # pub struct Context;
63//! # pub struct ExtraContext;
64//! // Simple handler where no error is modelled.
65//! async fn handler_a(input: CartIdentifier) -> ShoppingCart {
66//!     todo!()
67//! }
68//!
69//! // Handler with an extension where no error is modelled.
70//! async fn handler_b(input: CartIdentifier, ext: Extension<Context>) -> ShoppingCart {
71//!     todo!()
72//! }
73//!
74//! // More than one extension can be provided.
75//! async fn handler_c(input: CartIdentifier, ext_1: Extension<Context>, ext_2: Extension<ExtraContext>) -> ShoppingCart {
76//!     todo!()
77//! }
78//!
79//! // When an error is modelled we must return a `Result`.
80//! async fn handler_d(input: CartIdentifier, ext: Extension<Context>) -> Result<ShoppingCart, GetShoppingError> {
81//!     todo!()
82//! }
83//! ```
84//!
85//! ## [`OperationService`]
86//!
87//! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with
88//! `Response = Op::Output`, and `Error = Op::Error`.
89//!
90//! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]:
91//!
92//! - `Service<CartIdentifier, Response = ShoppingCart, Error = Infallible>`.
93//! - `Service<(CartIdentifier, Extension<Context>), Response = ShoppingCart, Error = GetShoppingCartError>`.
94//! - `Service<(CartIdentifier, Extension<Context>, Extension<ExtraContext>), Response = ShoppingCart, Error = GetShoppingCartError)`.
95//!
96//! Notice the parallels between [`OperationService`] and [`Handler`].
97//!
98//! ## Constructing an Operation
99//!
100//! The following is an example of using both construction approaches:
101//!
102//! ```rust,no_run
103//! # use std::task::{Poll, Context};
104//! # use aws_smithy_http_server::operation::*;
105//! # use tower::Service;
106//! # use aws_smithy_http_server::shape_id::ShapeId;
107//! # pub struct CartIdentifier;
108//! # pub struct ShoppingCart;
109//! # pub enum GetShoppingError {}
110//! # pub struct GetShopping;
111//! # impl OperationShape for GetShopping {
112//! #    const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping");
113//! #
114//! #    type Input = CartIdentifier;
115//! #    type Output = ShoppingCart;
116//! #    type Error = GetShoppingError;
117//! # }
118//! # type OpFuture = std::future::Ready<Result<ShoppingCart, GetShoppingError>>;
119//! // Construction of an `Operation` from a `Handler`.
120//!
121//! async fn op_handler(input: CartIdentifier) -> Result<ShoppingCart, GetShoppingError> {
122//!     todo!()
123//! }
124//!
125//! let operation = GetShopping::from_handler(op_handler);
126//!
127//! // Construction of an `Operation` from a `Service`.
128//!
129//! pub struct OpService;
130//!
131//! impl Service<CartIdentifier> for OpService {
132//!     type Response = ShoppingCart;
133//!     type Error = GetShoppingError;
134//!     type Future = OpFuture;
135//!
136//!     fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
137//!         todo!()
138//!     }
139//!
140//!     fn call(&mut self, request: CartIdentifier) -> Self::Future {
141//!         todo!()
142//!     }
143//! }
144//!
145//! let operation = GetShopping::from_service(OpService);
146//!
147//! ```
148//!
149//! ## Upgrading Smithy services to HTTP services
150//!
151//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. They are converted to a
152//! canonical form `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The
153//! [`UpgradePlugin`] acts upon such services by converting them to
154//! `Service<http::Request, Response = http::Response, Error = Infallible>` by applying serialization/deserialization
155//! and validation specified by the Smithy contract.
156//!
157//!
158//! The [`UpgradePlugin`], being a [`Plugin`](crate::plugin::Plugin), is parameterized by a protocol. This allows for
159//! upgrading to `Service<http::Request, Response = http::Response, Error = Infallible>` to be protocol dependent.
160//!
161//! [Smithy operation]: https://smithy.io/2.0/spec/service-types.html#operation
162
163mod handler;
164mod operation_service;
165mod shape;
166mod upgrade;
167
168pub use handler::*;
169pub use operation_service::*;
170pub use shape::*;
171pub use upgrade::*;