conjure_http/client/
conjure.rs

1// Copyright 2025 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::convert::TryFrom;
18
19use bytes::Bytes;
20use conjure_error::Error;
21use conjure_object::{Plain, ToPlain};
22use futures_core::Stream;
23use http::{header::CONTENT_TYPE, HeaderValue, Response, StatusCode};
24use serde::de::{DeserializeOwned, IgnoredAny};
25
26use crate::client::{
27    AsyncDeserializeResponse, AsyncRequestBody, AsyncSerializeRequest, AsyncWriteBody,
28    BoxAsyncWriteBody, BoxLocalAsyncWriteBody, ConjureRuntime, DeserializeResponse, EncodeHeader,
29    EncodeParam, LocalAsyncDeserializeResponse, LocalAsyncRequestBody, LocalAsyncSerializeRequest,
30    LocalAsyncWriteBody, RequestBody, SerializeRequest, StdResponseDeserializer, WriteBody,
31};
32use crate::private::APPLICATION_OCTET_STREAM;
33
34/// A body serializer for streaming requests.
35pub enum BinaryRequestSerializer {}
36
37impl<'a, T, R> SerializeRequest<'a, T, R> for BinaryRequestSerializer
38where
39    T: WriteBody<R> + 'a,
40{
41    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
42        APPLICATION_OCTET_STREAM
43    }
44
45    fn serialize(_: &ConjureRuntime, value: T) -> Result<RequestBody<'a, R>, Error> {
46        Ok(RequestBody::Streaming(Box::new(value)))
47    }
48}
49
50impl<'a, T, R> AsyncSerializeRequest<'a, T, R> for BinaryRequestSerializer
51where
52    T: AsyncWriteBody<R> + Send + 'a,
53{
54    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
55        APPLICATION_OCTET_STREAM
56    }
57
58    fn serialize(_: &ConjureRuntime, value: T) -> Result<AsyncRequestBody<'a, R>, Error> {
59        Ok(AsyncRequestBody::Streaming(BoxAsyncWriteBody::new(value)))
60    }
61}
62
63impl<'a, T, R> LocalAsyncSerializeRequest<'a, T, R> for BinaryRequestSerializer
64where
65    T: LocalAsyncWriteBody<R> + 'a,
66{
67    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
68        APPLICATION_OCTET_STREAM
69    }
70
71    fn serialize(_: &ConjureRuntime, value: T) -> Result<LocalAsyncRequestBody<'a, R>, Error> {
72        Ok(LocalAsyncRequestBody::Streaming(
73            BoxLocalAsyncWriteBody::new(value),
74        ))
75    }
76}
77
78/// A body deserializer for collection types.
79pub enum CollectionResponseDeserializer {}
80
81impl<T, R> DeserializeResponse<T, R> for CollectionResponseDeserializer
82where
83    T: DeserializeOwned + Default,
84    R: Iterator<Item = Result<Bytes, Error>>,
85{
86    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
87        <StdResponseDeserializer as DeserializeResponse<T, R>>::accept(runtime)
88    }
89
90    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
91        if response.status() == StatusCode::NO_CONTENT {
92            return Ok(T::default());
93        }
94
95        <StdResponseDeserializer as DeserializeResponse<T, R>>::deserialize(runtime, response)
96    }
97}
98
99impl<T, R> AsyncDeserializeResponse<T, R> for CollectionResponseDeserializer
100where
101    T: DeserializeOwned + Default,
102    R: Stream<Item = Result<Bytes, Error>> + Send,
103{
104    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
105        <StdResponseDeserializer as AsyncDeserializeResponse<T, R>>::accept(runtime)
106    }
107
108    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
109        if response.status() == StatusCode::NO_CONTENT {
110            return Ok(T::default());
111        }
112
113        <StdResponseDeserializer as AsyncDeserializeResponse<T, R>>::deserialize(runtime, response)
114            .await
115    }
116}
117
118impl<T, R> LocalAsyncDeserializeResponse<T, R> for CollectionResponseDeserializer
119where
120    T: DeserializeOwned + Default,
121    R: Stream<Item = Result<Bytes, Error>>,
122{
123    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
124        <StdResponseDeserializer as LocalAsyncDeserializeResponse<T, R>>::accept(runtime)
125    }
126
127    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
128        if response.status() == StatusCode::NO_CONTENT {
129            return Ok(T::default());
130        }
131
132        <StdResponseDeserializer as LocalAsyncDeserializeResponse<T, R>>::deserialize(
133            runtime, response,
134        )
135        .await
136    }
137}
138
139/// A body deserializer for binary types.
140pub enum BinaryResponseDeserializer {}
141
142impl<R> DeserializeResponse<R, R> for BinaryResponseDeserializer {
143    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
144        Some(APPLICATION_OCTET_STREAM)
145    }
146
147    fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
148        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
149            return Err(Error::internal_safe("invalid response Content-Type"));
150        }
151
152        Ok(response.into_body())
153    }
154}
155
156impl<R> AsyncDeserializeResponse<R, R> for BinaryResponseDeserializer
157where
158    R: Send,
159{
160    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
161        Some(APPLICATION_OCTET_STREAM)
162    }
163
164    async fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
165        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
166            return Err(Error::internal_safe("invalid response Content-Type"));
167        }
168
169        Ok(response.into_body())
170    }
171}
172
173impl<R> LocalAsyncDeserializeResponse<R, R> for BinaryResponseDeserializer {
174    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
175        Some(APPLICATION_OCTET_STREAM)
176    }
177
178    async fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
179        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
180            return Err(Error::internal_safe("invalid response Content-Type"));
181        }
182
183        Ok(response.into_body())
184    }
185}
186
187/// A body deserializer for optional binary types.
188pub enum OptionalBinaryResponseDeserializer {}
189
190impl<R> DeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer {
191    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
192        <BinaryResponseDeserializer as DeserializeResponse<R, R>>::accept(runtime)
193    }
194
195    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<Option<R>, Error> {
196        if response.status() == StatusCode::NO_CONTENT {
197            return Ok(None);
198        }
199
200        <BinaryResponseDeserializer as DeserializeResponse<R, R>>::deserialize(runtime, response)
201            .map(Some)
202    }
203}
204
205impl<R> AsyncDeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer
206where
207    R: Send,
208{
209    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
210        <BinaryResponseDeserializer as AsyncDeserializeResponse<R, R>>::accept(runtime)
211    }
212
213    async fn deserialize(
214        runtime: &ConjureRuntime,
215        response: Response<R>,
216    ) -> Result<Option<R>, Error> {
217        if response.status() == StatusCode::NO_CONTENT {
218            return Ok(None);
219        }
220
221        <BinaryResponseDeserializer as AsyncDeserializeResponse<R, R>>::deserialize(
222            runtime, response,
223        )
224        .await
225        .map(Some)
226    }
227}
228
229impl<R> LocalAsyncDeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer {
230    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
231        <BinaryResponseDeserializer as LocalAsyncDeserializeResponse<R, R>>::accept(runtime)
232    }
233
234    async fn deserialize(
235        runtime: &ConjureRuntime,
236        response: Response<R>,
237    ) -> Result<Option<R>, Error> {
238        if response.status() == StatusCode::NO_CONTENT {
239            return Ok(None);
240        }
241
242        <BinaryResponseDeserializer as LocalAsyncDeserializeResponse<R, R>>::deserialize(
243            runtime, response,
244        )
245        .await
246        .map(Some)
247    }
248}
249
250/// A body deserializer for unit types.
251pub enum EmptyResponseDeserializer {}
252
253impl<R> DeserializeResponse<(), R> for EmptyResponseDeserializer
254where
255    R: Iterator<Item = Result<Bytes, Error>>,
256{
257    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
258        <StdResponseDeserializer as DeserializeResponse<(), R>>::accept(runtime)
259    }
260
261    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
262        if response.status() == StatusCode::NO_CONTENT {
263            return Ok(());
264        }
265
266        <StdResponseDeserializer as DeserializeResponse<IgnoredAny, R>>::deserialize(
267            runtime, response,
268        )?;
269
270        Ok(())
271    }
272}
273
274impl<R> AsyncDeserializeResponse<(), R> for EmptyResponseDeserializer
275where
276    R: Stream<Item = Result<Bytes, Error>> + Send,
277{
278    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
279        <StdResponseDeserializer as AsyncDeserializeResponse<(), R>>::accept(runtime)
280    }
281
282    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
283        if response.status() == StatusCode::NO_CONTENT {
284            return Ok(());
285        }
286
287        <StdResponseDeserializer as AsyncDeserializeResponse<IgnoredAny, R>>::deserialize(
288            runtime, response,
289        )
290        .await?;
291
292        Ok(())
293    }
294}
295
296impl<R> LocalAsyncDeserializeResponse<(), R> for EmptyResponseDeserializer
297where
298    R: Stream<Item = Result<Bytes, Error>>,
299{
300    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
301        <StdResponseDeserializer as LocalAsyncDeserializeResponse<(), R>>::accept(runtime)
302    }
303
304    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
305        if response.status() == StatusCode::NO_CONTENT {
306            return Ok(());
307        }
308
309        <StdResponseDeserializer as LocalAsyncDeserializeResponse<IgnoredAny, R>>::deserialize(
310            runtime, response,
311        )
312        .await?;
313
314        Ok(())
315    }
316}
317
318/// An encoder which converts values via their `Plain` implementation.
319pub enum PlainEncoder {}
320
321impl<T> EncodeParam<T> for PlainEncoder
322where
323    T: Plain,
324{
325    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<String>, Error> {
326        Ok(vec![value.to_plain()])
327    }
328}
329
330impl<T> EncodeHeader<T> for PlainEncoder
331where
332    T: Plain,
333{
334    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<HeaderValue>, Error> {
335        HeaderValue::try_from(value.to_plain())
336            .map_err(Error::internal_safe)
337            .map(|v| vec![v])
338    }
339}
340
341/// An encoder which converts a sequence of values via their individual `Plain` implementations.
342pub enum PlainSeqEncoder {}
343
344impl<T, U> EncodeParam<T> for PlainSeqEncoder
345where
346    T: IntoIterator<Item = U>,
347    U: Plain,
348{
349    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<String>, Error> {
350        Ok(value.into_iter().map(|v| v.to_plain()).collect())
351    }
352}
353
354impl<T, U> EncodeHeader<T> for PlainSeqEncoder
355where
356    T: IntoIterator<Item = U>,
357    U: Plain,
358{
359    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<HeaderValue>, Error> {
360        value
361            .into_iter()
362            .map(|v| HeaderValue::try_from(v.to_plain()).map_err(Error::internal_safe))
363            .collect()
364    }
365}