Skip to main content

progenitor_client/
progenitor_client.rs

1// Copyright 2025 Oxide Computer Company
2
3#![allow(dead_code)]
4
5//! Support code for generated clients.
6
7use std::ops::{Deref, DerefMut};
8
9use bytes::Bytes;
10use futures_core::Stream;
11use reqwest::RequestBuilder;
12use serde::{Serialize, de::DeserializeOwned, ser::SerializeStruct};
13
14#[cfg(not(target_arch = "wasm32"))]
15type InnerByteStream = std::pin::Pin<Box<dyn Stream<Item = reqwest::Result<Bytes>> + Send + Sync>>;
16
17#[cfg(target_arch = "wasm32")]
18type InnerByteStream = std::pin::Pin<Box<dyn Stream<Item = reqwest::Result<Bytes>>>>;
19
20/// Untyped byte stream used for both success and error responses.
21pub struct ByteStream(InnerByteStream);
22
23impl ByteStream {
24    /// Creates a new ByteStream
25    ///
26    /// Useful for generating test fixtures.
27    pub fn new(inner: InnerByteStream) -> Self {
28        Self(inner)
29    }
30
31    /// Consumes the [`ByteStream`] and return its inner [`Stream`].
32    pub fn into_inner(self) -> InnerByteStream {
33        self.0
34    }
35}
36
37impl Deref for ByteStream {
38    type Target = InnerByteStream;
39
40    fn deref(&self) -> &Self::Target {
41        &self.0
42    }
43}
44
45impl DerefMut for ByteStream {
46    fn deref_mut(&mut self) -> &mut Self::Target {
47        &mut self.0
48    }
49}
50
51/// Interface for which an implementation is generated for all clients.
52pub trait ClientInfo<Inner> {
53    /// Get the version of this API.
54    ///
55    /// This string is pulled directly from the source OpenAPI document and may
56    /// be in any format the API selects.
57    fn api_version() -> &'static str;
58
59    /// Get the base URL to which requests are made.
60    fn baseurl(&self) -> &str;
61
62    /// Get the internal `reqwest::Client` used to make requests.
63    fn client(&self) -> &reqwest::Client;
64
65    /// Get the inner value of type `T` if one is specified.
66    fn inner(&self) -> &Inner;
67}
68
69impl<T, Inner> ClientInfo<Inner> for &T
70where
71    T: ClientInfo<Inner>,
72{
73    fn api_version() -> &'static str {
74        T::api_version()
75    }
76
77    fn baseurl(&self) -> &str {
78        (*self).baseurl()
79    }
80
81    fn client(&self) -> &reqwest::Client {
82        (*self).client()
83    }
84
85    fn inner(&self) -> &Inner {
86        (*self).inner()
87    }
88}
89
90/// Information about an operation, consumed by hook implementations.
91pub struct OperationInfo {
92    /// The corresponding operationId from the source OpenAPI document.
93    pub operation_id: &'static str,
94}
95
96/// Interface for changing the behavior of generated clients. All clients
97/// implement this for `&Client`; to override the default behavior, implement
98/// some or all of the interfaces for the `Client` type (without the
99/// reference). This mechanism relies on so-called "auto-ref specialization".
100#[allow(async_fn_in_trait, unused)]
101#[diagnostic::on_unimplemented(
102    message = "`{Self}` does not implement `ClientHooks<{Inner}>`",
103    label = "missing `impl ClientHooks<{Inner}> for {Self}`",
104    note = "clients generated with `hooks = Expected` require `impl ClientHooks<{Inner}> for {Self}`"
105)]
106pub trait ClientHooks<Inner = ()>
107where
108    Self: ClientInfo<Inner>,
109{
110    /// Runs prior to the execution of the request. This may be used to modify
111    /// the request before it is transmitted.
112    async fn pre<E>(
113        &self,
114        request: &mut reqwest::Request,
115        info: &OperationInfo,
116    ) -> std::result::Result<(), Error<E>> {
117        Ok(())
118    }
119
120    /// Runs after completion of the request.
121    async fn post<E>(
122        &self,
123        result: &reqwest::Result<reqwest::Response>,
124        info: &OperationInfo,
125    ) -> std::result::Result<(), Error<E>> {
126        Ok(())
127    }
128
129    /// Execute the request. Note that for almost any reasonable implementation
130    /// this will include code equivalent to this:
131    /// ```
132    /// # use progenitor_client::{ClientHooks, ClientInfo, OperationInfo};
133    /// # struct X;
134    /// # impl ClientInfo<()> for X {
135    /// #   fn api_version() -> &'static str { panic!() }
136    /// #   fn baseurl(&self) -> &str { panic!() }
137    /// #   fn client(&self) -> &reqwest::Client { panic!() }
138    /// #   fn inner(&self) -> &() { panic!() }
139    /// # }
140    /// # impl ClientHooks for X {
141    /// #   async fn exec(
142    /// #       &self,
143    /// #       request: reqwest::Request,
144    /// #       info: &OperationInfo,
145    /// #   ) -> reqwest::Result<reqwest::Response> {
146    ///         self.client().execute(request).await
147    /// #   }
148    /// # }
149    /// ```
150    async fn exec(
151        &self,
152        request: reqwest::Request,
153        info: &OperationInfo,
154    ) -> reqwest::Result<reqwest::Response> {
155        self.client().execute(request).await
156    }
157}
158
159/// Typed value returned by generated client methods.
160///
161/// This is used for successful responses and may appear in error responses
162/// generated from the server (see [`Error::ErrorResponse`])
163pub struct ResponseValue<T> {
164    inner: T,
165    status: reqwest::StatusCode,
166    headers: reqwest::header::HeaderMap,
167    // TODO cookies?
168}
169
170impl<T: DeserializeOwned> ResponseValue<T> {
171    #[doc(hidden)]
172    pub async fn from_response<E>(response: reqwest::Response) -> Result<Self, Error<E>> {
173        let status = response.status();
174        let headers = response.headers().clone();
175        let full = response.bytes().await.map_err(Error::ResponseBodyError)?;
176        let inner =
177            serde_json::from_slice(&full).map_err(|e| Error::InvalidResponsePayload(full, e))?;
178
179        Ok(Self {
180            inner,
181            status,
182            headers,
183        })
184    }
185}
186
187#[cfg(not(target_arch = "wasm32"))]
188impl ResponseValue<reqwest::Upgraded> {
189    #[doc(hidden)]
190    pub async fn upgrade<E: std::fmt::Debug>(
191        response: reqwest::Response,
192    ) -> Result<Self, Error<E>> {
193        let status = response.status();
194        let headers = response.headers().clone();
195        if status == reqwest::StatusCode::SWITCHING_PROTOCOLS {
196            let inner = response.upgrade().await.map_err(Error::InvalidUpgrade)?;
197
198            Ok(Self {
199                inner,
200                status,
201                headers,
202            })
203        } else {
204            Err(Error::UnexpectedResponse(response))
205        }
206    }
207}
208
209impl ResponseValue<ByteStream> {
210    #[doc(hidden)]
211    pub fn stream(response: reqwest::Response) -> Self {
212        let status = response.status();
213        let headers = response.headers().clone();
214        Self {
215            inner: ByteStream(Box::pin(response.bytes_stream())),
216            status,
217            headers,
218        }
219    }
220}
221
222impl ResponseValue<()> {
223    #[doc(hidden)]
224    pub fn empty(response: reqwest::Response) -> Self {
225        let status = response.status();
226        let headers = response.headers().clone();
227        // TODO is there anything we want to do to confirm that there is no
228        // content?
229        Self {
230            inner: (),
231            status,
232            headers,
233        }
234    }
235}
236
237impl<T> ResponseValue<T> {
238    /// Creates a [`ResponseValue`] from the inner type, status, and headers.
239    ///
240    /// Useful for generating test fixtures.
241    pub fn new(inner: T, status: reqwest::StatusCode, headers: reqwest::header::HeaderMap) -> Self {
242        Self {
243            inner,
244            status,
245            headers,
246        }
247    }
248
249    /// Consumes the ResponseValue, returning the wrapped value.
250    pub fn into_inner(self) -> T {
251        self.inner
252    }
253
254    /// Gets the status from this response.
255    pub fn status(&self) -> reqwest::StatusCode {
256        self.status
257    }
258
259    /// Gets the headers from this response.
260    pub fn headers(&self) -> &reqwest::header::HeaderMap {
261        &self.headers
262    }
263
264    /// Gets the parsed value of the Content-Length header, if present and
265    /// valid.
266    pub fn content_length(&self) -> Option<u64> {
267        self.headers
268            .get(reqwest::header::CONTENT_LENGTH)?
269            .to_str()
270            .ok()?
271            .parse::<u64>()
272            .ok()
273    }
274
275    #[doc(hidden)]
276    pub fn map<U: std::fmt::Debug, F, E>(self, f: F) -> Result<ResponseValue<U>, E>
277    where
278        F: FnOnce(T) -> U,
279    {
280        let Self {
281            inner,
282            status,
283            headers,
284        } = self;
285
286        Ok(ResponseValue {
287            inner: f(inner),
288            status,
289            headers,
290        })
291    }
292}
293
294impl ResponseValue<ByteStream> {
295    /// Consumes the `ResponseValue`, returning the wrapped [`Stream`].
296    pub fn into_inner_stream(self) -> InnerByteStream {
297        self.into_inner().into_inner()
298    }
299}
300
301impl<T> Deref for ResponseValue<T> {
302    type Target = T;
303
304    fn deref(&self) -> &Self::Target {
305        &self.inner
306    }
307}
308
309impl<T> DerefMut for ResponseValue<T> {
310    fn deref_mut(&mut self) -> &mut Self::Target {
311        &mut self.inner
312    }
313}
314
315impl<T> AsRef<T> for ResponseValue<T> {
316    fn as_ref(&self) -> &T {
317        &self.inner
318    }
319}
320
321impl<T: std::fmt::Debug> std::fmt::Debug for ResponseValue<T> {
322    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323        self.inner.fmt(f)
324    }
325}
326
327/// Error produced by generated client methods.
328///
329/// The type parameter may be a struct if there's a single expected error type
330/// or an enum if there are multiple valid error types. It can be the unit type
331/// if there are no structured returns expected.
332pub enum Error<E = ()> {
333    /// The request did not conform to API requirements.
334    InvalidRequest(String),
335
336    /// A server error either due to the data, or with the connection.
337    CommunicationError(reqwest::Error),
338
339    /// An expected response when upgrading connection.
340    InvalidUpgrade(reqwest::Error),
341
342    /// A documented, expected error response.
343    ErrorResponse(ResponseValue<E>),
344
345    /// Encountered an error reading the body for an expected response.
346    ResponseBodyError(reqwest::Error),
347
348    /// An expected response code whose deserialization failed.
349    InvalidResponsePayload(Bytes, serde_json::Error),
350
351    /// A response not listed in the API description. This may represent a
352    /// success or failure response; check `status().is_success()`.
353    UnexpectedResponse(reqwest::Response),
354
355    /// A custom error from a consumer-defined hook.
356    Custom(String),
357}
358
359impl<E> Error<E> {
360    /// Returns the status code, if the error was generated from a response.
361    pub fn status(&self) -> Option<reqwest::StatusCode> {
362        match self {
363            Error::InvalidRequest(_) => None,
364            Error::Custom(_) => None,
365            Error::CommunicationError(e) => e.status(),
366            Error::ErrorResponse(rv) => Some(rv.status()),
367            Error::InvalidUpgrade(e) => e.status(),
368            Error::ResponseBodyError(e) => e.status(),
369            Error::InvalidResponsePayload(_, _) => None,
370            Error::UnexpectedResponse(r) => Some(r.status()),
371        }
372    }
373
374    /// Returns `true` if this error indicates that it is transient, and that
375    /// the operation could succeed if retried.
376    ///
377    /// The following are considered retryable:
378    ///
379    /// * Communication errors (connection reset, timeout, etc.)
380    /// * 408 Request Timeout
381    /// * 429 Too Many Requests
382    /// * 502 Bad Gateway
383    /// * 503 Service Unavailable
384    /// * 504 Gateway Timeout
385    pub fn is_retryable(&self) -> bool {
386        match self {
387            Error::CommunicationError(_) => true,
388            Error::ErrorResponse(rv) => is_retryable_status(rv.status()),
389            Error::UnexpectedResponse(r) => is_retryable_status(r.status()),
390            Error::InvalidUpgrade(e) | Error::ResponseBodyError(e) => {
391                // We expect InvalidUpgrade and ResponseBodyError to be 2xx
392                // statuses.
393                //
394                // * If they are 2xx statuses, is_retryable_status returns
395                //   false so there's no harm in checking.
396                // * In the unlikely case that they are not 2xx statuses
397                //   (e.g., the error type was created outside of Progenitor),
398                //   it is appropriate to check for retryability.
399                e.status().is_some_and(is_retryable_status)
400            }
401            Error::InvalidRequest(_) => false,
402            Error::InvalidResponsePayload(_, _) => false,
403            Error::Custom(_) => false,
404        }
405    }
406
407    /// Converts this error into one without a typed body.
408    ///
409    /// This is useful for unified error handling with APIs that distinguish
410    /// various error response bodies.
411    pub fn into_untyped(self) -> Error {
412        match self {
413            Error::InvalidRequest(s) => Error::InvalidRequest(s),
414            Error::Custom(s) => Error::Custom(s),
415            Error::CommunicationError(e) => Error::CommunicationError(e),
416            Error::ErrorResponse(ResponseValue {
417                inner: _,
418                status,
419                headers,
420            }) => Error::ErrorResponse(ResponseValue {
421                inner: (),
422                status,
423                headers,
424            }),
425            Error::InvalidUpgrade(e) => Error::InvalidUpgrade(e),
426            Error::ResponseBodyError(e) => Error::ResponseBodyError(e),
427            Error::InvalidResponsePayload(b, e) => Error::InvalidResponsePayload(b, e),
428            Error::UnexpectedResponse(r) => Error::UnexpectedResponse(r),
429        }
430    }
431}
432
433impl<E> From<std::convert::Infallible> for Error<E> {
434    fn from(x: std::convert::Infallible) -> Self {
435        match x {}
436    }
437}
438
439impl<E> From<reqwest::Error> for Error<E> {
440    fn from(e: reqwest::Error) -> Self {
441        Self::CommunicationError(e)
442    }
443}
444
445impl<E> From<reqwest::header::InvalidHeaderValue> for Error<E> {
446    fn from(e: reqwest::header::InvalidHeaderValue) -> Self {
447        Self::InvalidRequest(e.to_string())
448    }
449}
450
451impl<E> std::fmt::Display for Error<E>
452where
453    ResponseValue<E>: ErrorFormat,
454{
455    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
456        match self {
457            Error::InvalidRequest(s) => {
458                write!(f, "Invalid Request: {}", s)?;
459            }
460            Error::CommunicationError(e) => {
461                write!(f, "Communication Error: {}", e)?;
462            }
463            Error::ErrorResponse(rve) => {
464                write!(f, "Error Response: ")?;
465                rve.fmt_info(f)?;
466            }
467            Error::InvalidUpgrade(e) => {
468                write!(f, "Invalid Response Upgrade: {}", e)?;
469            }
470            Error::ResponseBodyError(e) => {
471                write!(f, "Invalid Response Body Bytes: {}", e)?;
472            }
473            Error::InvalidResponsePayload(b, e) => {
474                write!(f, "Invalid Response Payload ({:?}): {}", b, e)?;
475            }
476            Error::UnexpectedResponse(r) => {
477                write!(f, "Unexpected Response: {:?}", r)?;
478            }
479            Error::Custom(s) => {
480                write!(f, "Error: {}", s)?;
481            }
482        }
483
484        if f.alternate() {
485            use std::error::Error as _;
486
487            let mut src = self.source().and_then(|e| e.source());
488            while let Some(s) = src {
489                write!(f, ": {s}")?;
490                src = s.source();
491            }
492        }
493        Ok(())
494    }
495}
496
497trait ErrorFormat {
498    fn fmt_info(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
499}
500
501impl<E> ErrorFormat for ResponseValue<E>
502where
503    E: std::fmt::Debug,
504{
505    fn fmt_info(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506        write!(
507            f,
508            "status: {}; headers: {:?}; value: {:?}",
509            self.status, self.headers, self.inner,
510        )
511    }
512}
513
514impl ErrorFormat for ResponseValue<ByteStream> {
515    fn fmt_info(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516        write!(
517            f,
518            "status: {}; headers: {:?}; value: <stream>",
519            self.status, self.headers,
520        )
521    }
522}
523
524impl<E> std::fmt::Debug for Error<E>
525where
526    ResponseValue<E>: ErrorFormat,
527{
528    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529        std::fmt::Display::fmt(self, f)
530    }
531}
532impl<E> std::error::Error for Error<E>
533where
534    ResponseValue<E>: ErrorFormat,
535{
536    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
537        match self {
538            Error::CommunicationError(e) => Some(e),
539            Error::InvalidUpgrade(e) => Some(e),
540            Error::ResponseBodyError(e) => Some(e),
541            Error::InvalidResponsePayload(_b, e) => Some(e),
542            _ => None,
543        }
544    }
545}
546
547/// Returns `true` if the given HTTP status code is considered transient and
548/// worth retrying.
549fn is_retryable_status(status: reqwest::StatusCode) -> bool {
550    matches!(
551        status,
552        reqwest::StatusCode::REQUEST_TIMEOUT
553            | reqwest::StatusCode::TOO_MANY_REQUESTS
554            | reqwest::StatusCode::BAD_GATEWAY
555            | reqwest::StatusCode::SERVICE_UNAVAILABLE
556            | reqwest::StatusCode::GATEWAY_TIMEOUT
557    )
558}
559
560// See https://url.spec.whatwg.org/#url-path-segment-string
561const PATH_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
562    .add(b' ')
563    .add(b'"')
564    .add(b'#')
565    .add(b'<')
566    .add(b'>')
567    .add(b'?')
568    .add(b'`')
569    .add(b'{')
570    .add(b'}')
571    .add(b'/')
572    .add(b'%');
573
574#[doc(hidden)]
575/// Percent encode input string.
576pub fn encode_path(pc: &str) -> String {
577    percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
578}
579
580#[doc(hidden)]
581pub trait RequestBuilderExt<E> {
582    fn form_urlencoded<T: Serialize + ?Sized>(self, body: &T) -> Result<RequestBuilder, Error<E>>;
583}
584
585impl<E> RequestBuilderExt<E> for RequestBuilder {
586    fn form_urlencoded<T: Serialize + ?Sized>(self, body: &T) -> Result<Self, Error<E>> {
587        Ok(self
588            .header(
589                reqwest::header::CONTENT_TYPE,
590                reqwest::header::HeaderValue::from_static("application/x-www-form-urlencoded"),
591            )
592            .body(
593                serde_urlencoded::to_string(body)
594                    .map_err(|_| Error::InvalidRequest("failed to serialize body".to_string()))?,
595            ))
596    }
597}
598
599#[doc(hidden)]
600pub struct QueryParam<'a, T> {
601    name: &'a str,
602    value: &'a T,
603}
604
605impl<'a, T> QueryParam<'a, T> {
606    #[doc(hidden)]
607    pub fn new(name: &'a str, value: &'a T) -> Self {
608        Self { name, value }
609    }
610}
611impl<T> Serialize for QueryParam<'_, T>
612where
613    T: Serialize,
614{
615    fn serialize<S>(&self, inner: S) -> Result<S::Ok, S::Error>
616    where
617        S: serde::Serializer,
618    {
619        let serializer = QuerySerializer {
620            inner,
621            name: self.name,
622        };
623        self.value.serialize(serializer)
624    }
625}
626
627pub(crate) struct QuerySerializer<'a, S> {
628    inner: S,
629    name: &'a str,
630}
631
632macro_rules! serialize_scalar {
633    ($f:ident, $t:ty) => {
634        fn $f(self, v: $t) -> Result<Self::Ok, Self::Error> {
635            [(self.name, v)].serialize(self.inner)
636        }
637    };
638}
639
640impl<'a, S> serde::Serializer for QuerySerializer<'a, S>
641where
642    S: serde::Serializer,
643{
644    type Ok = S::Ok;
645    type Error = S::Error;
646    type SerializeSeq = QuerySeq<'a, S::SerializeSeq>;
647    type SerializeTuple = S::SerializeTuple;
648    type SerializeTupleStruct = S::SerializeTupleStruct;
649    type SerializeTupleVariant = S::SerializeTupleVariant;
650    type SerializeMap = S::SerializeMap;
651    type SerializeStruct = S::SerializeStruct;
652    type SerializeStructVariant = S::SerializeStructVariant;
653
654    serialize_scalar!(serialize_bool, bool);
655    serialize_scalar!(serialize_i8, i8);
656    serialize_scalar!(serialize_i16, i16);
657    serialize_scalar!(serialize_i32, i32);
658    serialize_scalar!(serialize_i64, i64);
659    serialize_scalar!(serialize_u8, u8);
660    serialize_scalar!(serialize_u16, u16);
661    serialize_scalar!(serialize_u32, u32);
662    serialize_scalar!(serialize_u64, u64);
663    serialize_scalar!(serialize_f32, f32);
664    serialize_scalar!(serialize_f64, f64);
665    serialize_scalar!(serialize_char, char);
666    serialize_scalar!(serialize_str, &str);
667
668    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
669        self.inner.serialize_bytes(v)
670    }
671
672    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
673        self.inner.serialize_none()
674    }
675
676    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
677    where
678        T: ?Sized + Serialize,
679    {
680        // Serialize the value through self which will proxy into the inner
681        // Serializer as appropriate.
682        value.serialize(self)
683    }
684
685    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
686        self.inner.serialize_unit()
687    }
688
689    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
690        self.inner.serialize_unit_struct(name)
691    }
692
693    fn serialize_unit_variant(
694        self,
695        _name: &'static str,
696        _variant_index: u32,
697        variant: &'static str,
698    ) -> Result<Self::Ok, Self::Error> {
699        // A query parameter with a list of enumerated values will produce an
700        // enum with unit variants. We treat these as scalar values, ignoring
701        // the unit variant wrapper.
702        variant.serialize(self)
703    }
704
705    fn serialize_newtype_struct<T>(
706        self,
707        name: &'static str,
708        value: &T,
709    ) -> Result<Self::Ok, Self::Error>
710    where
711        T: ?Sized + Serialize,
712    {
713        self.inner.serialize_newtype_struct(name, value)
714    }
715
716    fn serialize_newtype_variant<T>(
717        self,
718        name: &'static str,
719        _variant_index: u32,
720        variant: &'static str,
721        value: &T,
722    ) -> Result<Self::Ok, Self::Error>
723    where
724        T: ?Sized + Serialize,
725    {
726        // As with serde_json, we treat a newtype variant like a struct with a
727        // single field. This may seem a little weird, but if an OpenAPI
728        // document were to specify a query parameter whose schema was a oneOf
729        // whose elements were objects with a single field, the user would end
730        // up with an enum like this as a parameter.
731        let mut map = self.inner.serialize_struct(name, 1)?;
732        map.serialize_field(variant, value)?;
733        map.end()
734    }
735
736    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
737        let Self { inner, name, .. } = self;
738        Ok(QuerySeq {
739            inner: inner.serialize_seq(len)?,
740            name,
741        })
742    }
743
744    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
745        self.inner.serialize_tuple(len)
746    }
747
748    fn serialize_tuple_struct(
749        self,
750        name: &'static str,
751        len: usize,
752    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
753        self.inner.serialize_tuple_struct(name, len)
754    }
755
756    fn serialize_tuple_variant(
757        self,
758        name: &'static str,
759        variant_index: u32,
760        variant: &'static str,
761        len: usize,
762    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
763        self.inner
764            .serialize_tuple_variant(name, variant_index, variant, len)
765    }
766
767    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
768        self.inner.serialize_map(len)
769    }
770
771    fn serialize_struct(
772        self,
773        name: &'static str,
774        len: usize,
775    ) -> Result<Self::SerializeStruct, Self::Error> {
776        self.inner.serialize_struct(name, len)
777    }
778
779    fn serialize_struct_variant(
780        self,
781        name: &'static str,
782        variant_index: u32,
783        variant: &'static str,
784        len: usize,
785    ) -> Result<Self::SerializeStructVariant, Self::Error> {
786        self.inner
787            .serialize_struct_variant(name, variant_index, variant, len)
788    }
789}
790
791#[doc(hidden)]
792pub struct QuerySeq<'a, S> {
793    inner: S,
794    name: &'a str,
795}
796
797impl<S> serde::ser::SerializeSeq for QuerySeq<'_, S>
798where
799    S: serde::ser::SerializeSeq,
800{
801    type Ok = S::Ok;
802
803    type Error = S::Error;
804
805    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
806    where
807        T: ?Sized + Serialize,
808    {
809        let v = (self.name, value);
810        self.inner.serialize_element(&v)
811    }
812
813    fn end(self) -> Result<Self::Ok, Self::Error> {
814        self.inner.end()
815    }
816}