aws_smithy_http_server/operation/
operation_service.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use std::{
7    marker::PhantomData,
8    task::{Context, Poll},
9};
10
11use tower::Service;
12
13use super::OperationShape;
14
15/// A utility trait used to provide an even interface for all operation services.
16///
17/// This serves to take [`Service`]s of the form `Service<(Op::Input, Ext0, Ext1, ...)>` to the canonical
18/// representation of `Service<(Input, (Ext0, Ext1, ...))>` inline with
19/// [`IntoService`](super::IntoService).
20///
21/// See [`operation`](crate::operation) documentation for more info.
22pub trait OperationService<Op, Exts>: Service<Self::Normalized, Response = Op::Output, Error = Op::Error>
23where
24    Op: OperationShape,
25{
26    type Normalized;
27
28    // Normalize the request type.
29    fn normalize(input: Op::Input, exts: Exts) -> Self::Normalized;
30}
31
32// `Service<Op::Input>`
33impl<Op, S> OperationService<Op, ()> for S
34where
35    Op: OperationShape,
36    S: Service<Op::Input, Response = Op::Output, Error = Op::Error>,
37{
38    type Normalized = Op::Input;
39
40    fn normalize(input: Op::Input, _exts: ()) -> Self::Normalized {
41        input
42    }
43}
44
45// `Service<(Op::Input, Ext0)>`
46impl<Op, Ext0, S> OperationService<Op, (Ext0,)> for S
47where
48    Op: OperationShape,
49    S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>,
50{
51    type Normalized = (Op::Input, Ext0);
52
53    fn normalize(input: Op::Input, exts: (Ext0,)) -> Self::Normalized {
54        (input, exts.0)
55    }
56}
57
58// `Service<(Op::Input, Ext0, Ext1)>`
59impl<Op, Ext0, Ext1, S> OperationService<Op, (Ext0, Ext1)> for S
60where
61    Op: OperationShape,
62    S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>,
63{
64    type Normalized = (Op::Input, Ext0, Ext1);
65
66    fn normalize(input: Op::Input, exts: (Ext0, Ext1)) -> Self::Normalized {
67        (input, exts.0, exts.1)
68    }
69}
70
71/// An extension trait of [`OperationService`].
72pub trait OperationServiceExt<Op, Exts>: OperationService<Op, Exts>
73where
74    Op: OperationShape,
75{
76    /// Convert the [`OperationService`] into a canonicalized [`Service`].
77    fn normalize(self) -> Normalize<Op, Self>
78    where
79        Self: Sized,
80    {
81        Normalize {
82            inner: self,
83            _operation: PhantomData,
84        }
85    }
86}
87
88impl<F, Op, Exts> OperationServiceExt<Op, Exts> for F
89where
90    Op: OperationShape,
91    F: OperationService<Op, Exts>,
92{
93}
94
95/// A [`Service`] normalizing the request type of a [`OperationService`].
96#[derive(Debug)]
97pub struct Normalize<Op, S> {
98    pub(crate) inner: S,
99    pub(crate) _operation: PhantomData<Op>,
100}
101
102impl<Op, S> Clone for Normalize<Op, S>
103where
104    S: Clone,
105{
106    fn clone(&self) -> Self {
107        Self {
108            inner: self.inner.clone(),
109            _operation: PhantomData,
110        }
111    }
112}
113
114impl<Op, S, Exts> Service<(Op::Input, Exts)> for Normalize<Op, S>
115where
116    Op: OperationShape,
117    S: OperationService<Op, Exts>,
118{
119    type Response = S::Response;
120    type Error = S::Error;
121    type Future = <S as Service<S::Normalized>>::Future;
122
123    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
124        self.inner.poll_ready(cx)
125    }
126
127    fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future {
128        let req = S::normalize(input, exts);
129        self.inner.call(req)
130    }
131}