conjure_http/server/
mod.rs

1// Copyright 2019 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//! The Conjure HTTP server API.
16use bytes::Bytes;
17use conjure_error::{Error, InvalidArgument};
18use futures_core::Stream;
19use http::header::CONTENT_TYPE;
20use http::{
21    request, Extensions, HeaderMap, HeaderValue, Method, Request, Response, StatusCode, Uri,
22};
23use serde::de::DeserializeOwned;
24use serde::Serialize;
25use std::borrow::Cow;
26use std::error;
27use std::future::Future;
28use std::io::Write;
29use std::iter::FromIterator;
30use std::marker::PhantomData;
31use std::ops::Deref;
32use std::pin::Pin;
33use std::str;
34use std::str::FromStr;
35use std::sync::Arc;
36
37use crate::private::{self, SERIALIZABLE_REQUEST_SIZE_LIMIT};
38pub use crate::server::encoding::*;
39#[doc(inline)]
40pub use crate::server::runtime::ConjureRuntime;
41
42pub mod conjure;
43mod encoding;
44pub mod runtime;
45
46/// Metadata about an HTTP endpoint.
47pub trait EndpointMetadata {
48    /// The endpoint's HTTP method.
49    fn method(&self) -> Method;
50
51    /// The endpoint's parsed HTTP URI path.
52    ///
53    /// Each value in the slice represents one segment of the URI.
54    fn path(&self) -> &[PathSegment];
55
56    /// The endpoint's raw HTTP URI template.
57    ///
58    /// Use the [`Self::path()`] method for routing rather than parsing this string.
59    fn template(&self) -> &str;
60
61    /// The name of the service defining this endpoint.
62    fn service_name(&self) -> &str;
63
64    /// The name of the endpoint.
65    fn name(&self) -> &str;
66
67    /// If the endpoint is deprecated, returns the deprecation documentation.
68    fn deprecated(&self) -> Option<&str>;
69}
70
71impl<T> EndpointMetadata for Box<T>
72where
73    T: ?Sized + EndpointMetadata,
74{
75    fn method(&self) -> Method {
76        (**self).method()
77    }
78
79    fn path(&self) -> &[PathSegment] {
80        (**self).path()
81    }
82
83    fn template(&self) -> &str {
84        (**self).template()
85    }
86
87    fn service_name(&self) -> &str {
88        (**self).service_name()
89    }
90
91    fn name(&self) -> &str {
92        (**self).name()
93    }
94
95    fn deprecated(&self) -> Option<&str> {
96        (**self).deprecated()
97    }
98}
99
100/// A blocking HTTP endpoint.
101pub trait Endpoint<I, O>: EndpointMetadata {
102    /// Handles a request to the endpoint.
103    ///
104    /// If the endpoint has path parameters, callers must include a
105    /// [`PathParams`](crate::PathParams) extension in the request containing the extracted
106    /// parameters from the URI. The implementation is reponsible for all other request handling,
107    /// including parsing query parameters, header parameters, and the request body.
108    ///
109    /// The `response_extensions` will be added to the extensions of the response produced by the
110    /// endpoint, even if an error is returned.
111    fn handle(
112        &self,
113        req: Request<I>,
114        response_extensions: &mut Extensions,
115    ) -> Result<Response<ResponseBody<O>>, Error>;
116}
117
118impl<T, I, O> Endpoint<I, O> for Box<T>
119where
120    T: ?Sized + Endpoint<I, O>,
121{
122    fn handle(
123        &self,
124        req: Request<I>,
125        response_extensions: &mut Extensions,
126    ) -> Result<Response<ResponseBody<O>>, Error> {
127        (**self).handle(req, response_extensions)
128    }
129}
130
131/// A nonblocking HTTP endpoint.
132pub trait AsyncEndpoint<I, O>: EndpointMetadata {
133    /// Handles a request to the endpoint.
134    ///
135    /// If the endpoint has path parameters, callers must include a
136    /// [`PathParams`](crate::PathParams) extension in the request containing the extracted
137    /// parameters from the URI. The implementation is reponsible for all other request handling,
138    /// including parsing query parameters, header parameters, and the request body.
139    ///
140    /// The `response_extensions` will be added to the extensions of the response produced by the
141    /// endpoint, even if an error is returned.
142    fn handle(
143        &self,
144        req: Request<I>,
145        response_extensions: &mut Extensions,
146    ) -> impl Future<Output = Result<Response<AsyncResponseBody<O>>, Error>> + Send;
147}
148
149impl<T, I, O> AsyncEndpoint<I, O> for Box<T>
150where
151    T: ?Sized + AsyncEndpoint<I, O>,
152{
153    fn handle(
154        &self,
155        req: Request<I>,
156        response_extensions: &mut Extensions,
157    ) -> impl Future<Output = Result<Response<AsyncResponseBody<O>>, Error>> + Send {
158        (**self).handle(req, response_extensions)
159    }
160}
161
162// An internal object-safe version of AsyncEndpoint used to implement BoxAsyncEndpoint
163trait AsyncEndpointEraser<I, O>: EndpointMetadata {
164    #[allow(clippy::type_complexity)]
165    fn handle<'a>(
166        &'a self,
167        req: Request<I>,
168        response_extensions: &'a mut Extensions,
169    ) -> Pin<Box<dyn Future<Output = Result<Response<AsyncResponseBody<O>>, Error>> + Send + 'a>>
170    where
171        I: 'a,
172        O: 'a;
173}
174
175impl<T, I, O> AsyncEndpointEraser<I, O> for T
176where
177    T: AsyncEndpoint<I, O>,
178{
179    fn handle<'a>(
180        &'a self,
181        req: Request<I>,
182        response_extensions: &'a mut Extensions,
183    ) -> Pin<Box<dyn Future<Output = Result<Response<AsyncResponseBody<O>>, Error>> + Send + 'a>>
184    where
185        I: 'a,
186        O: 'a,
187    {
188        Box::pin(self.handle(req, response_extensions))
189    }
190}
191
192/// A boxed [`AsyncEndpoint`] trait object.
193pub struct BoxAsyncEndpoint<'a, I, O> {
194    inner: Box<dyn AsyncEndpointEraser<I, O> + 'a + Sync + Send>,
195}
196
197impl<'a, I, O> BoxAsyncEndpoint<'a, I, O> {
198    /// Creates a new `BoxAsyncEndpoint`.
199    pub fn new<T>(v: T) -> Self
200    where
201        T: AsyncEndpoint<I, O> + Sync + Send + 'a,
202    {
203        BoxAsyncEndpoint { inner: Box::new(v) }
204    }
205}
206
207impl<I, O> EndpointMetadata for BoxAsyncEndpoint<'_, I, O> {
208    fn method(&self) -> Method {
209        self.inner.method()
210    }
211
212    fn path(&self) -> &[PathSegment] {
213        self.inner.path()
214    }
215
216    fn template(&self) -> &str {
217        self.inner.template()
218    }
219
220    fn service_name(&self) -> &str {
221        self.inner.service_name()
222    }
223
224    fn name(&self) -> &str {
225        self.inner.name()
226    }
227
228    fn deprecated(&self) -> Option<&str> {
229        self.inner.deprecated()
230    }
231}
232
233impl<I, O> AsyncEndpoint<I, O> for BoxAsyncEndpoint<'_, I, O>
234where
235    I: Send,
236{
237    async fn handle(
238        &self,
239        req: Request<I>,
240        response_extensions: &mut Extensions,
241    ) -> Result<Response<AsyncResponseBody<O>>, Error> {
242        self.inner.handle(req, response_extensions).await
243    }
244}
245
246/// One segment of an endpoint URI template.
247#[derive(Debug, Clone)]
248pub enum PathSegment {
249    /// A literal string.
250    Literal(Cow<'static, str>),
251
252    /// A parameter.
253    Parameter {
254        /// The name of the parameter.
255        name: Cow<'static, str>,
256
257        /// The regex pattern used to match the pattern.
258        regex: Option<Cow<'static, str>>,
259    },
260}
261
262/// The response body returned from a blocking endpoint.
263pub enum ResponseBody<O> {
264    /// An empty body.
265    Empty,
266    /// A body buffered in memory.
267    Fixed(Bytes),
268    /// A streaming body.
269    Streaming(Box<dyn WriteBody<O>>),
270}
271
272/// The response body returned from an async endpoint.
273pub enum AsyncResponseBody<O> {
274    /// An empty body.
275    Empty,
276    /// A body buffered in memory.
277    Fixed(Bytes),
278    /// A streaming body.
279    Streaming(BoxAsyncWriteBody<'static, O>),
280}
281
282/// A blocking Conjure service.
283pub trait Service<I, O> {
284    /// Returns the endpoints in the service.
285    fn endpoints(
286        &self,
287        runtime: &Arc<ConjureRuntime>,
288    ) -> Vec<Box<dyn Endpoint<I, O> + Sync + Send>>;
289}
290
291/// An async Conjure service.
292pub trait AsyncService<I, O> {
293    /// Returns the endpoints in the service.
294    fn endpoints(&self, runtime: &Arc<ConjureRuntime>) -> Vec<BoxAsyncEndpoint<'static, I, O>>;
295}
296
297/// A trait implemented by streaming bodies.
298pub trait WriteBody<W> {
299    /// Writes the body out, in its entirety.
300    // This should not be limited to `Box<Self>`, but it otherwise can't be used as a trait object currently :(
301    fn write_body(self: Box<Self>, w: &mut W) -> Result<(), Error>;
302}
303
304impl<W> WriteBody<W> for Vec<u8>
305where
306    W: Write,
307{
308    fn write_body(self: Box<Self>, w: &mut W) -> Result<(), Error> {
309        w.write_all(&self).map_err(Error::internal_safe)
310    }
311}
312
313/// A trait implemented by asynchronous streaming bodies.
314///
315/// # Examples
316///
317/// ```ignore
318/// use conjure_error::Error;
319/// use conjure_http::server::AsyncWriteBody;
320/// use std::pin::Pin;
321/// use tokio_io::{AsyncWrite, AsyncWriteExt};
322///
323/// pub struct SimpleBodyWriter;
324///
325/// impl<W> AsyncWriteBody<W> for SimpleBodyWriter
326/// where
327///     W: AsyncWrite + Send,
328/// {
329///     async fn write_body(self, mut w: Pin<&mut W>) -> Result<(), Error> {
330///         w.write_all(b"hello world").await.map_err(Error::internal_safe)
331///     }
332/// }
333/// ```
334pub trait AsyncWriteBody<W> {
335    /// Writes the body out, in its entirety.
336    fn write_body(self, w: Pin<&mut W>) -> impl Future<Output = Result<(), Error>> + Send;
337}
338
339// An internal object-safe version of AsyncWriteBody used to implement BoxAsyncWriteBody
340trait AsyncWriteBodyEraser<W> {
341    fn write_body<'a>(
342        self: Box<Self>,
343        w: Pin<&'a mut W>,
344    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>>
345    where
346        Self: 'a;
347}
348
349impl<T, W> AsyncWriteBodyEraser<W> for T
350where
351    T: AsyncWriteBody<W>,
352{
353    fn write_body<'a>(
354        self: Box<Self>,
355        w: Pin<&'a mut W>,
356    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>>
357    where
358        Self: 'a,
359    {
360        Box::pin((*self).write_body(w))
361    }
362}
363
364/// A boxed [`AsyncWriteBody`] trait object.
365pub struct BoxAsyncWriteBody<'a, W> {
366    inner: Box<dyn AsyncWriteBodyEraser<W> + Send + 'a>,
367}
368
369impl<'a, W> BoxAsyncWriteBody<'a, W> {
370    /// Creates a new `BoxAsyncWriteBody`.
371    pub fn new<T>(v: T) -> Self
372    where
373        T: AsyncWriteBody<W> + Send + 'a,
374    {
375        BoxAsyncWriteBody { inner: Box::new(v) }
376    }
377}
378
379impl<W> AsyncWriteBody<W> for BoxAsyncWriteBody<'_, W>
380where
381    W: Send,
382{
383    async fn write_body(self, w: Pin<&mut W>) -> Result<(), Error>
384    where
385        W: Send,
386    {
387        self.inner.write_body(w).await
388    }
389}
390
391/// An object containing extra low-level contextual information about a request.
392///
393/// Conjure service endpoints declared with the `server-request-context` tag will be passed a
394/// `RequestContext` in the generated trait.
395pub struct RequestContext<'a> {
396    request_parts: MaybeBorrowed<'a, request::Parts>,
397    response_extensions: &'a mut Extensions,
398}
399
400impl<'a> RequestContext<'a> {
401    // This is public API but not exposed in docs since it should only be called by generated code.
402    #[doc(hidden)]
403    #[inline]
404    // FIXME remove in favor of borrowed constructor
405    pub fn new(request_parts: request::Parts, response_extensions: &'a mut Extensions) -> Self {
406        RequestContext {
407            request_parts: MaybeBorrowed::Owned(request_parts),
408            response_extensions,
409        }
410    }
411
412    #[doc(hidden)]
413    #[inline]
414    pub fn new2(
415        request_parts: &'a request::Parts,
416        response_extensions: &'a mut Extensions,
417    ) -> Self {
418        RequestContext {
419            request_parts: MaybeBorrowed::Borrowed(request_parts),
420            response_extensions,
421        }
422    }
423
424    /// Returns the request's URI.
425    #[inline]
426    pub fn request_uri(&self) -> &Uri {
427        &self.request_parts.uri
428    }
429
430    /// Returns a shared reference to the request's headers.
431    #[inline]
432    pub fn request_headers(&self) -> &HeaderMap {
433        &self.request_parts.headers
434    }
435
436    /// Returns a shared reference to the request's extensions.
437    #[inline]
438    pub fn request_extensions(&self) -> &Extensions {
439        &self.request_parts.extensions
440    }
441
442    /// Returns a shared reference to extensions that will be added to the response.
443    #[inline]
444    pub fn response_extensions(&self) -> &Extensions {
445        self.response_extensions
446    }
447
448    /// Returns a mutable reference to extensions that will be added to the response.
449    #[inline]
450    pub fn response_extensions_mut(&mut self) -> &mut Extensions {
451        self.response_extensions
452    }
453}
454
455enum MaybeBorrowed<'a, T> {
456    Borrowed(&'a T),
457    Owned(T),
458}
459
460impl<T> Deref for MaybeBorrowed<'_, T> {
461    type Target = T;
462
463    #[inline]
464    fn deref(&self) -> &Self::Target {
465        match self {
466            MaybeBorrowed::Borrowed(v) => v,
467            MaybeBorrowed::Owned(v) => v,
468        }
469    }
470}
471
472/// A trait implemented by request body deserializers used by custom Conjure server trait
473/// implementations.
474pub trait DeserializeRequest<T, R> {
475    /// Deserializes the request body.
476    fn deserialize(runtime: &ConjureRuntime, headers: &HeaderMap, body: R) -> Result<T, Error>;
477}
478
479/// A trait implemented by response deserializers used by custom async Conjure server trait
480/// implementations.
481pub trait AsyncDeserializeRequest<T, R> {
482    /// Deserializes the request body.
483    fn deserialize(
484        runtime: &ConjureRuntime,
485        headers: &HeaderMap,
486        body: R,
487    ) -> impl Future<Output = Result<T, Error>> + Send;
488}
489
490/// A request deserializer for standard body types.
491///
492/// It is parameterized by the maximum number of bytes that will be read from the request body
493/// before an error is returned. The limit defaults to 50 MiB.
494pub enum StdRequestDeserializer<const N: usize = { SERIALIZABLE_REQUEST_SIZE_LIMIT }> {}
495
496impl<const N: usize, T, R> DeserializeRequest<T, R> for StdRequestDeserializer<N>
497where
498    T: DeserializeOwned,
499    R: Iterator<Item = Result<Bytes, Error>>,
500{
501    fn deserialize(runtime: &ConjureRuntime, headers: &HeaderMap, body: R) -> Result<T, Error> {
502        let encoding = runtime.request_body_encoding(headers)?;
503        let buf = private::read_body(body, Some(N))?;
504        let v = T::deserialize(encoding.deserializer(&buf).deserializer())
505            .map_err(|e| Error::service(e, InvalidArgument::new()))?;
506        Ok(v)
507    }
508}
509
510impl<const N: usize, T, R> AsyncDeserializeRequest<T, R> for StdRequestDeserializer<N>
511where
512    T: DeserializeOwned,
513    R: Stream<Item = Result<Bytes, Error>> + Send,
514{
515    async fn deserialize(
516        runtime: &ConjureRuntime,
517        headers: &HeaderMap,
518        body: R,
519    ) -> Result<T, Error> {
520        let encoding = runtime.request_body_encoding(headers)?;
521        let buf = private::async_read_body(body, Some(N)).await?;
522        let v = T::deserialize(encoding.deserializer(&buf).deserializer())
523            .map_err(|e| Error::service(e, InvalidArgument::new()))?;
524        Ok(v)
525    }
526}
527
528/// A request deserializer which maps the output of another with [`From::from`].
529pub struct FromRequestDeserializer<D, U> {
530    _p: PhantomData<(D, U)>,
531}
532
533impl<T, R, D, U> DeserializeRequest<T, R> for FromRequestDeserializer<D, U>
534where
535    T: From<U>,
536    D: DeserializeRequest<U, R>,
537{
538    fn deserialize(runtime: &ConjureRuntime, headers: &HeaderMap, body: R) -> Result<T, Error> {
539        D::deserialize(runtime, headers, body).map(From::from)
540    }
541}
542
543impl<T, R, D, U> AsyncDeserializeRequest<T, R> for FromRequestDeserializer<D, U>
544where
545    T: From<U>,
546    D: AsyncDeserializeRequest<U, R>,
547    R: Send,
548{
549    async fn deserialize(
550        runtime: &ConjureRuntime,
551        headers: &HeaderMap,
552        body: R,
553    ) -> Result<T, Error> {
554        D::deserialize(runtime, headers, body).await.map(From::from)
555    }
556}
557
558/// A trait implemented by response serializers used by custom Conjure server trait implementations.
559pub trait SerializeResponse<T, W> {
560    /// Serializes the response.
561    fn serialize(
562        runtime: &ConjureRuntime,
563        request_headers: &HeaderMap,
564        value: T,
565    ) -> Result<Response<ResponseBody<W>>, Error>;
566}
567
568/// A trait implemented by response serializers used by custom async Conjure server trait
569/// implementations.
570pub trait AsyncSerializeResponse<T, W> {
571    /// Serializes the response.
572    fn serialize(
573        runtime: &ConjureRuntime,
574        request_headers: &HeaderMap,
575        value: T,
576    ) -> Result<Response<AsyncResponseBody<W>>, Error>;
577}
578
579/// A serializer which encodes `()` as an empty body and status code of `204 No Content`.
580pub enum EmptyResponseSerializer {}
581
582impl EmptyResponseSerializer {
583    fn serialize_inner<T>(body: T) -> Result<Response<T>, Error> {
584        let mut response = Response::new(body);
585        *response.status_mut() = StatusCode::NO_CONTENT;
586        Ok(response)
587    }
588}
589
590impl<W> SerializeResponse<(), W> for EmptyResponseSerializer {
591    fn serialize(
592        _: &ConjureRuntime,
593        _: &HeaderMap,
594        _: (),
595    ) -> Result<Response<ResponseBody<W>>, Error> {
596        Self::serialize_inner(ResponseBody::Empty)
597    }
598}
599
600impl<W> AsyncSerializeResponse<(), W> for EmptyResponseSerializer {
601    fn serialize(
602        _: &ConjureRuntime,
603        _: &HeaderMap,
604        _: (),
605    ) -> Result<Response<AsyncResponseBody<W>>, Error> {
606        Self::serialize_inner(AsyncResponseBody::Empty)
607    }
608}
609
610/// A body serializer for standard response types.
611pub enum StdResponseSerializer {}
612
613impl StdResponseSerializer {
614    fn serialize_inner<B>(
615        runtime: &ConjureRuntime,
616        request_headers: &HeaderMap,
617        value: &dyn erased_serde::Serialize,
618        make_body: impl FnOnce(Bytes) -> B,
619    ) -> Result<Response<B>, Error> {
620        let encoding = runtime.response_body_encoding(request_headers)?;
621
622        let mut body = vec![];
623        value
624            .erased_serialize(&mut *encoding.serializer(&mut body).serializer())
625            .map_err(Error::internal)?;
626
627        let mut response = Response::new(make_body(body.into()));
628        response
629            .headers_mut()
630            .insert(CONTENT_TYPE, encoding.content_type());
631
632        Ok(response)
633    }
634}
635
636impl<T, W> SerializeResponse<T, W> for StdResponseSerializer
637where
638    T: Serialize,
639{
640    fn serialize(
641        runtime: &ConjureRuntime,
642        request_headers: &HeaderMap,
643        value: T,
644    ) -> Result<Response<ResponseBody<W>>, Error> {
645        Self::serialize_inner(runtime, request_headers, &value, ResponseBody::Fixed)
646    }
647}
648
649impl<T, W> AsyncSerializeResponse<T, W> for StdResponseSerializer
650where
651    T: Serialize,
652{
653    fn serialize(
654        runtime: &ConjureRuntime,
655        request_headers: &HeaderMap,
656        value: T,
657    ) -> Result<Response<AsyncResponseBody<W>>, Error> {
658        Self::serialize_inner(runtime, request_headers, &value, AsyncResponseBody::Fixed)
659    }
660}
661
662/// A trait implemented by header decoders used by custom Conjure server trait implementations.
663pub trait DecodeHeader<T> {
664    /// Decodes the value from headers.
665    fn decode<'a, I>(runtime: &ConjureRuntime, headers: I) -> Result<T, Error>
666    where
667        I: IntoIterator<Item = &'a HeaderValue>;
668}
669
670/// A trait implemented by URL parameter decoders used by custom Conjure server trait
671/// implementations.
672pub trait DecodeParam<T> {
673    /// Decodes the value from the sequence of values.
674    ///
675    /// The values have already been percent-decoded.
676    fn decode<I>(runtime: &ConjureRuntime, params: I) -> Result<T, Error>
677    where
678        I: IntoIterator,
679        I::Item: AsRef<str>;
680}
681
682/// A decoder which converts a single value using its [`FromStr`] implementation.
683pub enum FromStrDecoder {}
684
685impl<T> DecodeHeader<T> for FromStrDecoder
686where
687    T: FromStr,
688    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
689{
690    fn decode<'a, I>(_: &ConjureRuntime, headers: I) -> Result<T, Error>
691    where
692        I: IntoIterator<Item = &'a HeaderValue>,
693    {
694        only_item(headers)?
695            .to_str()
696            .map_err(|e| Error::service(e, InvalidArgument::new()))?
697            .parse()
698            .map_err(|e| Error::service(e, InvalidArgument::new()))
699    }
700}
701
702impl<T> DecodeParam<T> for FromStrDecoder
703where
704    T: FromStr,
705    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
706{
707    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<T, Error>
708    where
709        I: IntoIterator,
710        I::Item: AsRef<str>,
711    {
712        only_item(params)?
713            .as_ref()
714            .parse()
715            .map_err(|e| Error::service(e, InvalidArgument::new()))
716    }
717}
718
719/// A decoder which converts an optional value using its [`FromStr`] implementation.
720pub enum FromStrOptionDecoder {}
721
722impl<T> DecodeHeader<Option<T>> for FromStrOptionDecoder
723where
724    T: FromStr,
725    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
726{
727    fn decode<'a, I>(_: &ConjureRuntime, headers: I) -> Result<Option<T>, Error>
728    where
729        I: IntoIterator<Item = &'a HeaderValue>,
730    {
731        let Some(header) = optional_item(headers)? else {
732            return Ok(None);
733        };
734        let value = header
735            .to_str()
736            .map_err(|e| Error::service(e, InvalidArgument::new()))?
737            .parse()
738            .map_err(|e| Error::service(e, InvalidArgument::new()))?;
739        Ok(Some(value))
740    }
741}
742
743impl<T> DecodeParam<Option<T>> for FromStrOptionDecoder
744where
745    T: FromStr,
746    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
747{
748    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<Option<T>, Error>
749    where
750        I: IntoIterator,
751        I::Item: AsRef<str>,
752    {
753        let Some(param) = optional_item(params)? else {
754            return Ok(None);
755        };
756        let value = param
757            .as_ref()
758            .parse()
759            .map_err(|e| Error::service(e, InvalidArgument::new()))?;
760        Ok(Some(value))
761    }
762}
763
764fn optional_item<I>(it: I) -> Result<Option<I::Item>, Error>
765where
766    I: IntoIterator,
767{
768    let mut it = it.into_iter();
769    let Some(item) = it.next() else {
770        return Ok(None);
771    };
772
773    let remaining = it.count();
774    if remaining > 0 {
775        return Err(
776            Error::service_safe("expected at most 1 parameter", InvalidArgument::new())
777                .with_safe_param("actual", remaining + 1),
778        );
779    }
780
781    Ok(Some(item))
782}
783
784/// A decoder which converts a sequence of values via its [`FromStr`] implementation into a
785/// collection via a [`FromIterator`] implementation.
786pub struct FromStrSeqDecoder<U> {
787    _p: PhantomData<U>,
788}
789
790impl<T, U> DecodeParam<T> for FromStrSeqDecoder<U>
791where
792    T: FromIterator<U>,
793    U: FromStr,
794    U::Err: Into<Box<dyn error::Error + Sync + Send>>,
795{
796    fn decode<I>(_: &ConjureRuntime, params: I) -> Result<T, Error>
797    where
798        I: IntoIterator,
799        I::Item: AsRef<str>,
800    {
801        params
802            .into_iter()
803            .map(|s| {
804                s.as_ref()
805                    .parse()
806                    .map_err(|e| Error::service(e, InvalidArgument::new()))
807            })
808            .collect()
809    }
810}
811
812fn only_item<I>(it: I) -> Result<I::Item, Error>
813where
814    I: IntoIterator,
815{
816    let mut it = it.into_iter();
817    let Some(item) = it.next() else {
818        return Err(
819            Error::service_safe("expected exactly 1 parameter", InvalidArgument::new())
820                .with_safe_param("actual", 0),
821        );
822    };
823
824    let remaining = it.count();
825    if remaining > 0 {
826        return Err(
827            Error::service_safe("expected exactly 1 parameter", InvalidArgument::new())
828                .with_safe_param("actual", remaining + 1),
829        );
830    }
831
832    Ok(item)
833}
834
835/// A decoder which maps the output of another with [`From::from`].
836pub struct FromDecoder<D, U> {
837    _p: PhantomData<(D, U)>,
838}
839
840impl<T, D, U> DecodeParam<T> for FromDecoder<D, U>
841where
842    T: From<U>,
843    D: DecodeParam<U>,
844{
845    fn decode<I>(runtime: &ConjureRuntime, params: I) -> Result<T, Error>
846    where
847        I: IntoIterator,
848        I::Item: AsRef<str>,
849    {
850        D::decode(runtime, params).map(T::from)
851    }
852}
853
854impl<T, D, U> DecodeHeader<T> for FromDecoder<D, U>
855where
856    T: From<U>,
857    D: DecodeHeader<U>,
858{
859    fn decode<'a, I>(runtime: &ConjureRuntime, headers: I) -> Result<T, Error>
860    where
861        I: IntoIterator<Item = &'a HeaderValue>,
862    {
863        D::decode(runtime, headers).map(T::from)
864    }
865}