Skip to main content

satay_runtime/
lib.rs

1#![forbid(unsafe_code)]
2
3use std::str::FromStr;
4use std::{
5    fmt::{self, Debug, Formatter},
6    marker,
7};
8
9use http::header::{self, CONTENT_TYPE, HeaderName, HeaderValue};
10#[cfg(feature = "json")]
11use serde::de;
12#[cfg(feature = "json")]
13pub use serde_json::Value as JsonValue;
14use time::Month;
15use time::format_description::well_known::Rfc3339;
16pub use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
17pub use url::Url;
18
19use tracing::{debug, instrument};
20
21/// Operations required of dynamic strings in generated models and builders.
22///
23/// Serde bounds are applied separately by generated codecs. Implemented
24/// automatically for compatible containers, including `String` and `Box<str>`.
25pub trait StringStorage: AsRef<str> + From<String> + Clone + Debug + Eq + Ord {}
26
27impl<T> StringStorage for T where T: AsRef<str> + From<String> + Clone + Debug + Eq + Ord {}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct RequestParts<B> {
31    pub method: http::Method,
32    pub uri: String,
33    pub headers: http::HeaderMap,
34    pub body: B,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ResponseParts<B> {
39    pub status: http::StatusCode,
40    pub headers: http::HeaderMap,
41    pub body: B,
42}
43
44impl<B: AsRef<[u8]>> ResponseParts<B> {
45    /// Borrows the body while retaining owned HTTP metadata in the returned parts.
46    pub fn as_bytes(&self) -> ResponseParts<&[u8]> {
47        ResponseParts {
48            status: self.status,
49            headers: self.headers.clone(),
50            body: self.body.as_ref(),
51        }
52    }
53}
54
55/// A transport-owned response whose decoded model may borrow its body.
56pub struct BufferedResponse<A, B> {
57    parts: ResponseParts<B>,
58    action: marker::PhantomData<fn() -> A>,
59}
60
61impl<A, B: Debug> Debug for BufferedResponse<A, B> {
62    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63        f.debug_struct("BufferedResponse")
64            .field("parts", &self.parts)
65            .finish()
66    }
67}
68
69impl<A, B> BufferedResponse<A, B> {
70    /// Associates transport response parts with their action's decoder.
71    pub fn new(parts: ResponseParts<B>) -> Self {
72        Self {
73            parts,
74            action: marker::PhantomData,
75        }
76    }
77
78    /// Inspects the status, headers, and original body container.
79    pub fn parts(&self) -> &ResponseParts<B> {
80        &self.parts
81    }
82
83    /// Recovers the original transport response without decoding.
84    pub fn into_parts(self) -> ResponseParts<B> {
85        self.parts
86    }
87}
88
89impl<A: Action, B: AsRef<[u8]>> BufferedResponse<A, B> {
90    /// Decodes a model borrowing this response, if supported by the action.
91    ///
92    /// # Errors
93    /// Returns the action's decoding error for invalid response data.
94    pub fn decode(&self) -> Result<A::Response<'_>, Error> {
95        A::decode(self.parts.as_bytes())
96    }
97}
98
99impl<A: OwnedAction, B: AsRef<[u8]>> BufferedResponse<A, B> {
100    /// Decodes an owned response and releases the transport buffer.
101    ///
102    /// # Errors
103    /// Returns the action's decoding error for invalid response data.
104    pub fn decode_owned(self) -> Result<A::OwnedResponse, Error> {
105        let ResponseParts {
106            status,
107            headers,
108            body,
109        } = self.parts;
110        A::decode_owned(ResponseParts {
111            status,
112            headers,
113            body: body.as_ref(),
114        })
115    }
116}
117
118#[derive(Debug, thiserror::Error)]
119pub enum Error {
120    #[error("failed to build HTTP message: {0}")]
121    Http(#[from] http::Error),
122
123    #[error("invalid HTTP header value: {0}")]
124    InvalidHeaderValue(#[from] header::InvalidHeaderValue),
125
126    #[error("invalid HTTP header name: {0}")]
127    InvalidHeaderName(#[from] header::InvalidHeaderName),
128
129    #[error("missing required field `{0}`")]
130    MissingRequired(&'static str),
131
132    #[error("{0}")]
133    InvalidResponse(&'static str),
134
135    #[cfg(feature = "json")]
136    #[error("JSON error: {0}")]
137    Json(#[from] serde_json::Error),
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
141pub enum ParseRangeError {
142    #[error("range contains more than one `-` separator")]
143    TooManySeparators,
144
145    #[error("invalid range minimum `{value}`: {message}")]
146    InvalidMinimum { value: String, message: String },
147
148    #[error("invalid range maximum `{value}`: {message}")]
149    InvalidMaximum { value: String, message: String },
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
153pub enum ParseTimeError {
154    #[error("time must be exactly four ASCII digits in HHMM format")]
155    InvalidFormat,
156
157    #[error("time is outside valid HHMM range")]
158    ComponentRange,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
162pub enum ParseDateError {
163    #[error("date must be in YYYY-MM-DD format")]
164    InvalidFormat,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
168pub enum ParseNaiveDateTimeError {
169    #[error("datetime must be in YYYY-MM-DDTHH:mm:ss format")]
170    InvalidFormat,
171}
172
173pub trait Action {
174    /// Container produced when encoding the request.
175    type RequestBody;
176    /// Decoded response, potentially borrowing the supplied body.
177    type Response<'de>;
178
179    /// Builds the HTTP request for this action.
180    ///
181    /// # Errors
182    ///
183    /// Returns an error if request construction or required input validation fails.
184    fn request(self) -> Result<http::Request<Self::RequestBody>, Error>;
185
186    /// Decodes the HTTP response body into this action's response type.
187    ///
188    /// # Errors
189    ///
190    /// Returns an error if the response is invalid or cannot be decoded.
191    fn decode(response: ResponseParts<&[u8]>) -> Result<Self::Response<'_>, Error>;
192}
193
194/// An action that can decode a response independently of the input buffer's lifetime.
195///
196/// Transports use this contract for their one-step `send_with` methods. Actions
197/// that only support borrowing can implement [`Action`] alone and use buffering.
198pub trait OwnedAction: Action {
199    /// Decoded value that can outlive the HTTP response buffer.
200    type OwnedResponse;
201
202    /// Decodes without retaining references to the supplied response body.
203    ///
204    /// # Errors
205    /// Returns an error if the response is invalid or cannot be decoded.
206    fn decode_owned(response: ResponseParts<&[u8]>) -> Result<Self::OwnedResponse, Error>;
207}
208
209/// Converts request parts into an HTTP request.
210///
211/// # Errors
212///
213/// Returns an error if the method, URI, or body cannot be converted into an HTTP request.
214#[instrument(skip_all, fields(method = %method, uri = %uri))]
215pub fn into_request<B>(
216    RequestParts {
217        method,
218        uri,
219        headers,
220        body,
221    }: RequestParts<B>,
222) -> Result<http::Request<B>, Error> {
223    debug!("building HTTP request");
224    let mut request = http::Request::builder()
225        .method(method)
226        .uri(uri)
227        .body(body)?;
228    *request.headers_mut() = headers;
229    Ok(request)
230}
231
232/// Converts request parts with an empty body into an HTTP request.
233///
234/// # Errors
235///
236/// Returns an error if the method or URI cannot be converted into an HTTP request.
237#[instrument(skip_all, fields(method = %method, uri = %uri))]
238pub fn into_empty_request(
239    RequestParts {
240        method,
241        uri,
242        headers,
243        body: (),
244    }: RequestParts<()>,
245) -> Result<http::Request<Vec<u8>>, Error> {
246    debug!("building empty HTTP request");
247    let mut request = http::Request::builder()
248        .method(method)
249        .uri(uri)
250        .body(vec![])?;
251    *request.headers_mut() = headers;
252    Ok(request)
253}
254
255/// Converts serializable request parts into a JSON HTTP request.
256///
257/// # Errors
258///
259/// Returns an error if JSON serialization fails or the HTTP request cannot be built.
260#[cfg(feature = "json")]
261#[instrument(skip_all, fields(method = %method, uri = %uri))]
262pub fn into_json_request<T>(
263    RequestParts {
264        method,
265        uri,
266        headers,
267        body,
268    }: RequestParts<T>,
269) -> Result<http::Request<Vec<u8>>, Error>
270where
271    T: serde::Serialize,
272{
273    debug!("building JSON HTTP request");
274
275    let body = serde_json::to_vec(&body)?;
276    let mut request = http::Request::builder()
277        .method(method)
278        .uri(uri)
279        .body(body)?;
280
281    *request.headers_mut() = headers;
282
283    if !request.headers().contains_key(CONTENT_TYPE) {
284        request.headers_mut().insert(
285            CONTENT_TYPE,
286            http::HeaderValue::from_static("application/json"),
287        );
288    }
289
290    Ok(request)
291}
292
293/// Converts optional serializable request parts into a JSON HTTP request.
294///
295/// # Errors
296///
297/// Returns an error if JSON serialization fails or the HTTP request cannot be built.
298#[cfg(feature = "json")]
299#[instrument(skip_all, fields(method = %method, uri = %uri))]
300pub fn into_optional_json_request<T>(
301    RequestParts {
302        method,
303        uri,
304        headers,
305        body,
306    }: RequestParts<Option<T>>,
307) -> Result<http::Request<Vec<u8>>, Error>
308where
309    T: serde::Serialize,
310{
311    match body {
312        Some(body) => into_json_request(RequestParts {
313            method,
314            uri,
315            headers,
316            body,
317        }),
318        None => into_empty_request(RequestParts {
319            method,
320            uri,
321            headers,
322            body: (),
323        }),
324    }
325}
326
327/// Deserializes a JSON response body from bytes.
328///
329/// # Errors
330///
331/// Returns an error if the body is not valid JSON for `T`.
332#[cfg(feature = "json")]
333#[instrument(skip_all)]
334pub fn from_json_slice<'de, T>(body: &'de [u8]) -> Result<T, Error>
335where
336    T: serde::Deserialize<'de>,
337{
338    debug!("deserializing JSON response");
339    Ok(serde_json::from_slice(body)?)
340}
341
342/// Deserializes a projected JSON response body from bytes.
343///
344/// The top-level `unwrap_field` is selected first. When `map_field` is set, the
345/// unwrapped value must be an array of objects and that field is selected from
346/// every item. Missing fields become JSON `null`, allowing the projected Rust
347/// type's normal serde rules to distinguish optional and required values.
348///
349/// # Errors
350///
351/// Returns an error when the response does not have the configured container
352/// shape or when the projected JSON cannot be deserialized as `T`.
353#[cfg(feature = "json")]
354#[instrument(skip_all)]
355pub fn from_projected_json_slice<T>(
356    body: &[u8],
357    unwrap_field: &str,
358    map_field: Option<&str>,
359) -> Result<T, Error>
360where
361    T: de::DeserializeOwned,
362{
363    debug!(
364        unwrap_field,
365        map_field, "deserializing projected JSON response"
366    );
367    let mut value = serde_json::from_slice::<JsonValue>(body)?;
368    let object = value.as_object_mut().ok_or(Error::InvalidResponse(
369        "response projection expected a top-level JSON object",
370    ))?;
371    let mut projected = object.remove(unwrap_field).unwrap_or(JsonValue::Null);
372
373    if let Some(map_field) = map_field {
374        projected = match projected {
375            JsonValue::Null => JsonValue::Null,
376            JsonValue::Array(items) => JsonValue::Array(
377                items
378                    .into_iter()
379                    .map(|mut item| {
380                        let object = item.as_object_mut().ok_or(Error::InvalidResponse(
381                            "response projection expected array items to be JSON objects",
382                        ))?;
383                        Ok(object.remove(map_field).unwrap_or(JsonValue::Null))
384                    })
385                    .collect::<Result<Vec<_>, Error>>()?,
386            ),
387            _ => {
388                return Err(Error::InvalidResponse(
389                    "response projection expected the unwrapped field to be a JSON array",
390                ));
391            }
392        };
393    }
394
395    Ok(serde_json::from_value(projected)?)
396}
397
398pub fn append_path_segment(out: &mut String, value: &str) {
399    append_percent_encoded(out, value.as_bytes());
400}
401
402pub fn append_query_pair(out: &mut String, first: &mut bool, key: &str, value: &str) {
403    if *first {
404        out.push('?');
405        *first = false;
406    } else {
407        out.push('&');
408    }
409    append_percent_encoded(out, key.as_bytes());
410    out.push('=');
411    append_percent_encoded(out, value.as_bytes());
412}
413
414#[must_use]
415pub fn format_offset_datetime(value: &OffsetDateTime) -> String {
416    value.format(&Rfc3339).unwrap_or_else(|_| value.to_string())
417}
418
419#[must_use]
420pub fn format_unix_time(value: &OffsetDateTime) -> String {
421    value.unix_timestamp().to_string()
422}
423
424#[must_use]
425pub fn format_date(value: &Date) -> String {
426    format!(
427        "{:04}-{:02}-{:02}",
428        value.year(),
429        u8::from(value.month()),
430        value.day()
431    )
432}
433
434/// Parses a date in `YYYY-MM-DD` format.
435///
436/// # Errors
437///
438/// Returns an error if the value is not in `YYYY-MM-DD` format or is not a valid calendar date.
439pub fn parse_date(value: &str) -> Result<Date, ParseDateError> {
440    let value = value.trim().as_bytes();
441    if value.len() != 10 || value[4] != b'-' || value[7] != b'-' {
442        return Err(ParseDateError::InvalidFormat);
443    }
444
445    for (index, byte) in value.iter().enumerate() {
446        if matches!(index, 4 | 7) {
447            if *byte != b'-' {
448                return Err(ParseDateError::InvalidFormat);
449            }
450        } else if !byte.is_ascii_digit() {
451            return Err(ParseDateError::InvalidFormat);
452        }
453    }
454
455    let year = parse_date_year(&value[0..4])?;
456    let month = parse_date_u8(&value[5..7])?;
457    let day = parse_date_u8(&value[8..10])?;
458    let month = Month::try_from(month).map_err(|_| ParseDateError::InvalidFormat)?;
459    Date::from_calendar_date(year, month, day).map_err(|_| ParseDateError::InvalidFormat)
460}
461
462fn parse_date_year(bytes: &[u8]) -> Result<i32, ParseDateError> {
463    let mut value = 0i32;
464    for byte in bytes {
465        value = value
466            .checked_mul(10)
467            .and_then(|value| value.checked_add(i32::from(*byte - b'0')))
468            .ok_or(ParseDateError::InvalidFormat)?;
469    }
470    Ok(value)
471}
472
473fn parse_date_u8(bytes: &[u8]) -> Result<u8, ParseDateError> {
474    let mut value = 0u16;
475    for byte in bytes {
476        value = value
477            .checked_mul(10)
478            .and_then(|value| value.checked_add(u16::from(*byte - b'0')))
479            .ok_or(ParseDateError::InvalidFormat)?;
480    }
481    u8::try_from(value).map_err(|_| ParseDateError::InvalidFormat)
482}
483
484#[must_use]
485pub fn format_naive_datetime(value: &PrimitiveDateTime) -> String {
486    format!(
487        "{}T{:02}:{:02}:{:02}",
488        format_date(&value.date()),
489        value.hour(),
490        value.minute(),
491        value.second()
492    )
493}
494
495/// Parses a datetime in `YYYY-MM-DDTHH:mm:ss` format.
496///
497/// # Errors
498///
499/// Returns an error if the value is not in the expected format or has invalid date/time fields.
500pub fn parse_naive_datetime(value: &str) -> Result<PrimitiveDateTime, ParseNaiveDateTimeError> {
501    let value = value.trim();
502    let bytes = value.as_bytes();
503    if bytes.len() != 19
504        || bytes[4] != b'-'
505        || bytes[7] != b'-'
506        || bytes[10] != b'T'
507        || bytes[13] != b':'
508        || bytes[16] != b':'
509    {
510        return Err(ParseNaiveDateTimeError::InvalidFormat);
511    }
512
513    for (index, byte) in bytes.iter().enumerate() {
514        if matches!(index, 4 | 7 | 10 | 13 | 16) {
515            continue;
516        }
517        if !byte.is_ascii_digit() {
518            return Err(ParseNaiveDateTimeError::InvalidFormat);
519        }
520    }
521
522    let date = parse_date(&value[0..10]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
523    let hour = parse_date_u8(&bytes[11..13]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
524    let minute =
525        parse_date_u8(&bytes[14..16]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
526    let second =
527        parse_date_u8(&bytes[17..19]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
528    let time =
529        Time::from_hms(hour, minute, second).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
530    Ok(PrimitiveDateTime::new(date, time))
531}
532
533/// Parses a time in `HHMM` format.
534///
535/// # Errors
536///
537/// Returns an error if the value is not four ASCII digits or is outside the valid time range.
538pub fn parse_time(value: &str) -> Result<Time, ParseTimeError> {
539    let value = value.trim();
540    let bytes = value.as_bytes();
541    if bytes.len() != 4 || !bytes.iter().all(u8::is_ascii_digit) {
542        return Err(ParseTimeError::InvalidFormat);
543    }
544
545    let hour = (bytes[0] - b'0') * 10 + (bytes[1] - b'0');
546    let minute = (bytes[2] - b'0') * 10 + (bytes[3] - b'0');
547    Time::from_hms(hour, minute, 0).map_err(|_| ParseTimeError::ComponentRange)
548}
549
550#[must_use]
551pub fn format_time(value: &Time) -> String {
552    format!("{:02}{:02}", value.hour(), value.minute())
553}
554
555#[must_use]
556pub fn format_bool(value: &bool) -> &'static str {
557    if *value { "1" } else { "0" }
558}
559
560/// Parses an inclusive range string into optional minimum and maximum bounds.
561///
562/// # Errors
563///
564/// Returns an error if the range has too many separators or either bound cannot be parsed as `T`.
565pub fn parse_range<T>(value: &str) -> Result<(Option<T>, Option<T>), ParseRangeError>
566where
567    T: FromStr,
568    T::Err: fmt::Display,
569{
570    let value = value.trim();
571    if value.is_empty() {
572        return Ok((None, None));
573    }
574
575    let (min, max) = match value.split_once('-') {
576        Some((min, max)) => {
577            if max.contains('-') {
578                return Err(ParseRangeError::TooManySeparators);
579            }
580            (min, max)
581        }
582        None => (value, value),
583    };
584
585    Ok((parse_range_min(min)?, parse_range_max(max)?))
586}
587
588#[must_use]
589pub fn format_range<T>(min: &Option<T>, max: &Option<T>) -> String
590where
591    T: fmt::Display,
592{
593    match (min, max) {
594        (Some(min), Some(max)) => format!("{min}-{max}"),
595        (Some(min), None) => format!("{min}-"),
596        (None, Some(max)) => format!("-{max}"),
597        (None, None) => String::new(),
598    }
599}
600
601fn parse_range_min<T>(value: &str) -> Result<Option<T>, ParseRangeError>
602where
603    T: FromStr,
604    T::Err: fmt::Display,
605{
606    parse_range_bound(value, |value, message| ParseRangeError::InvalidMinimum {
607        value,
608        message,
609    })
610}
611
612fn parse_range_max<T>(value: &str) -> Result<Option<T>, ParseRangeError>
613where
614    T: FromStr,
615    T::Err: fmt::Display,
616{
617    parse_range_bound(value, |value, message| ParseRangeError::InvalidMaximum {
618        value,
619        message,
620    })
621}
622
623fn parse_range_bound<T>(
624    value: &str,
625    invalid: impl FnOnce(String, String) -> ParseRangeError,
626) -> Result<Option<T>, ParseRangeError>
627where
628    T: FromStr,
629    T::Err: fmt::Display,
630{
631    let value = value.trim();
632    if value.is_empty() {
633        return Ok(None);
634    }
635
636    value
637        .parse::<T>()
638        .map(Some)
639        .map_err(|err| invalid(value.to_owned(), err.to_string()))
640}
641
642#[cfg(feature = "serde")]
643#[allow(clippy::missing_errors_doc)]
644pub mod serde_string {
645    use std::fmt;
646    use std::str::FromStr;
647
648    use serde::Deserialize;
649    use serde::de::Error as DeError;
650    macro_rules! string_from_str_module {
651        ($module:ident, $ty:ty) => {
652            pub mod $module {
653                pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
654                where
655                    S: serde::Serializer,
656                {
657                    super::serialize_display(value, serializer)
658                }
659
660                pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
661                where
662                    D: serde::Deserializer<'de>,
663                {
664                    super::deserialize_from_str(deserializer)
665                }
666
667                pub fn serialize_none_if<S>(
668                    value: &Option<$ty>,
669                    none_value: &str,
670                    serializer: S,
671                ) -> Result<S::Ok, S::Error>
672                where
673                    S: serde::Serializer,
674                {
675                    super::serialize_option_or_sentinel(
676                        value.as_ref(),
677                        none_value,
678                        serializer,
679                        serialize,
680                    )
681                }
682
683                pub fn deserialize_none_if<'de, D>(
684                    deserializer: D,
685                    none_if: &[&str],
686                ) -> Result<Option<$ty>, D::Error>
687                where
688                    D: serde::Deserializer<'de>,
689                {
690                    super::deserialize_none_if_with(deserializer, none_if, str::parse::<$ty>)
691                }
692
693                pub mod option {
694                    pub fn serialize<S>(
695                        value: &Option<$ty>,
696                        serializer: S,
697                    ) -> Result<S::Ok, S::Error>
698                    where
699                        S: serde::Serializer,
700                    {
701                        super::super::serialize_option_display(value.as_ref(), serializer)
702                    }
703
704                    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
705                    where
706                        D: serde::Deserializer<'de>,
707                    {
708                        super::super::deserialize_option_from_str(deserializer)
709                    }
710
711                    pub fn deserialize_none_if<'de, D>(
712                        deserializer: D,
713                        none_if: &[&str],
714                    ) -> Result<Option<$ty>, D::Error>
715                    where
716                        D: serde::Deserializer<'de>,
717                    {
718                        super::super::deserialize_option_none_if_with(
719                            deserializer,
720                            none_if,
721                            str::parse::<$ty>,
722                        )
723                    }
724                }
725            }
726        };
727    }
728
729    macro_rules! string_float_module {
730        ($module:ident, $ty:ty) => {
731            pub mod $module {
732                use serde::Deserialize;
733                use serde::de::Error as DeError;
734
735                pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
736                where
737                    S: serde::Serializer,
738                {
739                    super::serialize_display(value, serializer)
740                }
741
742                pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
743                where
744                    D: serde::Deserializer<'de>,
745                {
746                    let value = <String as Deserialize>::deserialize(deserializer)?;
747                    fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
748                }
749
750                pub fn serialize_none_if<S>(
751                    value: &Option<$ty>,
752                    none_value: &str,
753                    serializer: S,
754                ) -> Result<S::Ok, S::Error>
755                where
756                    S: serde::Serializer,
757                {
758                    super::serialize_option_or_sentinel(
759                        value.as_ref(),
760                        none_value,
761                        serializer,
762                        serialize,
763                    )
764                }
765
766                pub fn deserialize_none_if<'de, D>(
767                    deserializer: D,
768                    none_if: &[&str],
769                ) -> Result<Option<$ty>, D::Error>
770                where
771                    D: serde::Deserializer<'de>,
772                {
773                    super::deserialize_none_if_with(deserializer, none_if, |value| {
774                        fast_float2::parse::<$ty, _>(value)
775                    })
776                }
777
778                pub mod option {
779                    use serde::Deserialize;
780                    use serde::de::Error as DeError;
781
782                    pub fn serialize<S>(
783                        value: &Option<$ty>,
784                        serializer: S,
785                    ) -> Result<S::Ok, S::Error>
786                    where
787                        S: serde::Serializer,
788                    {
789                        super::super::serialize_option_display(value.as_ref(), serializer)
790                    }
791
792                    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
793                    where
794                        D: serde::Deserializer<'de>,
795                    {
796                        let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
797                        value
798                            .map(|value| {
799                                fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
800                            })
801                            .transpose()
802                    }
803
804                    pub fn deserialize_none_if<'de, D>(
805                        deserializer: D,
806                        none_if: &[&str],
807                    ) -> Result<Option<$ty>, D::Error>
808                    where
809                        D: serde::Deserializer<'de>,
810                    {
811                        super::super::deserialize_option_none_if_with(
812                            deserializer,
813                            none_if,
814                            |value| fast_float2::parse::<$ty, _>(value),
815                        )
816                    }
817                }
818            }
819        };
820    }
821
822    string_from_str_module!(as_url, crate::Url);
823    string_from_str_module!(as_u8, u8);
824    string_from_str_module!(as_u16, u16);
825    string_from_str_module!(as_u32, u32);
826    string_from_str_module!(as_u64, u64);
827    string_from_str_module!(as_i8, i8);
828    string_from_str_module!(as_i16, i16);
829    string_from_str_module!(as_i32, i32);
830    string_from_str_module!(as_i64, i64);
831    string_float_module!(as_f32, f32);
832    string_float_module!(as_f64, f64);
833
834    pub mod as_bool {
835        use std::fmt;
836
837        use serde::de::{Error as DeError, Visitor};
838
839        pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
840        where
841            S: serde::Serializer,
842        {
843            serializer.serialize_str(crate::format_bool(value))
844        }
845
846        pub fn serialize_mapped<S>(
847            value: &bool,
848            true_value: &str,
849            false_value: &str,
850            serializer: S,
851        ) -> Result<S::Ok, S::Error>
852        where
853            S: serde::Serializer,
854        {
855            serializer.serialize_str(if *value { true_value } else { false_value })
856        }
857
858        pub fn serialize_mapped_none_if<S>(
859            value: &Option<bool>,
860            true_value: &str,
861            false_value: &str,
862            none_value: &str,
863            serializer: S,
864        ) -> Result<S::Ok, S::Error>
865        where
866            S: serde::Serializer,
867        {
868            super::serialize_option_or_sentinel(
869                value.as_ref(),
870                none_value,
871                serializer,
872                |value, serializer| serialize_mapped(value, true_value, false_value, serializer),
873            )
874        }
875
876        pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
877        where
878            D: serde::Deserializer<'de>,
879        {
880            deserializer.deserialize_any(BoolVisitor)
881        }
882
883        pub fn deserialize_mapped<'de, D>(
884            deserializer: D,
885            true_values: &[&str],
886            false_values: &[&str],
887            unknown_as: Option<bool>,
888        ) -> Result<bool, D::Error>
889        where
890            D: serde::Deserializer<'de>,
891        {
892            deserializer.deserialize_any(MappedBoolVisitor {
893                true_values,
894                false_values,
895                unknown_as,
896            })
897        }
898
899        pub fn serialize_none_if<S>(
900            value: &Option<bool>,
901            none_value: &str,
902            serializer: S,
903        ) -> Result<S::Ok, S::Error>
904        where
905            S: serde::Serializer,
906        {
907            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
908        }
909
910        pub fn deserialize_none_if<'de, D>(
911            deserializer: D,
912            none_if: &[&str],
913        ) -> Result<Option<bool>, D::Error>
914        where
915            D: serde::Deserializer<'de>,
916        {
917            deserializer.deserialize_any(BoolNoneIfVisitor { none_if })
918        }
919
920        pub fn deserialize_mapped_none_if<'de, D>(
921            deserializer: D,
922            true_values: &[&str],
923            false_values: &[&str],
924            unknown_as: Option<bool>,
925            none_if: &[&str],
926        ) -> Result<Option<bool>, D::Error>
927        where
928            D: serde::Deserializer<'de>,
929        {
930            deserializer.deserialize_any(MappedBoolNoneIfVisitor {
931                true_values,
932                false_values,
933                unknown_as,
934                none_if,
935            })
936        }
937
938        struct BoolVisitor;
939
940        impl Visitor<'_> for BoolVisitor {
941            type Value = bool;
942
943            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
944                formatter.write_str("a boolean string or numeric boolean")
945            }
946
947            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
948                Ok(value)
949            }
950
951            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
952            where
953                E: DeError,
954            {
955                super::deserialize_bool(value).map_err(DeError::custom)
956            }
957
958            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
959            where
960                E: DeError,
961            {
962                self.visit_str(&value)
963            }
964
965            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
966            where
967                E: DeError,
968            {
969                match value {
970                    0 => Ok(false),
971                    1 => Ok(true),
972                    _ => Err(DeError::custom("invalid boolean number")),
973                }
974            }
975
976            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
977            where
978                E: DeError,
979            {
980                match value {
981                    0 => Ok(false),
982                    1 => Ok(true),
983                    _ => Err(DeError::custom("invalid boolean number")),
984                }
985            }
986        }
987
988        struct MappedBoolVisitor<'a> {
989            true_values: &'a [&'a str],
990            false_values: &'a [&'a str],
991            unknown_as: Option<bool>,
992        }
993
994        impl Visitor<'_> for MappedBoolVisitor<'_> {
995            type Value = bool;
996
997            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
998                formatter.write_str("a configured boolean string or numeric boolean")
999            }
1000
1001            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
1002                Ok(value)
1003            }
1004
1005            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1006            where
1007                E: DeError,
1008            {
1009                mapped_bool(value, self.true_values, self.false_values, self.unknown_as)
1010                    .ok_or_else(|| DeError::custom("invalid mapped boolean string"))
1011            }
1012
1013            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1014            where
1015                E: DeError,
1016            {
1017                self.visit_str(&value)
1018            }
1019
1020            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1021            where
1022                E: DeError,
1023            {
1024                match value {
1025                    0 => Ok(false),
1026                    1 => Ok(true),
1027                    _ => Err(DeError::custom("invalid boolean number")),
1028                }
1029            }
1030
1031            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
1032            where
1033                E: DeError,
1034            {
1035                match value {
1036                    0 => Ok(false),
1037                    1 => Ok(true),
1038                    _ => Err(DeError::custom("invalid boolean number")),
1039                }
1040            }
1041        }
1042
1043        struct BoolNoneIfVisitor<'a> {
1044            none_if: &'a [&'a str],
1045        }
1046
1047        impl Visitor<'_> for BoolNoneIfVisitor<'_> {
1048            type Value = Option<bool>;
1049
1050            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1051                formatter.write_str("a boolean string, numeric boolean, or configured sentinel")
1052            }
1053
1054            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
1055                Ok(Some(value))
1056            }
1057
1058            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1059            where
1060                E: DeError,
1061            {
1062                if self.none_if.contains(&value) {
1063                    Ok(None)
1064                } else {
1065                    super::deserialize_bool(value)
1066                        .map(Some)
1067                        .map_err(DeError::custom)
1068                }
1069            }
1070
1071            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1072            where
1073                E: DeError,
1074            {
1075                self.visit_str(&value)
1076            }
1077
1078            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1079            where
1080                E: DeError,
1081            {
1082                match value {
1083                    0 => Ok(Some(false)),
1084                    1 => Ok(Some(true)),
1085                    _ => Err(DeError::custom("invalid boolean number")),
1086                }
1087            }
1088
1089            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
1090            where
1091                E: DeError,
1092            {
1093                match value {
1094                    0 => Ok(Some(false)),
1095                    1 => Ok(Some(true)),
1096                    _ => Err(DeError::custom("invalid boolean number")),
1097                }
1098            }
1099        }
1100
1101        struct MappedBoolNoneIfVisitor<'a> {
1102            true_values: &'a [&'a str],
1103            false_values: &'a [&'a str],
1104            unknown_as: Option<bool>,
1105            none_if: &'a [&'a str],
1106        }
1107
1108        impl Visitor<'_> for MappedBoolNoneIfVisitor<'_> {
1109            type Value = Option<bool>;
1110
1111            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1112                formatter.write_str(
1113                    "a configured boolean string, numeric boolean, or configured sentinel",
1114                )
1115            }
1116
1117            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
1118                Ok(Some(value))
1119            }
1120
1121            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1122            where
1123                E: DeError,
1124            {
1125                if self.none_if.contains(&value) {
1126                    Ok(None)
1127                } else {
1128                    mapped_bool(value, self.true_values, self.false_values, self.unknown_as)
1129                        .map(Some)
1130                        .ok_or_else(|| DeError::custom("invalid mapped boolean string"))
1131                }
1132            }
1133
1134            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1135            where
1136                E: DeError,
1137            {
1138                self.visit_str(&value)
1139            }
1140
1141            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1142            where
1143                E: DeError,
1144            {
1145                match value {
1146                    0 => Ok(Some(false)),
1147                    1 => Ok(Some(true)),
1148                    _ => Err(DeError::custom("invalid boolean number")),
1149                }
1150            }
1151
1152            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
1153            where
1154                E: DeError,
1155            {
1156                match value {
1157                    0 => Ok(Some(false)),
1158                    1 => Ok(Some(true)),
1159                    _ => Err(DeError::custom("invalid boolean number")),
1160                }
1161            }
1162        }
1163
1164        pub mod option {
1165            use std::fmt;
1166
1167            use serde::de::Visitor;
1168
1169            pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
1170            where
1171                S: serde::Serializer,
1172            {
1173                match value {
1174                    Some(value) => super::serialize(value, serializer),
1175                    None => serializer.serialize_none(),
1176                }
1177            }
1178
1179            pub fn serialize_mapped<S>(
1180                value: &Option<bool>,
1181                true_value: &str,
1182                false_value: &str,
1183                serializer: S,
1184            ) -> Result<S::Ok, S::Error>
1185            where
1186                S: serde::Serializer,
1187            {
1188                match value {
1189                    Some(value) => {
1190                        super::serialize_mapped(value, true_value, false_value, serializer)
1191                    }
1192                    None => serializer.serialize_none(),
1193                }
1194            }
1195
1196            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
1197            where
1198                D: serde::Deserializer<'de>,
1199            {
1200                deserializer.deserialize_option(BoolOptionVisitor)
1201            }
1202
1203            pub fn deserialize_mapped<'de, D>(
1204                deserializer: D,
1205                true_values: &[&str],
1206                false_values: &[&str],
1207                unknown_as: Option<bool>,
1208            ) -> Result<Option<bool>, D::Error>
1209            where
1210                D: serde::Deserializer<'de>,
1211            {
1212                deserializer.deserialize_option(MappedBoolOptionVisitor {
1213                    true_values,
1214                    false_values,
1215                    unknown_as,
1216                    none_if: None,
1217                })
1218            }
1219
1220            pub fn deserialize_none_if<'de, D>(
1221                deserializer: D,
1222                none_if: &[&str],
1223            ) -> Result<Option<bool>, D::Error>
1224            where
1225                D: serde::Deserializer<'de>,
1226            {
1227                deserializer.deserialize_option(BoolOptionNoneIfVisitor { none_if })
1228            }
1229
1230            pub fn deserialize_mapped_none_if<'de, D>(
1231                deserializer: D,
1232                true_values: &[&str],
1233                false_values: &[&str],
1234                unknown_as: Option<bool>,
1235                none_if: &[&str],
1236            ) -> Result<Option<bool>, D::Error>
1237            where
1238                D: serde::Deserializer<'de>,
1239            {
1240                deserializer.deserialize_option(MappedBoolOptionVisitor {
1241                    true_values,
1242                    false_values,
1243                    unknown_as,
1244                    none_if: Some(none_if),
1245                })
1246            }
1247
1248            struct BoolOptionVisitor;
1249
1250            impl<'de> Visitor<'de> for BoolOptionVisitor {
1251                type Value = Option<bool>;
1252
1253                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1254                    formatter.write_str("an optional boolean string or numeric boolean")
1255                }
1256
1257                fn visit_none<E>(self) -> Result<Self::Value, E> {
1258                    Ok(None)
1259                }
1260
1261                fn visit_unit<E>(self) -> Result<Self::Value, E> {
1262                    Ok(None)
1263                }
1264
1265                fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1266                where
1267                    D: serde::Deserializer<'de>,
1268                {
1269                    super::deserialize(deserializer).map(Some)
1270                }
1271            }
1272
1273            struct BoolOptionNoneIfVisitor<'a> {
1274                none_if: &'a [&'a str],
1275            }
1276
1277            impl<'de> Visitor<'de> for BoolOptionNoneIfVisitor<'_> {
1278                type Value = Option<bool>;
1279
1280                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1281                    formatter.write_str("an optional boolean string, numeric boolean, or sentinel")
1282                }
1283
1284                fn visit_none<E>(self) -> Result<Self::Value, E> {
1285                    Ok(None)
1286                }
1287
1288                fn visit_unit<E>(self) -> Result<Self::Value, E> {
1289                    Ok(None)
1290                }
1291
1292                fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1293                where
1294                    D: serde::Deserializer<'de>,
1295                {
1296                    deserializer.deserialize_any(super::BoolNoneIfVisitor {
1297                        none_if: self.none_if,
1298                    })
1299                }
1300            }
1301
1302            struct MappedBoolOptionVisitor<'a> {
1303                true_values: &'a [&'a str],
1304                false_values: &'a [&'a str],
1305                unknown_as: Option<bool>,
1306                none_if: Option<&'a [&'a str]>,
1307            }
1308
1309            impl<'de> Visitor<'de> for MappedBoolOptionVisitor<'_> {
1310                type Value = Option<bool>;
1311
1312                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1313                    formatter.write_str("an optional configured boolean string or numeric boolean")
1314                }
1315
1316                fn visit_none<E>(self) -> Result<Self::Value, E> {
1317                    Ok(None)
1318                }
1319
1320                fn visit_unit<E>(self) -> Result<Self::Value, E> {
1321                    Ok(None)
1322                }
1323
1324                fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1325                where
1326                    D: serde::Deserializer<'de>,
1327                {
1328                    match self.none_if {
1329                        Some(none_if) => {
1330                            deserializer.deserialize_any(super::MappedBoolNoneIfVisitor {
1331                                true_values: self.true_values,
1332                                false_values: self.false_values,
1333                                unknown_as: self.unknown_as,
1334                                none_if,
1335                            })
1336                        }
1337                        None => super::deserialize_mapped(
1338                            deserializer,
1339                            self.true_values,
1340                            self.false_values,
1341                            self.unknown_as,
1342                        )
1343                        .map(Some),
1344                    }
1345                }
1346            }
1347        }
1348
1349        fn mapped_bool(
1350            value: &str,
1351            true_values: &[&str],
1352            false_values: &[&str],
1353            unknown_as: Option<bool>,
1354        ) -> Option<bool> {
1355            if true_values.contains(&value) {
1356                Some(true)
1357            } else if false_values.contains(&value) {
1358                Some(false)
1359            } else {
1360                unknown_as
1361            }
1362        }
1363    }
1364
1365    pub mod as_date {
1366        use serde::Deserialize;
1367        use serde::de::Error as DeError;
1368
1369        use crate::Date;
1370
1371        pub fn serialize<S>(value: &Date, serializer: S) -> Result<S::Ok, S::Error>
1372        where
1373            S: serde::Serializer,
1374        {
1375            serializer.serialize_str(&crate::format_date(value))
1376        }
1377
1378        pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
1379        where
1380            D: serde::Deserializer<'de>,
1381        {
1382            let value = <String as Deserialize>::deserialize(deserializer)?;
1383            crate::parse_date(&value).map_err(DeError::custom)
1384        }
1385
1386        pub fn serialize_none_if<S>(
1387            value: &Option<Date>,
1388            none_value: &str,
1389            serializer: S,
1390        ) -> Result<S::Ok, S::Error>
1391        where
1392            S: serde::Serializer,
1393        {
1394            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1395        }
1396
1397        pub fn deserialize_none_if<'de, D>(
1398            deserializer: D,
1399            none_if: &[&str],
1400        ) -> Result<Option<Date>, D::Error>
1401        where
1402            D: serde::Deserializer<'de>,
1403        {
1404            super::deserialize_none_if_with(deserializer, none_if, crate::parse_date)
1405        }
1406
1407        pub mod option {
1408            use serde::Deserialize;
1409            use serde::de::Error as DeError;
1410
1411            use super::super as serde_string;
1412            use crate::Date;
1413
1414            pub fn serialize<S>(value: &Option<Date>, serializer: S) -> Result<S::Ok, S::Error>
1415            where
1416                S: serde::Serializer,
1417            {
1418                match value {
1419                    Some(value) => super::serialize(value, serializer),
1420                    None => serializer.serialize_none(),
1421                }
1422            }
1423
1424            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Date>, D::Error>
1425            where
1426                D: serde::Deserializer<'de>,
1427            {
1428                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1429                value
1430                    .map(|value| crate::parse_date(&value).map_err(DeError::custom))
1431                    .transpose()
1432            }
1433
1434            pub fn deserialize_none_if<'de, D>(
1435                deserializer: D,
1436                none_if: &[&str],
1437            ) -> Result<Option<Date>, D::Error>
1438            where
1439                D: serde::Deserializer<'de>,
1440            {
1441                serde_string::deserialize_option_none_if_with(
1442                    deserializer,
1443                    none_if,
1444                    crate::parse_date,
1445                )
1446            }
1447        }
1448    }
1449
1450    pub mod as_naive_datetime {
1451        use serde::Deserialize;
1452        use serde::de::Error as DeError;
1453
1454        use crate::PrimitiveDateTime;
1455
1456        pub fn serialize<S>(value: &PrimitiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
1457        where
1458            S: serde::Serializer,
1459        {
1460            serializer.serialize_str(&crate::format_naive_datetime(value))
1461        }
1462
1463        pub fn deserialize<'de, D>(deserializer: D) -> Result<PrimitiveDateTime, D::Error>
1464        where
1465            D: serde::Deserializer<'de>,
1466        {
1467            let value = <String as Deserialize>::deserialize(deserializer)?;
1468            crate::parse_naive_datetime(&value).map_err(DeError::custom)
1469        }
1470
1471        pub fn serialize_none_if<S>(
1472            value: &Option<PrimitiveDateTime>,
1473            none_value: &str,
1474            serializer: S,
1475        ) -> Result<S::Ok, S::Error>
1476        where
1477            S: serde::Serializer,
1478        {
1479            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1480        }
1481
1482        pub fn deserialize_none_if<'de, D>(
1483            deserializer: D,
1484            none_if: &[&str],
1485        ) -> Result<Option<PrimitiveDateTime>, D::Error>
1486        where
1487            D: serde::Deserializer<'de>,
1488        {
1489            super::deserialize_none_if_with(deserializer, none_if, crate::parse_naive_datetime)
1490        }
1491
1492        pub mod option {
1493            use serde::Deserialize;
1494            use serde::de::Error as DeError;
1495
1496            use super::super as serde_string;
1497            use crate::PrimitiveDateTime;
1498
1499            pub fn serialize<S>(
1500                value: &Option<PrimitiveDateTime>,
1501                serializer: S,
1502            ) -> Result<S::Ok, S::Error>
1503            where
1504                S: serde::Serializer,
1505            {
1506                match value {
1507                    Some(value) => super::serialize(value, serializer),
1508                    None => serializer.serialize_none(),
1509                }
1510            }
1511
1512            pub fn deserialize<'de, D>(
1513                deserializer: D,
1514            ) -> Result<Option<PrimitiveDateTime>, D::Error>
1515            where
1516                D: serde::Deserializer<'de>,
1517            {
1518                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1519                value
1520                    .map(|value| crate::parse_naive_datetime(&value).map_err(DeError::custom))
1521                    .transpose()
1522            }
1523
1524            pub fn deserialize_none_if<'de, D>(
1525                deserializer: D,
1526                none_if: &[&str],
1527            ) -> Result<Option<PrimitiveDateTime>, D::Error>
1528            where
1529                D: serde::Deserializer<'de>,
1530            {
1531                serde_string::deserialize_option_none_if_with(
1532                    deserializer,
1533                    none_if,
1534                    crate::parse_naive_datetime,
1535                )
1536            }
1537        }
1538    }
1539
1540    pub mod as_offset_datetime {
1541        use serde::Deserialize;
1542        use serde::de::Error as DeError;
1543        use serde::ser::Error as SerError;
1544
1545        use crate::OffsetDateTime;
1546        use time::format_description::well_known::Rfc3339;
1547
1548        pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1549        where
1550            S: serde::Serializer,
1551        {
1552            let value = value.format(&Rfc3339).map_err(SerError::custom)?;
1553            serializer.serialize_str(&value)
1554        }
1555
1556        pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1557        where
1558            D: serde::Deserializer<'de>,
1559        {
1560            let value = <String as Deserialize>::deserialize(deserializer)?;
1561            OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom)
1562        }
1563
1564        pub fn serialize_none_if<S>(
1565            value: &Option<OffsetDateTime>,
1566            none_value: &str,
1567            serializer: S,
1568        ) -> Result<S::Ok, S::Error>
1569        where
1570            S: serde::Serializer,
1571        {
1572            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1573        }
1574
1575        pub fn deserialize_none_if<'de, D>(
1576            deserializer: D,
1577            none_if: &[&str],
1578        ) -> Result<Option<OffsetDateTime>, D::Error>
1579        where
1580            D: serde::Deserializer<'de>,
1581        {
1582            super::deserialize_none_if_with(deserializer, none_if, |value| {
1583                OffsetDateTime::parse(value, &Rfc3339)
1584            })
1585        }
1586
1587        pub mod option {
1588            use serde::Deserialize;
1589            use serde::de::Error as DeError;
1590
1591            use super::super as serde_string;
1592            use crate::OffsetDateTime;
1593            use time::format_description::well_known::Rfc3339;
1594
1595            pub fn serialize<S>(
1596                value: &Option<OffsetDateTime>,
1597                serializer: S,
1598            ) -> Result<S::Ok, S::Error>
1599            where
1600                S: serde::Serializer,
1601            {
1602                match value {
1603                    Some(value) => super::serialize(value, serializer),
1604                    None => serializer.serialize_none(),
1605                }
1606            }
1607
1608            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1609            where
1610                D: serde::Deserializer<'de>,
1611            {
1612                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1613                value
1614                    .map(|value| OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom))
1615                    .transpose()
1616            }
1617
1618            pub fn deserialize_none_if<'de, D>(
1619                deserializer: D,
1620                none_if: &[&str],
1621            ) -> Result<Option<OffsetDateTime>, D::Error>
1622            where
1623                D: serde::Deserializer<'de>,
1624            {
1625                serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1626                    OffsetDateTime::parse(value, &Rfc3339)
1627                })
1628            }
1629        }
1630    }
1631
1632    pub mod as_unix_time {
1633        use serde::Deserialize;
1634        use serde::de::Error as DeError;
1635
1636        use crate::OffsetDateTime;
1637
1638        pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1639        where
1640            S: serde::Serializer,
1641        {
1642            serializer.serialize_str(&crate::format_unix_time(value))
1643        }
1644
1645        pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1646        where
1647            D: serde::Deserializer<'de>,
1648        {
1649            let value = <String as Deserialize>::deserialize(deserializer)?;
1650            let value = value.parse::<i64>().map_err(DeError::custom)?;
1651            OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1652        }
1653
1654        pub fn serialize_none_if<S>(
1655            value: &Option<OffsetDateTime>,
1656            none_value: &str,
1657            serializer: S,
1658        ) -> Result<S::Ok, S::Error>
1659        where
1660            S: serde::Serializer,
1661        {
1662            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1663        }
1664
1665        pub fn deserialize_none_if<'de, D>(
1666            deserializer: D,
1667            none_if: &[&str],
1668        ) -> Result<Option<OffsetDateTime>, D::Error>
1669        where
1670            D: serde::Deserializer<'de>,
1671        {
1672            super::deserialize_none_if_with(deserializer, none_if, |value| {
1673                let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1674                OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1675            })
1676        }
1677
1678        pub mod option {
1679            use serde::Deserialize;
1680            use serde::de::Error as DeError;
1681
1682            use super::super as serde_string;
1683            use crate::OffsetDateTime;
1684
1685            pub fn serialize<S>(
1686                value: &Option<OffsetDateTime>,
1687                serializer: S,
1688            ) -> Result<S::Ok, S::Error>
1689            where
1690                S: serde::Serializer,
1691            {
1692                match value {
1693                    Some(value) => super::serialize(value, serializer),
1694                    None => serializer.serialize_none(),
1695                }
1696            }
1697
1698            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1699            where
1700                D: serde::Deserializer<'de>,
1701            {
1702                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1703                value
1704                    .map(|value| {
1705                        let value = value.parse::<i64>().map_err(DeError::custom)?;
1706                        OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1707                    })
1708                    .transpose()
1709            }
1710
1711            pub fn deserialize_none_if<'de, D>(
1712                deserializer: D,
1713                none_if: &[&str],
1714            ) -> Result<Option<OffsetDateTime>, D::Error>
1715            where
1716                D: serde::Deserializer<'de>,
1717            {
1718                serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1719                    let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1720                    OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1721                })
1722            }
1723        }
1724    }
1725
1726    pub mod as_time {
1727        use serde::Deserialize;
1728        use serde::de::Error as DeError;
1729
1730        use crate::Time;
1731
1732        pub fn serialize<S>(value: &Time, serializer: S) -> Result<S::Ok, S::Error>
1733        where
1734            S: serde::Serializer,
1735        {
1736            serializer.serialize_str(&crate::format_time(value))
1737        }
1738
1739        pub fn deserialize<'de, D>(deserializer: D) -> Result<Time, D::Error>
1740        where
1741            D: serde::Deserializer<'de>,
1742        {
1743            let value = <String as Deserialize>::deserialize(deserializer)?;
1744            crate::parse_time(&value).map_err(DeError::custom)
1745        }
1746
1747        pub fn serialize_none_if<S>(
1748            value: &Option<Time>,
1749            none_value: &str,
1750            serializer: S,
1751        ) -> Result<S::Ok, S::Error>
1752        where
1753            S: serde::Serializer,
1754        {
1755            super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1756        }
1757
1758        pub fn deserialize_none_if<'de, D>(
1759            deserializer: D,
1760            none_if: &[&str],
1761        ) -> Result<Option<Time>, D::Error>
1762        where
1763            D: serde::Deserializer<'de>,
1764        {
1765            super::deserialize_none_if_with(deserializer, none_if, crate::parse_time)
1766        }
1767
1768        pub mod option {
1769            use serde::Deserialize;
1770            use serde::de::Error as DeError;
1771
1772            use crate::Time;
1773
1774            pub fn serialize<S>(value: &Option<Time>, serializer: S) -> Result<S::Ok, S::Error>
1775            where
1776                S: serde::Serializer,
1777            {
1778                match value {
1779                    Some(value) => super::serialize(value, serializer),
1780                    None => serializer.serialize_none(),
1781                }
1782            }
1783
1784            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Time>, D::Error>
1785            where
1786                D: serde::Deserializer<'de>,
1787            {
1788                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1789                let Some(value) = value else {
1790                    return Ok(None);
1791                };
1792                let value = value.trim();
1793                if value.is_empty() {
1794                    return Ok(None);
1795                }
1796                crate::parse_time(value).map(Some).map_err(DeError::custom)
1797            }
1798
1799            pub fn deserialize_none_if<'de, D>(
1800                deserializer: D,
1801                none_if: &[&str],
1802            ) -> Result<Option<Time>, D::Error>
1803            where
1804                D: serde::Deserializer<'de>,
1805            {
1806                let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1807                let Some(value) = value else {
1808                    return Ok(None);
1809                };
1810                if none_if.contains(&value.as_str()) {
1811                    return Ok(None);
1812                }
1813                let value = value.trim();
1814                if value.is_empty() {
1815                    return Ok(None);
1816                }
1817                crate::parse_time(value).map(Some).map_err(DeError::custom)
1818            }
1819        }
1820    }
1821
1822    /// Field-local string codecs for a pair of values.
1823    ///
1824    /// The generated model supplies its constructor so the target type keeps its
1825    /// ordinary object representation and its existing numeric validations.
1826    pub mod pair {
1827        use std::fmt;
1828
1829        use serde::{
1830            de::{Error as DeError, Visitor},
1831            ser::Error,
1832        };
1833
1834        pub fn serialize<A, B, S>(
1835            first: &A,
1836            second: &B,
1837            delimiter: &str,
1838            serializer: S,
1839        ) -> Result<S::Ok, S::Error>
1840        where
1841            A: fmt::Display,
1842            B: fmt::Display,
1843            S: serde::Serializer,
1844        {
1845            if delimiter.is_empty() {
1846                return Err(Error::custom("pair delimiter must not be empty"));
1847            }
1848            serializer.collect_str(&format_args!("{first}{delimiter}{second}"))
1849        }
1850
1851        pub fn deserialize<'de, T, D>(
1852            deserializer: D,
1853            delimiter: &str,
1854            parse: impl FnOnce(&str, &str) -> Result<T, D::Error>,
1855        ) -> Result<T, D::Error>
1856        where
1857            D: serde::Deserializer<'de>,
1858        {
1859            deserializer.deserialize_str(StringVisitor(|value: &str| {
1860                parse_with(value, delimiter, parse)
1861            }))
1862        }
1863
1864        pub fn deserialize_none_if<'de, T, D>(
1865            deserializer: D,
1866            delimiter: &str,
1867            none_if: &[&str],
1868            parse: impl FnOnce(&str, &str) -> Result<T, D::Error>,
1869        ) -> Result<Option<T>, D::Error>
1870        where
1871            D: serde::Deserializer<'de>,
1872        {
1873            deserializer.deserialize_str(StringVisitor(|value: &str| -> Result<_, D::Error> {
1874                super::parse_none_if_with(value, none_if, |value| {
1875                    parse_with(value, delimiter, parse)
1876                })
1877            }))
1878        }
1879
1880        pub mod option {
1881            use crate::serde_string::parse_none_if_with;
1882
1883            use super::{OptionVisitor, parse_with};
1884
1885            pub fn deserialize<'de, T, D>(
1886                deserializer: D,
1887                delimiter: &str,
1888                parse: impl FnOnce(&str, &str) -> Result<T, D::Error>,
1889            ) -> Result<Option<T>, D::Error>
1890            where
1891                D: serde::Deserializer<'de>,
1892            {
1893                deserialize_none_if(deserializer, delimiter, &[], parse)
1894            }
1895
1896            pub fn deserialize_none_if<'de, T, D>(
1897                deserializer: D,
1898                delimiter: &str,
1899                none_if: &[&str],
1900                parse: impl FnOnce(&str, &str) -> Result<T, D::Error>,
1901            ) -> Result<Option<T>, D::Error>
1902            where
1903                D: serde::Deserializer<'de>,
1904            {
1905                deserializer.deserialize_option(OptionVisitor(
1906                    |value: &str| -> Result<_, D::Error> {
1907                        parse_none_if_with(value, none_if, |value| {
1908                            parse_with(value, delimiter, parse)
1909                        })
1910                    },
1911                ))
1912            }
1913
1914            /// Consumes the complete JSON value before discarding invalid input.
1915            #[cfg(feature = "json")]
1916            pub fn deserialize_lossy<'de, T, D>(
1917                deserializer: D,
1918                delimiter: &str,
1919                parse: impl FnOnce(&str, &str) -> Result<T, D::Error>,
1920            ) -> Result<Option<T>, D::Error>
1921            where
1922                D: serde::Deserializer<'de>,
1923            {
1924                use serde::Deserialize;
1925                use serde_json::Value;
1926
1927                let value = serde_json::Value::deserialize(deserializer)?;
1928                match value {
1929                    Value::String(value) => Ok(parse_with(&value, delimiter, parse).ok()),
1930                    _ => Ok(None),
1931                }
1932            }
1933        }
1934
1935        fn parse_with<T, E>(
1936            value: &str,
1937            delimiter: &str,
1938            parse: impl FnOnce(&str, &str) -> Result<T, E>,
1939        ) -> Result<T, E>
1940        where
1941            E: DeError,
1942        {
1943            if delimiter.is_empty() {
1944                return Err(E::custom("pair delimiter must not be empty"));
1945            }
1946            let mut components = value.trim().split(delimiter);
1947            let first = components.next().unwrap_or_default().trim();
1948            let second = components.next().unwrap_or_default().trim();
1949            if first.is_empty() || second.is_empty() || components.next().is_some() {
1950                return Err(E::custom("expected exactly two nonempty pair components"));
1951            }
1952            parse(first, second)
1953        }
1954
1955        struct StringVisitor<F>(F);
1956
1957        impl<T, E, F> Visitor<'_> for StringVisitor<F>
1958        where
1959            E: fmt::Display,
1960            F: FnOnce(&str) -> Result<T, E>,
1961        {
1962            type Value = T;
1963
1964            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1965                formatter.write_str("a string containing two delimited components")
1966            }
1967
1968            fn visit_str<Error>(self, value: &str) -> Result<T, Error>
1969            where
1970                Error: DeError,
1971            {
1972                (self.0)(value).map_err(Error::custom)
1973            }
1974        }
1975
1976        struct OptionVisitor<F>(F);
1977
1978        impl<'de, T, E, F> Visitor<'de> for OptionVisitor<F>
1979        where
1980            E: fmt::Display,
1981            F: FnOnce(&str) -> Result<Option<T>, E>,
1982        {
1983            type Value = Option<T>;
1984
1985            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1986                formatter.write_str("null or a string containing two delimited components")
1987            }
1988
1989            fn visit_none<Error>(self) -> Result<Self::Value, Error>
1990            where
1991                Error: DeError,
1992            {
1993                Ok(None)
1994            }
1995
1996            fn visit_unit<Error>(self) -> Result<Self::Value, Error>
1997            where
1998                Error: DeError,
1999            {
2000                Ok(None)
2001            }
2002
2003            fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2004            where
2005                D: serde::Deserializer<'de>,
2006            {
2007                deserializer.deserialize_str(StringVisitor(self.0))
2008            }
2009        }
2010    }
2011
2012    fn serialize_display<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
2013    where
2014        T: fmt::Display,
2015        S: serde::Serializer,
2016    {
2017        serializer.serialize_str(&value.to_string())
2018    }
2019
2020    fn serialize_option_display<T, S>(value: Option<&T>, serializer: S) -> Result<S::Ok, S::Error>
2021    where
2022        T: fmt::Display,
2023        S: serde::Serializer,
2024    {
2025        match value {
2026            Some(value) => serialize_display(value, serializer),
2027            None => serializer.serialize_none(),
2028        }
2029    }
2030
2031    fn serialize_option_or_sentinel<T, S>(
2032        value: Option<&T>,
2033        none_value: &str,
2034        serializer: S,
2035        serialize: impl FnOnce(&T, S) -> Result<S::Ok, S::Error>,
2036    ) -> Result<S::Ok, S::Error>
2037    where
2038        S: serde::Serializer,
2039    {
2040        match value {
2041            Some(value) => serialize(value, serializer),
2042            None => serializer.serialize_str(none_value),
2043        }
2044    }
2045
2046    fn deserialize_from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
2047    where
2048        T: FromStr,
2049        T::Err: fmt::Display,
2050        D: serde::Deserializer<'de>,
2051    {
2052        let value = String::deserialize(deserializer)?;
2053        value.parse::<T>().map_err(DeError::custom)
2054    }
2055
2056    fn deserialize_option_from_str<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
2057    where
2058        T: FromStr,
2059        T::Err: fmt::Display,
2060        D: serde::Deserializer<'de>,
2061    {
2062        let value = Option::<String>::deserialize(deserializer)?;
2063        value
2064            .map(|value| value.parse::<T>().map_err(DeError::custom))
2065            .transpose()
2066    }
2067
2068    fn deserialize_none_if_with<'de, T, D, E>(
2069        deserializer: D,
2070        none_if: &[&str],
2071        parse: impl FnOnce(&str) -> Result<T, E>,
2072    ) -> Result<Option<T>, D::Error>
2073    where
2074        D: serde::Deserializer<'de>,
2075        E: fmt::Display,
2076    {
2077        let value = String::deserialize(deserializer)?;
2078        parse_none_if_with(&value, none_if, parse)
2079    }
2080
2081    fn deserialize_option_none_if_with<'de, T, D, E>(
2082        deserializer: D,
2083        none_if: &[&str],
2084        parse: impl FnOnce(&str) -> Result<T, E>,
2085    ) -> Result<Option<T>, D::Error>
2086    where
2087        D: serde::Deserializer<'de>,
2088        E: fmt::Display,
2089    {
2090        let Some(value) = Option::<String>::deserialize(deserializer)? else {
2091            return Ok(None);
2092        };
2093        parse_none_if_with(&value, none_if, parse)
2094    }
2095
2096    fn parse_none_if_with<T, E, D>(
2097        value: &str,
2098        none_if: &[&str],
2099        parse: impl FnOnce(&str) -> Result<T, E>,
2100    ) -> Result<Option<T>, D>
2101    where
2102        E: fmt::Display,
2103        D: DeError,
2104    {
2105        if none_if.contains(&value) {
2106            return Ok(None);
2107        }
2108        parse(value).map(Some).map_err(DeError::custom)
2109    }
2110
2111    fn deserialize_bool(value: &str) -> Result<bool, &'static str> {
2112        match value {
2113            "1" => Ok(true),
2114            "0" => Ok(false),
2115            value if value.eq_ignore_ascii_case("true") => Ok(true),
2116            value if value.eq_ignore_ascii_case("false") => Ok(false),
2117            _ => Err("invalid boolean string"),
2118        }
2119    }
2120}
2121
2122#[cfg(feature = "serde")]
2123#[allow(clippy::missing_errors_doc)]
2124pub mod serde_integer {
2125    pub mod as_unix_time {
2126        use serde::Deserialize;
2127        use serde::de::Error as DeError;
2128
2129        use crate::OffsetDateTime;
2130
2131        pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
2132        where
2133            S: serde::Serializer,
2134        {
2135            serializer.serialize_i64(value.unix_timestamp())
2136        }
2137
2138        pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
2139        where
2140            D: serde::Deserializer<'de>,
2141        {
2142            let value = <i64 as Deserialize>::deserialize(deserializer)?;
2143            OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
2144        }
2145
2146        pub mod option {
2147            use serde::Deserialize;
2148            use serde::de::Error as DeError;
2149
2150            use crate::OffsetDateTime;
2151
2152            pub fn serialize<S>(
2153                value: &Option<OffsetDateTime>,
2154                serializer: S,
2155            ) -> Result<S::Ok, S::Error>
2156            where
2157                S: serde::Serializer,
2158            {
2159                match value {
2160                    Some(value) => super::serialize(value, serializer),
2161                    None => serializer.serialize_none(),
2162                }
2163            }
2164
2165            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
2166            where
2167                D: serde::Deserializer<'de>,
2168            {
2169                let value = <Option<i64> as Deserialize>::deserialize(deserializer)?;
2170                value
2171                    .map(|value| {
2172                        OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
2173                    })
2174                    .transpose()
2175            }
2176        }
2177    }
2178
2179    pub mod as_bool {
2180        use crate::serde_string::as_bool as string_bool;
2181
2182        pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
2183        where
2184            S: serde::Serializer,
2185        {
2186            serializer.serialize_u8(u8::from(*value))
2187        }
2188
2189        pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
2190        where
2191            D: serde::Deserializer<'de>,
2192        {
2193            string_bool::deserialize(deserializer)
2194        }
2195
2196        pub mod option {
2197            use crate::serde_string::as_bool::option as string_bool_option;
2198
2199            pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
2200            where
2201                S: serde::Serializer,
2202            {
2203                match value {
2204                    Some(value) => super::serialize(value, serializer),
2205                    None => serializer.serialize_none(),
2206                }
2207            }
2208
2209            pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
2210            where
2211                D: serde::Deserializer<'de>,
2212            {
2213                string_bool_option::deserialize(deserializer)
2214            }
2215        }
2216    }
2217}
2218
2219#[cfg(feature = "json")]
2220pub mod treat_error_as_none {
2221    use serde::de::DeserializeOwned;
2222    use serde::{Deserialize, Deserializer, Serialize, Serializer};
2223
2224    /// Serializes an optional value, omitting `None`.
2225    ///
2226    /// # Errors
2227    ///
2228    /// Returns an error if serializing the inner value fails.
2229    pub fn serialize<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
2230    where
2231        S: Serializer,
2232        T: Serialize,
2233    {
2234        match value {
2235            Some(inner) => inner.serialize(serializer),
2236            None => serializer.serialize_none(),
2237        }
2238    }
2239
2240    /// Deserializes an optional value, treating invalid inner values as `None`.
2241    ///
2242    /// # Errors
2243    ///
2244    /// Returns an error if the input cannot be read as JSON.
2245    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
2246    where
2247        D: Deserializer<'de>,
2248        T: DeserializeOwned,
2249    {
2250        let value = serde_json::Value::deserialize(deserializer)?;
2251        match T::deserialize(value) {
2252            Ok(parsed) => Ok(Some(parsed)),
2253            Err(_) => Ok(None),
2254        }
2255    }
2256}
2257
2258/// Inserts a header into a header map.
2259///
2260/// # Errors
2261///
2262/// Returns an error if the header name or value is invalid.
2263pub fn insert_header(
2264    headers: &mut http::HeaderMap,
2265    name: &'static str,
2266    value: &str,
2267) -> Result<(), Error> {
2268    headers.insert(
2269        HeaderName::from_bytes(name.as_bytes())?,
2270        HeaderValue::from_str(value)?,
2271    );
2272    Ok(())
2273}
2274
2275#[must_use]
2276pub fn has_json_content_type(headers: &http::HeaderMap) -> bool {
2277    headers
2278        .get(CONTENT_TYPE)
2279        .and_then(|value| value.to_str().ok())
2280        .is_some_and(is_json_media_type)
2281}
2282
2283fn is_json_media_type(value: &str) -> bool {
2284    let media_type = value.split(';').next().unwrap_or(value).trim();
2285    if media_type.eq_ignore_ascii_case("application/json") {
2286        return true;
2287    }
2288
2289    let Some((_, subtype)) = media_type.rsplit_once('/') else {
2290        return false;
2291    };
2292    ends_with_ignore_ascii_case(subtype, "+json")
2293}
2294
2295fn ends_with_ignore_ascii_case(value: &str, suffix: &str) -> bool {
2296    let value = value.as_bytes();
2297    let suffix = suffix.as_bytes();
2298    value.len() >= suffix.len() && value[value.len() - suffix.len()..].eq_ignore_ascii_case(suffix)
2299}
2300
2301fn append_percent_encoded(out: &mut String, bytes: &[u8]) {
2302    const HEX: &[u8; 16] = b"0123456789ABCDEF";
2303
2304    for &byte in bytes {
2305        if is_unreserved(byte) {
2306            out.push(byte as char);
2307        } else {
2308            out.push('%');
2309            out.push(HEX[(byte >> 4) as usize] as char);
2310            out.push(HEX[(byte & 0x0f) as usize] as char);
2311        }
2312    }
2313}
2314
2315const fn is_unreserved(byte: u8) -> bool {
2316    matches!(
2317        byte,
2318        b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~'
2319    )
2320}
2321
2322#[cfg(test)]
2323mod tests {
2324    use super::*;
2325
2326    #[cfg(all(feature = "serde", feature = "json"))]
2327    mod required_f64_none_if {
2328        use crate::serde_string::as_f64;
2329
2330        #[allow(clippy::ref_option)]
2331        pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
2332        where
2333            S: serde::Serializer,
2334        {
2335            as_f64::serialize_none_if(value, "NA", serializer)
2336        }
2337
2338        pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
2339        where
2340            D: serde::Deserializer<'de>,
2341        {
2342            as_f64::deserialize_none_if(deserializer, &["NA", "-"])
2343        }
2344    }
2345
2346    #[cfg(all(feature = "serde", feature = "json"))]
2347    mod optional_f64_none_if {
2348        use crate::serde_string::as_f64::{self, option};
2349
2350        #[allow(clippy::ref_option)]
2351        pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
2352        where
2353            S: serde::Serializer,
2354        {
2355            as_f64::serialize_none_if(value, "NA", serializer)
2356        }
2357
2358        pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
2359        where
2360            D: serde::Deserializer<'de>,
2361        {
2362            option::deserialize_none_if(deserializer, &["NA", "-"])
2363        }
2364    }
2365
2366    #[test]
2367    fn encodes_path_segments() {
2368        let mut out = String::new();
2369        append_path_segment(&mut out, "a/b c");
2370        assert_eq!(out, "a%2Fb%20c");
2371    }
2372
2373    #[test]
2374    fn appends_query_pairs() {
2375        let mut out = String::from("/pets");
2376        let mut first = true;
2377        append_query_pair(&mut out, &mut first, "tag name", "small/dog");
2378        append_query_pair(&mut out, &mut first, "limit", "10");
2379        assert_eq!(out, "/pets?tag%20name=small%2Fdog&limit=10");
2380    }
2381
2382    #[test]
2383    fn parses_range_strings() {
2384        assert_eq!(parse_range::<u8>("14-17").unwrap(), (Some(14), Some(17)));
2385        assert_eq!(parse_range::<u8>("14-").unwrap(), (Some(14), None));
2386        assert_eq!(parse_range::<u8>("-17").unwrap(), (None, Some(17)));
2387        assert_eq!(parse_range::<u8>("").unwrap(), (None, None));
2388        assert!(matches!(
2389            parse_range::<u8>("14-17-20"),
2390            Err(ParseRangeError::TooManySeparators)
2391        ));
2392    }
2393
2394    #[test]
2395    fn formats_range_strings() {
2396        assert_eq!(format_range(&Some(14), &Some(17)), "14-17");
2397        assert_eq!(format_range(&Some(14), &None::<u8>), "14-");
2398        assert_eq!(format_range(&None::<u8>, &Some(17)), "-17");
2399        assert_eq!(format_range(&None::<u8>, &None::<u8>), "");
2400    }
2401
2402    #[test]
2403    fn parses_and_formats_time_strings() {
2404        let time = parse_time("0620").unwrap();
2405        assert_eq!(time.hour(), 6);
2406        assert_eq!(time.minute(), 20);
2407        assert_eq!(format_time(&time), "0620");
2408        assert_eq!(parse_time("6:20"), Err(ParseTimeError::InvalidFormat));
2409        assert_eq!(parse_time("2400"), Err(ParseTimeError::ComponentRange));
2410    }
2411
2412    #[test]
2413    fn parses_and_formats_date_strings() {
2414        let date = parse_date("2024-07-16").unwrap();
2415        assert_eq!(
2416            date,
2417            Date::from_calendar_date(2024, Month::July, 16).unwrap()
2418        );
2419        assert_eq!(format_date(&date), "2024-07-16");
2420        assert_eq!(parse_date("2024-7-16"), Err(ParseDateError::InvalidFormat));
2421        assert_eq!(parse_date("not-a-date"), Err(ParseDateError::InvalidFormat));
2422    }
2423
2424    #[test]
2425    fn parses_and_formats_naive_datetime_strings() {
2426        let datetime = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
2427        assert_eq!(datetime.hour(), 23);
2428        assert_eq!(datetime.minute(), 59);
2429        assert_eq!(datetime.second(), 0);
2430        assert_eq!(format_naive_datetime(&datetime), "2024-07-16T23:59:00");
2431        assert_eq!(
2432            parse_naive_datetime("2024-07-16T23:59"),
2433            Err(ParseNaiveDateTimeError::InvalidFormat)
2434        );
2435        assert_eq!(
2436            parse_naive_datetime("2024-07-16 23:59:00"),
2437            Err(ParseNaiveDateTimeError::InvalidFormat)
2438        );
2439    }
2440
2441    #[test]
2442    fn formats_unix_time_seconds() {
2443        let datetime = OffsetDateTime::from_unix_timestamp(-1).unwrap();
2444        assert_eq!(format_unix_time(&datetime), "-1");
2445    }
2446
2447    #[cfg(all(feature = "serde", feature = "json"))]
2448    #[test]
2449    fn serde_string_naive_datetime_round_trips() {
2450        #[derive(serde::Deserialize, serde::Serialize)]
2451        struct Value {
2452            #[serde(with = "crate::serde_string::as_naive_datetime")]
2453            at: PrimitiveDateTime,
2454        }
2455
2456        let at = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
2457        let encoded = serde_json::to_value(Value { at }).unwrap();
2458        assert_eq!(encoded, serde_json::json!({ "at": "2024-07-16T23:59:00" }));
2459
2460        let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2461        assert_eq!(decoded.at, at);
2462    }
2463
2464    #[cfg(all(feature = "serde", feature = "json"))]
2465    #[test]
2466    fn serde_string_url_round_trips_and_rejects_invalid_values() {
2467        #[derive(Debug, serde::Serialize, serde::Deserialize)]
2468        struct Links {
2469            #[serde(with = "crate::serde_string::as_url")]
2470            url: Url,
2471            #[serde(default, with = "crate::serde_string::as_url::option")]
2472            optional: Option<Url>,
2473        }
2474
2475        let links: Links = serde_json::from_str(
2476            r#"{"url":"https://EXAMPLE.com:443/path","optional":"mailto:hello@example.com"}"#,
2477        )
2478        .unwrap();
2479        assert_eq!(links.url.as_str(), "https://example.com/path");
2480        assert_eq!(links.optional.as_ref().unwrap().scheme(), "mailto");
2481        assert_eq!(
2482            serde_json::to_value(&links).unwrap()["url"],
2483            "https://example.com/path"
2484        );
2485        for optional in ["null", "\"/relative\"", "42"] {
2486            let json = format!(r#"{{"url":"https://example.com/","optional":{optional}}}"#);
2487            let result = serde_json::from_str::<Links>(&json);
2488            if optional == "null" {
2489                assert!(result.unwrap().optional.is_none());
2490            } else {
2491                assert!(result.is_err());
2492            }
2493        }
2494        assert!(serde_json::from_str::<Links>(r#"{"url":"/relative"}"#).is_err());
2495        assert!(serde_json::from_str::<Links>(r#"{"url":42}"#).is_err());
2496    }
2497
2498    #[cfg(all(feature = "serde", feature = "json"))]
2499    #[test]
2500    fn serde_string_date_round_trips() {
2501        #[derive(serde::Deserialize, serde::Serialize)]
2502        struct Value {
2503            #[serde(with = "crate::serde_string::as_date")]
2504            day: Date,
2505        }
2506
2507        let date = Date::from_calendar_date(2024, Month::July, 16).unwrap();
2508        let encoded = serde_json::to_value(Value { day: date }).unwrap();
2509        assert_eq!(encoded, serde_json::json!({ "day": "2024-07-16" }));
2510
2511        let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2512        assert_eq!(decoded.day, date);
2513    }
2514
2515    #[test]
2516    fn recognizes_json_content_types() {
2517        let mut headers = http::HeaderMap::new();
2518        headers.insert(
2519            CONTENT_TYPE,
2520            http::HeaderValue::from_static("application/problem+json; charset=utf-8"),
2521        );
2522        assert!(has_json_content_type(&headers));
2523    }
2524
2525    #[test]
2526    fn response_parts_holds_status_headers_body() {
2527        let mut headers = http::HeaderMap::new();
2528        headers.insert(
2529            CONTENT_TYPE,
2530            http::HeaderValue::from_static("application/json"),
2531        );
2532        let body = br#"{"ok":true}"#.to_vec();
2533        let parts = ResponseParts {
2534            status: http::StatusCode::OK,
2535            headers,
2536            body,
2537        };
2538        assert_eq!(parts.status, http::StatusCode::OK);
2539        assert_eq!(parts.headers.get(CONTENT_TYPE).unwrap(), "application/json");
2540        assert_eq!(parts.body, br#"{"ok":true}"#);
2541    }
2542
2543    #[cfg(feature = "json")]
2544    #[test]
2545    fn projected_json_unwraps_and_maps_fields() {
2546        let body = br#"{
2547            "odata.metadata": "https://example.test/metadata",
2548            "value": [
2549                {"Link": "https://example.test/a", "Name": "A"},
2550                {"Link": "https://example.test/b", "Name": "B"}
2551            ]
2552        }"#;
2553
2554        let rows = from_projected_json_slice::<Vec<JsonValue>>(body, "value", None).unwrap();
2555        assert_eq!(rows.len(), 2);
2556        assert_eq!(rows[0]["Name"], "A");
2557
2558        let links = from_projected_json_slice::<Vec<String>>(body, "value", Some("Link")).unwrap();
2559        assert_eq!(
2560            links,
2561            vec![
2562                "https://example.test/a".to_owned(),
2563                "https://example.test/b".to_owned()
2564            ]
2565        );
2566    }
2567
2568    #[cfg(feature = "json")]
2569    #[test]
2570    fn projected_json_preserves_optional_missing_fields_as_null() {
2571        let missing = br#"{"metadata":"present"}"#;
2572        let value = from_projected_json_slice::<Option<Vec<String>>>(missing, "value", None)
2573            .expect("optional missing projection");
2574        assert_eq!(value, None);
2575
2576        let rows = br#"{"value":[{}, {"Link":"present"}]}"#;
2577        let links = from_projected_json_slice::<Vec<Option<String>>>(rows, "value", Some("Link"))
2578            .expect("optional mapped field");
2579        assert_eq!(links, vec![None, Some("present".to_owned())]);
2580    }
2581
2582    #[cfg(feature = "json")]
2583    #[test]
2584    fn projected_json_rejects_invalid_container_shapes() {
2585        let scalar = from_projected_json_slice::<Vec<String>>(
2586            br#"{"value":"not-an-array"}"#,
2587            "value",
2588            Some("Link"),
2589        );
2590        assert!(matches!(scalar, Err(Error::InvalidResponse(_))));
2591
2592        let scalar_item = from_projected_json_slice::<Vec<String>>(
2593            br#"{"value":["not-an-object"]}"#,
2594            "value",
2595            Some("Link"),
2596        );
2597        assert!(matches!(scalar_item, Err(Error::InvalidResponse(_))));
2598    }
2599
2600    #[cfg(all(feature = "serde", feature = "json"))]
2601    #[test]
2602    fn serde_string_bool_accepts_string_and_numeric_values() {
2603        #[derive(serde::Deserialize, serde::Serialize)]
2604        struct Value {
2605            #[serde(with = "crate::serde_string::as_bool")]
2606            monitored: bool,
2607        }
2608
2609        let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
2610        assert!(!numeric.monitored);
2611
2612        let string = serde_json::from_str::<Value>(r#"{"monitored":"1"}"#).unwrap();
2613        assert!(string.monitored);
2614
2615        let encoded = serde_json::to_value(Value { monitored: false }).unwrap();
2616        assert_eq!(encoded, serde_json::json!({ "monitored": "0" }));
2617    }
2618
2619    #[cfg(all(feature = "serde", feature = "json"))]
2620    #[test]
2621    fn serde_string_none_if_is_strict_and_canonical() {
2622        #[derive(Debug, serde::Deserialize, serde::Serialize)]
2623        struct Value {
2624            #[serde(with = "required_f64_none_if")]
2625            required: Option<f64>,
2626            #[serde(default, with = "optional_f64_none_if")]
2627            optional: Option<f64>,
2628        }
2629
2630        let valid =
2631            serde_json::from_str::<Value>(r#"{"required":"28.7","optional":"10.5"}"#).unwrap();
2632        assert_eq!(valid.required, Some(28.7));
2633        assert_eq!(valid.optional, Some(10.5));
2634
2635        let sentinel =
2636            serde_json::from_str::<Value>(r#"{"required":"-","optional":"NA"}"#).unwrap();
2637        assert_eq!(sentinel.required, None);
2638        assert_eq!(sentinel.optional, None);
2639
2640        let null_optional =
2641            serde_json::from_str::<Value>(r#"{"required":"28.7","optional":null}"#).unwrap();
2642        assert_eq!(null_optional.optional, None);
2643        assert!(serde_json::from_str::<Value>(r#"{"required":null}"#).is_err());
2644        assert!(serde_json::from_str::<Value>(r#"{"required":"unexpected"}"#).is_err());
2645
2646        let encoded = serde_json::to_value(Value {
2647            required: None,
2648            optional: Some(10.5),
2649        })
2650        .unwrap();
2651        assert_eq!(
2652            encoded,
2653            serde_json::json!({"required": "NA", "optional": "10.5"})
2654        );
2655    }
2656
2657    #[cfg(all(feature = "serde", feature = "json"))]
2658    #[test]
2659    fn serde_string_none_if_preserves_bool_and_time_parsers() {
2660        use crate::serde_string::{as_bool, as_time::option as time_option};
2661        use serde_json::Value;
2662
2663        let bool_sentinel =
2664            as_bool::deserialize_none_if(Value::String("NA".to_owned()), &["NA"]).unwrap();
2665        assert_eq!(bool_sentinel, None);
2666
2667        let bool_numeric = as_bool::deserialize_none_if(Value::from(1), &["NA"]).unwrap();
2668        assert_eq!(bool_numeric, Some(true));
2669
2670        let empty_time =
2671            time_option::deserialize_none_if(Value::String("  ".to_owned()), &["-"]).unwrap();
2672        assert_eq!(empty_time, None);
2673        assert!(
2674            time_option::deserialize_none_if(Value::String("invalid".to_owned()), &["-"]).is_err()
2675        );
2676    }
2677
2678    #[cfg(all(feature = "serde", feature = "json"))]
2679    #[test]
2680    fn serde_integer_bool_accepts_numeric_values() {
2681        #[derive(serde::Deserialize, serde::Serialize)]
2682        struct Value {
2683            #[serde(with = "crate::serde_integer::as_bool")]
2684            monitored: bool,
2685        }
2686
2687        let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
2688        assert!(!numeric.monitored);
2689
2690        let encoded = serde_json::to_value(Value { monitored: true }).unwrap();
2691        assert_eq!(encoded, serde_json::json!({ "monitored": 1 }));
2692    }
2693
2694    #[cfg(all(feature = "serde", feature = "json"))]
2695    #[test]
2696    fn serde_string_unix_time_round_trips() {
2697        #[derive(serde::Deserialize, serde::Serialize)]
2698        struct Value {
2699            #[serde(with = "crate::serde_string::as_unix_time")]
2700            at: OffsetDateTime,
2701        }
2702
2703        let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
2704        let encoded = serde_json::to_value(Value { at }).unwrap();
2705        assert_eq!(encoded, serde_json::json!({ "at": "1719892800" }));
2706
2707        let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2708        assert_eq!(decoded.at, at);
2709    }
2710
2711    #[cfg(all(feature = "serde", feature = "json"))]
2712    #[test]
2713    fn serde_integer_unix_time_round_trips_and_handles_null() {
2714        #[derive(serde::Deserialize, serde::Serialize)]
2715        struct Value {
2716            #[serde(with = "crate::serde_integer::as_unix_time")]
2717            at: OffsetDateTime,
2718            #[serde(with = "crate::serde_integer::as_unix_time::option")]
2719            maybe_at: Option<OffsetDateTime>,
2720        }
2721
2722        let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
2723        let encoded = serde_json::to_value(Value { at, maybe_at: None }).unwrap();
2724        assert_eq!(
2725            encoded,
2726            serde_json::json!({ "at": 1_719_892_800, "maybe_at": null })
2727        );
2728
2729        let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2730        assert_eq!(decoded.at, at);
2731        assert_eq!(decoded.maybe_at, None);
2732    }
2733
2734    #[cfg(all(feature = "serde", feature = "json"))]
2735    #[test]
2736    fn serde_integer_unix_time_rejects_out_of_range_values() {
2737        #[derive(serde::Deserialize)]
2738        #[allow(dead_code)]
2739        struct Value {
2740            #[serde(with = "crate::serde_integer::as_unix_time")]
2741            at: OffsetDateTime,
2742        }
2743
2744        assert!(serde_json::from_str::<Value>(r#"{"at":9223372036854775807}"#).is_err());
2745    }
2746}
2747
2748#[cfg(all(test, feature = "json"))]
2749mod buffered_response_tests {
2750    use super::*;
2751    use std::borrow::Cow;
2752
2753    struct BorrowingAction;
2754
2755    #[derive(Debug, serde::Deserialize)]
2756    struct Record<'a> {
2757        #[serde(borrow)]
2758        name: Cow<'a, str>,
2759    }
2760
2761    impl Action for BorrowingAction {
2762        type RequestBody = Box<[u8]>;
2763        type Response<'de> = Record<'de>;
2764
2765        fn request(self) -> Result<http::Request<Self::RequestBody>, Error> {
2766            Ok(http::Request::new(Box::from(&b"{}"[..])))
2767        }
2768
2769        fn decode(parts: ResponseParts<&[u8]>) -> Result<Record<'_>, Error> {
2770            assert_eq!(parts.status, http::StatusCode::OK);
2771            assert_eq!(parts.headers["x-request-id"], "test");
2772            from_json_slice(parts.body)
2773        }
2774    }
2775
2776    impl OwnedAction for BorrowingAction {
2777        type OwnedResponse = String;
2778
2779        fn decode_owned(response: ResponseParts<&[u8]>) -> Result<String, Error> {
2780            Ok(Self::decode(response)?.name.into_owned())
2781        }
2782    }
2783
2784    #[test]
2785    fn borrows_unescaped_strings_from_a_custom_buffer_and_preserves_parts() {
2786        let body: Box<[u8]> = Box::from(&br#"{"name":"Mochi"}"#[..]);
2787        let original_ptr = body.as_ptr();
2788        let response = BufferedResponse::<BorrowingAction, _>::new(ResponseParts {
2789            status: http::StatusCode::OK,
2790            headers: http::HeaderMap::from_iter([(
2791                HeaderName::from_static("x-request-id"),
2792                http::HeaderValue::from_static("test"),
2793            )]),
2794            body,
2795        });
2796        let record = response.decode().unwrap();
2797        assert!(matches!(record.name, Cow::Borrowed("Mochi")));
2798        assert_eq!(record.name.as_ptr(), response.parts().body[9..].as_ptr());
2799        assert!(matches!(response.decode().unwrap().name, Cow::Borrowed(_)));
2800        drop(record);
2801        let parts = response.into_parts();
2802        assert_eq!(parts.body.as_ptr(), original_ptr);
2803        assert_eq!(parts.headers["x-request-id"], "test");
2804        let owned = BufferedResponse::<BorrowingAction, _>::new(parts)
2805            .decode_owned()
2806            .unwrap();
2807        assert_eq!(owned, "Mochi");
2808        let request = BorrowingAction.request().unwrap();
2809        assert_eq!(&**request.body(), b"{}");
2810    }
2811
2812    #[test]
2813    fn escaped_strings_are_owned_and_invalid_json_is_reported_at_decode() {
2814        let record: Record<'_> = from_json_slice(br#"{"name":"Mo\u0063hi"}"#).unwrap();
2815        assert!(matches!(record.name, Cow::Owned(ref value) if value == "Mochi"));
2816        assert!(from_json_slice::<Record<'_>>(b"invalid").is_err());
2817    }
2818}