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