conjure_http/server/
conjure.rs

1// Copyright 2024 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Implementations for Conjure-generated endpoints.
16
17use std::{error, iter::FromIterator, marker::PhantomData};
18
19use bytes::Bytes;
20use conjure_error::{Error, InvalidArgument};
21use conjure_object::FromPlain;
22use futures_core::Stream;
23use http::{header::CONTENT_TYPE, HeaderMap, HeaderValue, Response};
24use serde::{de::DeserializeOwned, Serialize};
25
26use crate::private::APPLICATION_OCTET_STREAM;
27
28use super::{
29    AsyncDeserializeRequest, AsyncResponseBody, AsyncSerializeResponse, AsyncWriteBody,
30    ConjureRuntime, DecodeHeader, DecodeParam, DeserializeRequest, EmptyResponseSerializer,
31    ResponseBody, SerializeResponse, StdRequestDeserializer, StdResponseSerializer, WriteBody,
32};
33
34/// A request deserializer for optional body types.
35pub enum OptionalRequestDeserializer {}
36
37impl<T, R> DeserializeRequest<Option<T>, R> for OptionalRequestDeserializer
38where
39    T: DeserializeOwned,
40    R: Iterator<Item = Result<Bytes, Error>>,
41{
42    fn deserialize(
43        runtime: &ConjureRuntime,
44        headers: &HeaderMap,
45        body: R,
46    ) -> Result<Option<T>, Error> {
47        if !headers.contains_key(CONTENT_TYPE) {
48            return Ok(None);
49        }
50
51        <StdRequestDeserializer as DeserializeRequest<_, _>>::deserialize(runtime, headers, body)
52    }
53}
54
55impl<T, R> AsyncDeserializeRequest<Option<T>, R> for OptionalRequestDeserializer
56where
57    T: DeserializeOwned,
58    R: Stream<Item = Result<Bytes, Error>> + Send,
59{
60    async fn deserialize(
61        runtime: &ConjureRuntime,
62        headers: &HeaderMap,
63        body: R,
64    ) -> Result<Option<T>, Error> {
65        if !headers.contains_key(CONTENT_TYPE) {
66            return Ok(None);
67        }
68
69        <StdRequestDeserializer as AsyncDeserializeRequest<_, _>>::deserialize(
70            runtime, headers, body,
71        )
72        .await
73    }
74}
75
76/// A request deserializer for binary body types.
77pub enum BinaryRequestDeserializer {}
78
79impl BinaryRequestDeserializer {
80    fn deserialize_inner<R>(headers: &HeaderMap, body: R) -> Result<R, Error> {
81        if headers.get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
82            return Err(Error::service_safe(
83                "unexpected Content-Type",
84                InvalidArgument::new(),
85            ));
86        }
87
88        Ok(body)
89    }
90}
91
92impl<R> DeserializeRequest<R, R> for BinaryRequestDeserializer {
93    fn deserialize(_runtime: &ConjureRuntime, headers: &HeaderMap, body: R) -> Result<R, Error> {
94        Self::deserialize_inner(headers, body)
95    }
96}
97
98impl<R> AsyncDeserializeRequest<R, R> for BinaryRequestDeserializer
99where
100    R: Send,
101{
102    async fn deserialize(
103        _runtime: &ConjureRuntime,
104        headers: &HeaderMap,
105        body: R,
106    ) -> Result<R, Error> {
107        Self::deserialize_inner(headers, body)
108    }
109}
110
111/// A body serializer for collection types.
112pub enum CollectionResponseSerializer {}
113
114impl<T, W> SerializeResponse<T, W> for CollectionResponseSerializer
115where
116    T: Serialize + PartialEq + Default,
117{
118    fn serialize(
119        runtime: &ConjureRuntime,
120        request_headers: &HeaderMap,
121        value: T,
122    ) -> Result<Response<ResponseBody<W>>, Error> {
123        if value == T::default() {
124            <EmptyResponseSerializer as SerializeResponse<_, _>>::serialize(
125                runtime,
126                request_headers,
127                (),
128            )
129        } else {
130            <StdResponseSerializer as SerializeResponse<_, _>>::serialize(
131                runtime,
132                request_headers,
133                value,
134            )
135        }
136    }
137}
138
139impl<T, W> AsyncSerializeResponse<T, W> for CollectionResponseSerializer
140where
141    T: Serialize + PartialEq + Default,
142{
143    fn serialize(
144        runtime: &ConjureRuntime,
145        request_headers: &HeaderMap,
146        value: T,
147    ) -> Result<Response<AsyncResponseBody<W>>, Error> {
148        if value == T::default() {
149            <EmptyResponseSerializer as AsyncSerializeResponse<_, _>>::serialize(
150                runtime,
151                request_headers,
152                (),
153            )
154        } else {
155            <StdResponseSerializer as AsyncSerializeResponse<_, _>>::serialize(
156                runtime,
157                request_headers,
158                value,
159            )
160        }
161    }
162}
163
164/// A response serializer for binary types.
165pub enum BinaryResponseSerializer {}
166
167impl BinaryResponseSerializer {
168    fn serialize_inner<B>(body: B) -> Result<Response<B>, Error> {
169        let mut response = Response::new(body);
170
171        response
172            .headers_mut()
173            .insert(CONTENT_TYPE, APPLICATION_OCTET_STREAM);
174        Ok(response)
175    }
176}
177
178impl<T, W> SerializeResponse<T, W> for BinaryResponseSerializer
179where
180    T: WriteBody<W> + 'static,
181{
182    fn serialize(
183        _runtime: &ConjureRuntime,
184        _request_headers: &HeaderMap,
185        value: T,
186    ) -> Result<Response<ResponseBody<W>>, Error> {
187        Self::serialize_inner(ResponseBody::Streaming(Box::new(value)))
188    }
189}
190
191impl<T, W> AsyncSerializeResponse<T, W> for BinaryResponseSerializer
192where
193    T: AsyncWriteBody<W> + 'static + Send,
194{
195    fn serialize(
196        _runtime: &ConjureRuntime,
197        _request_headers: &HeaderMap,
198        value: T,
199    ) -> Result<Response<AsyncResponseBody<W>>, Error> {
200        Self::serialize_inner(AsyncResponseBody::Streaming(super::BoxAsyncWriteBody::new(
201            value,
202        )))
203    }
204}
205
206/// A response serializer for optional binary types.
207pub enum OptionalBinaryResponseSerializer {}
208
209impl<T, W> SerializeResponse<Option<T>, W> for OptionalBinaryResponseSerializer
210where
211    T: WriteBody<W> + 'static,
212{
213    fn serialize(
214        runtime: &ConjureRuntime,
215        request_headers: &HeaderMap,
216        value: Option<T>,
217    ) -> Result<Response<ResponseBody<W>>, Error> {
218        match value {
219            Some(value) => <BinaryResponseSerializer as SerializeResponse<_, _>>::serialize(
220                runtime,
221                request_headers,
222                value,
223            ),
224            None => <EmptyResponseSerializer as SerializeResponse<_, _>>::serialize(
225                runtime,
226                request_headers,
227                (),
228            ),
229        }
230    }
231}
232
233impl<T, W> AsyncSerializeResponse<Option<T>, W> for OptionalBinaryResponseSerializer
234where
235    T: AsyncWriteBody<W> + 'static + Send,
236{
237    fn serialize(
238        runtime: &ConjureRuntime,
239        request_headers: &HeaderMap,
240        value: Option<T>,
241    ) -> Result<Response<AsyncResponseBody<W>>, Error> {
242        match value {
243            Some(value) => <BinaryResponseSerializer as AsyncSerializeResponse<_, _>>::serialize(
244                runtime,
245                request_headers,
246                value,
247            ),
248            None => <EmptyResponseSerializer as AsyncSerializeResponse<_, _>>::serialize(
249                runtime,
250                request_headers,
251                (),
252            ),
253        }
254    }
255}
256
257/// A decoder which converts a single value using its [`FromPlain`] implementation.
258pub enum FromPlainDecoder {}
259
260impl<T> DecodeHeader<T> for FromPlainDecoder
261where
262    T: FromPlain,
263    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
264{
265    fn decode<'a, I>(_: &ConjureRuntime, headers: I) -> Result<T, Error>
266    where
267        I: IntoIterator<Item = &'a HeaderValue>,
268    {
269        T::from_plain(
270            super::only_item(headers)?
271                .to_str()
272                .map_err(|e| Error::service(e, InvalidArgument::new()))?,
273        )
274        .map_err(|e| Error::service(e, InvalidArgument::new()))
275    }
276}
277
278impl<T> DecodeParam<T> for FromPlainDecoder
279where
280    T: FromPlain,
281    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
282{
283    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<T, Error>
284    where
285        I: IntoIterator,
286        I::Item: AsRef<str>,
287    {
288        T::from_plain(super::only_item(params)?.as_ref())
289            .map_err(|e| Error::service(e, InvalidArgument::new()))
290    }
291}
292
293/// A decoder which converts an optional value using its [`FromPlain`] implementation.
294pub enum FromPlainOptionDecoder {}
295
296impl<T> DecodeHeader<Option<T>> for FromPlainOptionDecoder
297where
298    T: FromPlain,
299    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
300{
301    fn decode<'a, I>(_: &ConjureRuntime, headers: I) -> Result<Option<T>, Error>
302    where
303        I: IntoIterator<Item = &'a HeaderValue>,
304    {
305        let Some(header) = super::optional_item(headers)? else {
306            return Ok(None);
307        };
308        let value = T::from_plain(
309            header
310                .to_str()
311                .map_err(|e| Error::service(e, InvalidArgument::new()))?,
312        )
313        .map_err(|e| Error::service(e, InvalidArgument::new()))?;
314        Ok(Some(value))
315    }
316}
317
318impl<T> DecodeParam<Option<T>> for FromPlainOptionDecoder
319where
320    T: FromPlain,
321    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
322{
323    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<Option<T>, Error>
324    where
325        I: IntoIterator,
326        I::Item: AsRef<str>,
327    {
328        let Some(param) = super::optional_item(params)? else {
329            return Ok(None);
330        };
331        let value =
332            T::from_plain(param.as_ref()).map_err(|e| Error::service(e, InvalidArgument::new()))?;
333        Ok(Some(value))
334    }
335}
336
337/// A decoder which converts a sequence of values via its [`FromPlain`] implementation into a
338/// collection via a [`FromIterator`] implementation.
339pub struct FromPlainSeqDecoder<U> {
340    _p: PhantomData<U>,
341}
342
343impl<T, U> DecodeParam<T> for FromPlainSeqDecoder<U>
344where
345    T: FromIterator<U>,
346    U: FromPlain,
347    U::Err: Into<Box<dyn error::Error + Sync + Send>>,
348{
349    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<T, Error>
350    where
351        I: IntoIterator,
352        I::Item: AsRef<str>,
353    {
354        params
355            .into_iter()
356            .map(|s| {
357                U::from_plain(s.as_ref()).map_err(|e| Error::service(e, InvalidArgument::new()))
358            })
359            .collect()
360    }
361}