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