Unverified Commit 7347c584 authored by Harry Barber's avatar Harry Barber Committed by GitHub
Browse files

Remove `PollError` from server type parameters (#2457)

## Motivation and Context

https://github.com/awslabs/smithy-rs/issues/2444

## Description

Remove `PollError` from the implementation and documentation.
parent 840221dd
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -105,3 +105,13 @@ author = "david-perez"
references = ["smithy-rs#2676", "smithy-rs#2685"]
meta = { "breaking" = true, "tada" = false, "bug" = false }

[[smithy-rs]]
message = """Remove `PollError` from an operations `Service::Error`.

Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to
[`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service)
no longer requires `Service::Error = OperationError<Op::Error, PollError>`, instead requiring just `Service::Error = Op::Error`.
"""
references = ["smithy-rs#2457"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" }
author = "hlbarber"
+5 −8
Original line number Diff line number Diff line
@@ -10,13 +10,10 @@ use std::{
    task::{Context, Poll},
};

use futures_util::{
    future::{Map, MapErr},
    FutureExt, TryFutureExt,
};
use futures_util::{future::Map, FutureExt};
use tower::Service;

use super::{OperationError, OperationShape};
use super::OperationShape;

/// A utility trait used to provide an even interface for all operation handlers.
///
@@ -143,14 +140,14 @@ where
    H: Handler<Op, Exts>,
{
    type Response = Op::Output;
    type Error = OperationError<Op::Error, Infallible>;
    type Future = MapErr<H::Future, fn(Op::Error) -> Self::Error>;
    type Error = Op::Error;
    type Future = H::Future;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future {
        self.handler.call(input, exts).map_err(OperationError::Model)
        self.handler.call(input, exts)
    }
}
+12 −32
Original line number Diff line number Diff line
@@ -84,16 +84,13 @@
//! ## [`OperationService`]
//!
//! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with
//! `Response = Op::Output`, and `Error = OperationError<Op::Error, PollError>`.
//!
//! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom
//! [`Service::poll_ready`](tower::Service::poll_ready) implementation.
//! `Response = Op::Output`, and `Error = Op::Error`.
//!
//! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]:
//!
//! - `Service<CartIdentifier, Response = ShoppingCart, Error = OperationError<Infallible, Infallible>>`.
//! - `Service<(CartIdentifier, Extension<Context>), Response = ShoppingCart, Error = OperationError<GetShoppingCartError, Infallible>>`.
//! - `Service<(CartIdentifier, Extension<Context>, Extension<ExtraContext>), Response = ShoppingCart, Error = OperationError<GetShoppingCartError, PollError>)`.
//! - `Service<CartIdentifier, Response = ShoppingCart, Error = Infallible>`.
//! - `Service<(CartIdentifier, Extension<Context>), Response = ShoppingCart, Error = GetShoppingCartError>`.
//! - `Service<(CartIdentifier, Extension<Context>, Extension<ExtraContext>), Response = ShoppingCart, Error = GetShoppingCartError)`.
//!
//! Notice the parallels between [`OperationService`] and [`Handler`].
//!
@@ -116,7 +113,7 @@
//! #    type Output = ShoppingCart;
//! #    type Error = GetShoppingError;
//! # }
//! # type OpFuture = std::future::Ready<Result<ShoppingCart, OperationError<GetShoppingError, PollError>>>;
//! # type OpFuture = std::future::Ready<Result<ShoppingCart, GetShoppingError>>;
//! // Construction of an `Operation` from a `Handler`.
//!
//! async fn op_handler(input: CartIdentifier) -> Result<ShoppingCart, GetShoppingError> {
@@ -127,13 +124,11 @@
//!
//! // Construction of an `Operation` from a `Service`.
//!
//! pub struct PollError;
//!
//! pub struct OpService;
//!
//! impl Service<CartIdentifier> for OpService {
//!     type Response = ShoppingCart;
//!     type Error = OperationError<GetShoppingError, PollError>;
//!     type Error = GetShoppingError;
//!     type Future = OpFuture;
//!
//!     fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
@@ -155,19 +150,13 @@
//!
//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is
//! constructed they are converted to a canonical form
//! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError<Op::Error, PollError>>`. The
//! `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The
//! [`UpgradeLayer`] acts upon such services by converting them to
//! `Service<http::Request, Response = http::Response, Error = PollError>`.
//! `Service<http::Request, Response = http::Response, Error = Infallible>`.
//!
//! Note that the `PollError` is still exposed, for two reasons:
//!
//! - Smithy is agnostic to `PollError` and therefore we have no prescribed way to serialize it to a [`http::Response`]
//! , unlike the operation errors.
//! - The intention of `PollError` is to signal that the underlying service is no longer able to take requests, so
//! should be discarded. See [`Service::poll_ready`](tower::Service::poll_ready).
//!
//! The [`UpgradeLayer`] and it's [`Layer::Service`](tower::Layer::Service) [`Upgrade`] are both parameterized by a
//! protocol. This allows for upgrading to `Service<http::Request, Response = http::Response, Error = PollError>` to be
//! protocol. This allows for upgrading to `Service<http::Request, Response = http::Response, Error = Infallible>` to be
//! protocol dependent.
//!
//! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`](tower::Layer) `L`. The
@@ -208,15 +197,15 @@ impl<S, L> Operation<S, L> {
    }
}

impl<Op, S, PollError> Operation<Normalize<Op, S, PollError>> {
impl<Op, S> Operation<Normalize<Op, S>> {
    /// Creates an [`Operation`] from a [`Service`](tower::Service).
    pub fn from_service<Exts>(inner: S) -> Self
    where
        Op: OperationShape,
        S: OperationService<Op, Exts, PollError>,
        S: OperationService<Op, Exts>,
    {
        Self {
            inner: inner.canonicalize(),
            inner: inner.normalize(),
            layer: Identity::new(),
        }
    }
@@ -235,12 +224,3 @@ impl<Op, H> Operation<IntoService<Op, H>> {
        }
    }
}

/// The operation [`Service`](tower::Service) has two classes of failure modes - those specified by the Smithy model
/// and those associated with [`Service::poll_ready`](tower::Service::poll_ready).
pub enum OperationError<ModelError, PollError> {
    /// An error modelled by the Smithy model occurred.
    Model(ModelError),
    /// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred.
    PollReady(PollError),
}
+16 −20
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ use std::{

use tower::Service;

use super::{OperationError, OperationShape};
use super::OperationShape;

/// A utility trait used to provide an even interface for all operation services.
///
@@ -19,8 +19,7 @@ use super::{OperationError, OperationShape};
/// [`IntoService`](super::IntoService).
///
/// See [`operation`](crate::operation) documentation for more info.
pub trait OperationService<Op, Exts, PollError>:
    Service<Self::Normalized, Response = Op::Output, Error = OperationError<Op::Error, PollError>>
pub trait OperationService<Op, Exts>: Service<Self::Normalized, Response = Op::Output, Error = Op::Error>
where
    Op: OperationShape,
{
@@ -31,10 +30,10 @@ where
}

// `Service<Op::Input>`
impl<Op, S, PollError> OperationService<Op, (), PollError> for S
impl<Op, S> OperationService<Op, ()> for S
where
    Op: OperationShape,
    S: Service<Op::Input, Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
    S: Service<Op::Input, Response = Op::Output, Error = Op::Error>,
{
    type Normalized = Op::Input;

@@ -44,10 +43,10 @@ where
}

// `Service<(Op::Input, Ext0)>`
impl<Op, Ext0, S, PollError> OperationService<Op, (Ext0,), PollError> for S
impl<Op, Ext0, S> OperationService<Op, (Ext0,)> for S
where
    Op: OperationShape,
    S: Service<(Op::Input, Ext0), Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
    S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>,
{
    type Normalized = (Op::Input, Ext0);

@@ -57,10 +56,10 @@ where
}

// `Service<(Op::Input, Ext0, Ext1)>`
impl<Op, Ext0, Ext1, S, PollError> OperationService<Op, (Ext0, Ext1), PollError> for S
impl<Op, Ext0, Ext1, S> OperationService<Op, (Ext0, Ext1)> for S
where
    Op: OperationShape,
    S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
    S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>,
{
    type Normalized = (Op::Input, Ext0, Ext1);

@@ -70,39 +69,37 @@ where
}

/// An extension trait of [`OperationService`].
pub trait OperationServiceExt<Op, Exts, PollError>: OperationService<Op, Exts, PollError>
pub trait OperationServiceExt<Op, Exts>: OperationService<Op, Exts>
where
    Op: OperationShape,
{
    /// Convert the [`OperationService`] into a canonicalized [`Service`].
    fn canonicalize(self) -> Normalize<Op, Self, PollError>
    fn normalize(self) -> Normalize<Op, Self>
    where
        Self: Sized,
    {
        Normalize {
            inner: self,
            _operation: PhantomData,
            _poll_error: PhantomData,
        }
    }
}

impl<F, Op, Exts, PollError> OperationServiceExt<Op, Exts, PollError> for F
impl<F, Op, Exts> OperationServiceExt<Op, Exts> for F
where
    Op: OperationShape,
    F: OperationService<Op, Exts, PollError>,
    F: OperationService<Op, Exts>,
{
}

/// A [`Service`] normalizing the request type of a [`OperationService`].
#[derive(Debug)]
pub struct Normalize<Op, S, PollError> {
pub struct Normalize<Op, S> {
    inner: S,
    _operation: PhantomData<Op>,
    _poll_error: PhantomData<PollError>,
}

impl<Op, S, PollError> Clone for Normalize<Op, S, PollError>
impl<Op, S> Clone for Normalize<Op, S>
where
    S: Clone,
{
@@ -110,15 +107,14 @@ where
        Self {
            inner: self.inner.clone(),
            _operation: PhantomData,
            _poll_error: PhantomData,
        }
    }
}

impl<Op, S, Exts, PollError> Service<(Op::Input, Exts)> for Normalize<Op, S, PollError>
impl<Op, S, Exts> Service<(Op::Input, Exts)> for Normalize<Op, S>
where
    Op: OperationShape,
    S: OperationService<Op, Exts, PollError>,
    S: OperationService<Op, Exts>,
{
    type Response = S::Response;
    type Error = S::Error;
+2 −2
Original line number Diff line number Diff line
@@ -33,9 +33,9 @@ pub trait OperationShapeExt: OperationShape {
    }

    /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s.
    fn from_service<S, Exts, PollError>(svc: S) -> Operation<Normalize<Self, S, PollError>>
    fn from_service<S, Exts>(svc: S) -> Operation<Normalize<Self, S>>
    where
        S: OperationService<Self, Exts, PollError>,
        S: OperationService<Self, Exts>,
        Self: Sized,
    {
        Operation::from_service(svc)
Loading