1#![forbid(unsafe_code)]
2
3use std::fmt;
4use std::str::FromStr;
5
6use http::header::{self, CONTENT_TYPE, HeaderName, HeaderValue};
7#[cfg(feature = "json")]
8use serde::de;
9#[cfg(feature = "json")]
10pub use serde_json::Value as JsonValue;
11use time::Month;
12use time::format_description::well_known::Rfc3339;
13pub use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
14
15use tracing::{debug, instrument};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct RequestParts<B> {
19 pub method: http::Method,
20 pub uri: String,
21 pub headers: http::HeaderMap,
22 pub body: B,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ResponseParts<B> {
27 pub status: http::StatusCode,
28 pub headers: http::HeaderMap,
29 pub body: B,
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum Error {
34 #[error("failed to build HTTP message: {0}")]
35 Http(#[from] http::Error),
36
37 #[error("invalid HTTP header value: {0}")]
38 InvalidHeaderValue(#[from] header::InvalidHeaderValue),
39
40 #[error("invalid HTTP header name: {0}")]
41 InvalidHeaderName(#[from] header::InvalidHeaderName),
42
43 #[error("missing required field `{0}`")]
44 MissingRequired(&'static str),
45
46 #[error("{0}")]
47 InvalidResponse(&'static str),
48
49 #[cfg(feature = "json")]
50 #[error("JSON error: {0}")]
51 Json(#[from] serde_json::Error),
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
55pub enum ParseRangeError {
56 #[error("range contains more than one `-` separator")]
57 TooManySeparators,
58
59 #[error("invalid range minimum `{value}`: {message}")]
60 InvalidMinimum { value: String, message: String },
61
62 #[error("invalid range maximum `{value}`: {message}")]
63 InvalidMaximum { value: String, message: String },
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
67pub enum ParseTimeError {
68 #[error("time must be exactly four ASCII digits in HHMM format")]
69 InvalidFormat,
70
71 #[error("time is outside valid HHMM range")]
72 ComponentRange,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
76pub enum ParseDateError {
77 #[error("date must be in YYYY-MM-DD format")]
78 InvalidFormat,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
82pub enum ParseNaiveDateTimeError {
83 #[error("datetime must be in YYYY-MM-DDTHH:mm:ss format")]
84 InvalidFormat,
85}
86
87pub trait Action {
88 type Response;
89
90 fn request(self) -> Result<http::Request<Vec<u8>>, Error>;
96
97 fn decode<B: AsRef<[u8]>>(response: ResponseParts<B>) -> Result<Self::Response, Error>;
103}
104
105#[instrument(skip_all, fields(method = %method, uri = %uri))]
111pub fn into_request<B>(
112 RequestParts {
113 method,
114 uri,
115 headers,
116 body,
117 }: RequestParts<B>,
118) -> Result<http::Request<B>, Error> {
119 debug!("building HTTP request");
120 let mut request = http::Request::builder()
121 .method(method)
122 .uri(uri)
123 .body(body)?;
124 *request.headers_mut() = headers;
125 Ok(request)
126}
127
128#[instrument(skip_all, fields(method = %method, uri = %uri))]
134pub fn into_empty_request(
135 RequestParts {
136 method,
137 uri,
138 headers,
139 body: (),
140 }: RequestParts<()>,
141) -> Result<http::Request<Vec<u8>>, Error> {
142 debug!("building empty HTTP request");
143 let mut request = http::Request::builder()
144 .method(method)
145 .uri(uri)
146 .body(vec![])?;
147 *request.headers_mut() = headers;
148 Ok(request)
149}
150
151#[cfg(feature = "json")]
157#[instrument(skip_all, fields(method = %method, uri = %uri))]
158pub fn into_json_request<T>(
159 RequestParts {
160 method,
161 uri,
162 headers,
163 body,
164 }: RequestParts<T>,
165) -> Result<http::Request<Vec<u8>>, Error>
166where
167 T: serde::Serialize,
168{
169 debug!("building JSON HTTP request");
170 let body = serde_json::to_vec(&body)?;
171 let mut request = http::Request::builder()
172 .method(method)
173 .uri(uri)
174 .body(body)?;
175 *request.headers_mut() = headers;
176 if !request.headers().contains_key(CONTENT_TYPE) {
177 request.headers_mut().insert(
178 CONTENT_TYPE,
179 http::HeaderValue::from_static("application/json"),
180 );
181 }
182 Ok(request)
183}
184
185#[cfg(feature = "json")]
191#[instrument(skip_all, fields(method = %method, uri = %uri))]
192pub fn into_optional_json_request<T>(
193 RequestParts {
194 method,
195 uri,
196 headers,
197 body,
198 }: RequestParts<Option<T>>,
199) -> Result<http::Request<Vec<u8>>, Error>
200where
201 T: serde::Serialize,
202{
203 match body {
204 Some(body) => into_json_request(RequestParts {
205 method,
206 uri,
207 headers,
208 body,
209 }),
210 None => into_empty_request(RequestParts {
211 method,
212 uri,
213 headers,
214 body: (),
215 }),
216 }
217}
218
219#[cfg(feature = "json")]
225#[instrument(skip_all)]
226pub fn from_json_slice<T>(body: &[u8]) -> Result<T, Error>
227where
228 T: de::DeserializeOwned,
229{
230 debug!("deserializing JSON response");
231 Ok(serde_json::from_slice(body)?)
232}
233
234pub fn append_path_segment(out: &mut String, value: &str) {
235 append_percent_encoded(out, value.as_bytes());
236}
237
238pub fn append_query_pair(out: &mut String, first: &mut bool, key: &str, value: &str) {
239 if *first {
240 out.push('?');
241 *first = false;
242 } else {
243 out.push('&');
244 }
245 append_percent_encoded(out, key.as_bytes());
246 out.push('=');
247 append_percent_encoded(out, value.as_bytes());
248}
249
250#[must_use]
251pub fn format_offset_datetime(value: &OffsetDateTime) -> String {
252 value.format(&Rfc3339).unwrap_or_else(|_| value.to_string())
253}
254
255#[must_use]
256pub fn format_unix_time(value: &OffsetDateTime) -> String {
257 value.unix_timestamp().to_string()
258}
259
260#[must_use]
261pub fn format_date(value: &Date) -> String {
262 format!(
263 "{:04}-{:02}-{:02}",
264 value.year(),
265 u8::from(value.month()),
266 value.day()
267 )
268}
269
270pub fn parse_date(value: &str) -> Result<Date, ParseDateError> {
276 let value = value.trim().as_bytes();
277 if value.len() != 10 || value[4] != b'-' || value[7] != b'-' {
278 return Err(ParseDateError::InvalidFormat);
279 }
280
281 for (index, byte) in value.iter().enumerate() {
282 if matches!(index, 4 | 7) {
283 if *byte != b'-' {
284 return Err(ParseDateError::InvalidFormat);
285 }
286 } else if !byte.is_ascii_digit() {
287 return Err(ParseDateError::InvalidFormat);
288 }
289 }
290
291 let year = parse_date_year(&value[0..4])?;
292 let month = parse_date_u8(&value[5..7])?;
293 let day = parse_date_u8(&value[8..10])?;
294 let month = Month::try_from(month).map_err(|_| ParseDateError::InvalidFormat)?;
295 Date::from_calendar_date(year, month, day).map_err(|_| ParseDateError::InvalidFormat)
296}
297
298fn parse_date_year(bytes: &[u8]) -> Result<i32, ParseDateError> {
299 let mut value = 0i32;
300 for byte in bytes {
301 value = value
302 .checked_mul(10)
303 .and_then(|value| value.checked_add(i32::from(*byte - b'0')))
304 .ok_or(ParseDateError::InvalidFormat)?;
305 }
306 Ok(value)
307}
308
309fn parse_date_u8(bytes: &[u8]) -> Result<u8, ParseDateError> {
310 let mut value = 0u16;
311 for byte in bytes {
312 value = value
313 .checked_mul(10)
314 .and_then(|value| value.checked_add(u16::from(*byte - b'0')))
315 .ok_or(ParseDateError::InvalidFormat)?;
316 }
317 u8::try_from(value).map_err(|_| ParseDateError::InvalidFormat)
318}
319
320#[must_use]
321pub fn format_naive_datetime(value: &PrimitiveDateTime) -> String {
322 format!(
323 "{}T{:02}:{:02}:{:02}",
324 format_date(&value.date()),
325 value.hour(),
326 value.minute(),
327 value.second()
328 )
329}
330
331pub fn parse_naive_datetime(value: &str) -> Result<PrimitiveDateTime, ParseNaiveDateTimeError> {
337 let value = value.trim();
338 let bytes = value.as_bytes();
339 if bytes.len() != 19
340 || bytes[4] != b'-'
341 || bytes[7] != b'-'
342 || bytes[10] != b'T'
343 || bytes[13] != b':'
344 || bytes[16] != b':'
345 {
346 return Err(ParseNaiveDateTimeError::InvalidFormat);
347 }
348
349 for (index, byte) in bytes.iter().enumerate() {
350 if matches!(index, 4 | 7 | 10 | 13 | 16) {
351 continue;
352 }
353 if !byte.is_ascii_digit() {
354 return Err(ParseNaiveDateTimeError::InvalidFormat);
355 }
356 }
357
358 let date = parse_date(&value[0..10]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
359 let hour = parse_date_u8(&bytes[11..13]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
360 let minute =
361 parse_date_u8(&bytes[14..16]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
362 let second =
363 parse_date_u8(&bytes[17..19]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
364 let time =
365 Time::from_hms(hour, minute, second).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
366 Ok(PrimitiveDateTime::new(date, time))
367}
368
369pub fn parse_time(value: &str) -> Result<Time, ParseTimeError> {
375 let value = value.trim();
376 let bytes = value.as_bytes();
377 if bytes.len() != 4 || !bytes.iter().all(u8::is_ascii_digit) {
378 return Err(ParseTimeError::InvalidFormat);
379 }
380
381 let hour = (bytes[0] - b'0') * 10 + (bytes[1] - b'0');
382 let minute = (bytes[2] - b'0') * 10 + (bytes[3] - b'0');
383 Time::from_hms(hour, minute, 0).map_err(|_| ParseTimeError::ComponentRange)
384}
385
386#[must_use]
387pub fn format_time(value: &Time) -> String {
388 format!("{:02}{:02}", value.hour(), value.minute())
389}
390
391#[must_use]
392pub fn format_bool(value: &bool) -> &'static str {
393 if *value { "1" } else { "0" }
394}
395
396pub fn parse_range<T>(value: &str) -> Result<(Option<T>, Option<T>), ParseRangeError>
402where
403 T: FromStr,
404 T::Err: fmt::Display,
405{
406 let value = value.trim();
407 if value.is_empty() {
408 return Ok((None, None));
409 }
410
411 let (min, max) = match value.split_once('-') {
412 Some((min, max)) => {
413 if max.contains('-') {
414 return Err(ParseRangeError::TooManySeparators);
415 }
416 (min, max)
417 }
418 None => (value, value),
419 };
420
421 Ok((parse_range_min(min)?, parse_range_max(max)?))
422}
423
424#[must_use]
425pub fn format_range<T>(min: &Option<T>, max: &Option<T>) -> String
426where
427 T: fmt::Display,
428{
429 match (min, max) {
430 (Some(min), Some(max)) => format!("{min}-{max}"),
431 (Some(min), None) => format!("{min}-"),
432 (None, Some(max)) => format!("-{max}"),
433 (None, None) => String::new(),
434 }
435}
436
437fn parse_range_min<T>(value: &str) -> Result<Option<T>, ParseRangeError>
438where
439 T: FromStr,
440 T::Err: fmt::Display,
441{
442 parse_range_bound(value, |value, message| ParseRangeError::InvalidMinimum {
443 value,
444 message,
445 })
446}
447
448fn parse_range_max<T>(value: &str) -> Result<Option<T>, ParseRangeError>
449where
450 T: FromStr,
451 T::Err: fmt::Display,
452{
453 parse_range_bound(value, |value, message| ParseRangeError::InvalidMaximum {
454 value,
455 message,
456 })
457}
458
459fn parse_range_bound<T>(
460 value: &str,
461 invalid: impl FnOnce(String, String) -> ParseRangeError,
462) -> Result<Option<T>, ParseRangeError>
463where
464 T: FromStr,
465 T::Err: fmt::Display,
466{
467 let value = value.trim();
468 if value.is_empty() {
469 return Ok(None);
470 }
471
472 value
473 .parse::<T>()
474 .map(Some)
475 .map_err(|err| invalid(value.to_owned(), err.to_string()))
476}
477
478#[cfg(feature = "serde")]
479#[allow(clippy::missing_errors_doc)]
480pub mod serde_string {
481 use std::fmt;
482 use std::str::FromStr;
483
484 use serde::Deserialize;
485 use serde::de::Error as DeError;
486 macro_rules! string_from_str_module {
487 ($module:ident, $ty:ty) => {
488 pub mod $module {
489 pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
490 where
491 S: serde::Serializer,
492 {
493 super::serialize_display(value, serializer)
494 }
495
496 pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
497 where
498 D: serde::Deserializer<'de>,
499 {
500 super::deserialize_from_str(deserializer)
501 }
502
503 pub fn serialize_none_if<S>(
504 value: &Option<$ty>,
505 none_value: &str,
506 serializer: S,
507 ) -> Result<S::Ok, S::Error>
508 where
509 S: serde::Serializer,
510 {
511 super::serialize_option_or_sentinel(
512 value.as_ref(),
513 none_value,
514 serializer,
515 serialize,
516 )
517 }
518
519 pub fn deserialize_none_if<'de, D>(
520 deserializer: D,
521 none_if: &[&str],
522 ) -> Result<Option<$ty>, D::Error>
523 where
524 D: serde::Deserializer<'de>,
525 {
526 super::deserialize_none_if_with(deserializer, none_if, str::parse::<$ty>)
527 }
528
529 pub mod option {
530 pub fn serialize<S>(
531 value: &Option<$ty>,
532 serializer: S,
533 ) -> Result<S::Ok, S::Error>
534 where
535 S: serde::Serializer,
536 {
537 super::super::serialize_option_display(value.as_ref(), serializer)
538 }
539
540 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
541 where
542 D: serde::Deserializer<'de>,
543 {
544 super::super::deserialize_option_from_str(deserializer)
545 }
546
547 pub fn deserialize_none_if<'de, D>(
548 deserializer: D,
549 none_if: &[&str],
550 ) -> Result<Option<$ty>, D::Error>
551 where
552 D: serde::Deserializer<'de>,
553 {
554 super::super::deserialize_option_none_if_with(
555 deserializer,
556 none_if,
557 str::parse::<$ty>,
558 )
559 }
560 }
561 }
562 };
563 }
564
565 macro_rules! string_float_module {
566 ($module:ident, $ty:ty) => {
567 pub mod $module {
568 use serde::Deserialize;
569 use serde::de::Error as DeError;
570
571 pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
572 where
573 S: serde::Serializer,
574 {
575 super::serialize_display(value, serializer)
576 }
577
578 pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
579 where
580 D: serde::Deserializer<'de>,
581 {
582 let value = <String as Deserialize>::deserialize(deserializer)?;
583 fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
584 }
585
586 pub fn serialize_none_if<S>(
587 value: &Option<$ty>,
588 none_value: &str,
589 serializer: S,
590 ) -> Result<S::Ok, S::Error>
591 where
592 S: serde::Serializer,
593 {
594 super::serialize_option_or_sentinel(
595 value.as_ref(),
596 none_value,
597 serializer,
598 serialize,
599 )
600 }
601
602 pub fn deserialize_none_if<'de, D>(
603 deserializer: D,
604 none_if: &[&str],
605 ) -> Result<Option<$ty>, D::Error>
606 where
607 D: serde::Deserializer<'de>,
608 {
609 super::deserialize_none_if_with(deserializer, none_if, |value| {
610 fast_float2::parse::<$ty, _>(value)
611 })
612 }
613
614 pub mod option {
615 use serde::Deserialize;
616 use serde::de::Error as DeError;
617
618 pub fn serialize<S>(
619 value: &Option<$ty>,
620 serializer: S,
621 ) -> Result<S::Ok, S::Error>
622 where
623 S: serde::Serializer,
624 {
625 super::super::serialize_option_display(value.as_ref(), serializer)
626 }
627
628 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
629 where
630 D: serde::Deserializer<'de>,
631 {
632 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
633 value
634 .map(|value| {
635 fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
636 })
637 .transpose()
638 }
639
640 pub fn deserialize_none_if<'de, D>(
641 deserializer: D,
642 none_if: &[&str],
643 ) -> Result<Option<$ty>, D::Error>
644 where
645 D: serde::Deserializer<'de>,
646 {
647 super::super::deserialize_option_none_if_with(
648 deserializer,
649 none_if,
650 |value| fast_float2::parse::<$ty, _>(value),
651 )
652 }
653 }
654 }
655 };
656 }
657
658 string_from_str_module!(as_u8, u8);
659 string_from_str_module!(as_u16, u16);
660 string_from_str_module!(as_u32, u32);
661 string_from_str_module!(as_u64, u64);
662 string_from_str_module!(as_i8, i8);
663 string_from_str_module!(as_i16, i16);
664 string_from_str_module!(as_i32, i32);
665 string_from_str_module!(as_i64, i64);
666 string_float_module!(as_f32, f32);
667 string_float_module!(as_f64, f64);
668
669 pub mod as_bool {
670 use std::fmt;
671
672 use serde::de::{Error as DeError, Visitor};
673
674 pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
675 where
676 S: serde::Serializer,
677 {
678 serializer.serialize_str(crate::format_bool(value))
679 }
680
681 pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
682 where
683 D: serde::Deserializer<'de>,
684 {
685 deserializer.deserialize_any(BoolVisitor)
686 }
687
688 pub fn serialize_none_if<S>(
689 value: &Option<bool>,
690 none_value: &str,
691 serializer: S,
692 ) -> Result<S::Ok, S::Error>
693 where
694 S: serde::Serializer,
695 {
696 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
697 }
698
699 pub fn deserialize_none_if<'de, D>(
700 deserializer: D,
701 none_if: &[&str],
702 ) -> Result<Option<bool>, D::Error>
703 where
704 D: serde::Deserializer<'de>,
705 {
706 deserializer.deserialize_any(BoolNoneIfVisitor { none_if })
707 }
708
709 struct BoolVisitor;
710
711 impl Visitor<'_> for BoolVisitor {
712 type Value = bool;
713
714 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
715 formatter.write_str("a boolean string or numeric boolean")
716 }
717
718 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
719 Ok(value)
720 }
721
722 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
723 where
724 E: DeError,
725 {
726 super::deserialize_bool(value).map_err(DeError::custom)
727 }
728
729 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
730 where
731 E: DeError,
732 {
733 self.visit_str(&value)
734 }
735
736 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
737 where
738 E: DeError,
739 {
740 match value {
741 0 => Ok(false),
742 1 => Ok(true),
743 _ => Err(DeError::custom("invalid boolean number")),
744 }
745 }
746
747 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
748 where
749 E: DeError,
750 {
751 match value {
752 0 => Ok(false),
753 1 => Ok(true),
754 _ => Err(DeError::custom("invalid boolean number")),
755 }
756 }
757 }
758
759 struct BoolNoneIfVisitor<'a> {
760 none_if: &'a [&'a str],
761 }
762
763 impl Visitor<'_> for BoolNoneIfVisitor<'_> {
764 type Value = Option<bool>;
765
766 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
767 formatter.write_str("a boolean string, numeric boolean, or configured sentinel")
768 }
769
770 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
771 Ok(Some(value))
772 }
773
774 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
775 where
776 E: DeError,
777 {
778 if self.none_if.contains(&value) {
779 Ok(None)
780 } else {
781 super::deserialize_bool(value)
782 .map(Some)
783 .map_err(DeError::custom)
784 }
785 }
786
787 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
788 where
789 E: DeError,
790 {
791 self.visit_str(&value)
792 }
793
794 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
795 where
796 E: DeError,
797 {
798 match value {
799 0 => Ok(Some(false)),
800 1 => Ok(Some(true)),
801 _ => Err(DeError::custom("invalid boolean number")),
802 }
803 }
804
805 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
806 where
807 E: DeError,
808 {
809 match value {
810 0 => Ok(Some(false)),
811 1 => Ok(Some(true)),
812 _ => Err(DeError::custom("invalid boolean number")),
813 }
814 }
815 }
816
817 pub mod option {
818 use std::fmt;
819
820 use serde::de::Visitor;
821
822 pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
823 where
824 S: serde::Serializer,
825 {
826 match value {
827 Some(value) => super::serialize(value, serializer),
828 None => serializer.serialize_none(),
829 }
830 }
831
832 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
833 where
834 D: serde::Deserializer<'de>,
835 {
836 deserializer.deserialize_option(BoolOptionVisitor)
837 }
838
839 pub fn deserialize_none_if<'de, D>(
840 deserializer: D,
841 none_if: &[&str],
842 ) -> Result<Option<bool>, D::Error>
843 where
844 D: serde::Deserializer<'de>,
845 {
846 deserializer.deserialize_option(BoolOptionNoneIfVisitor { none_if })
847 }
848
849 struct BoolOptionVisitor;
850
851 impl<'de> Visitor<'de> for BoolOptionVisitor {
852 type Value = Option<bool>;
853
854 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
855 formatter.write_str("an optional boolean string or numeric boolean")
856 }
857
858 fn visit_none<E>(self) -> Result<Self::Value, E> {
859 Ok(None)
860 }
861
862 fn visit_unit<E>(self) -> Result<Self::Value, E> {
863 Ok(None)
864 }
865
866 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
867 where
868 D: serde::Deserializer<'de>,
869 {
870 super::deserialize(deserializer).map(Some)
871 }
872 }
873
874 struct BoolOptionNoneIfVisitor<'a> {
875 none_if: &'a [&'a str],
876 }
877
878 impl<'de> Visitor<'de> for BoolOptionNoneIfVisitor<'_> {
879 type Value = Option<bool>;
880
881 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
882 formatter.write_str("an optional boolean string, numeric boolean, or sentinel")
883 }
884
885 fn visit_none<E>(self) -> Result<Self::Value, E> {
886 Ok(None)
887 }
888
889 fn visit_unit<E>(self) -> Result<Self::Value, E> {
890 Ok(None)
891 }
892
893 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
894 where
895 D: serde::Deserializer<'de>,
896 {
897 deserializer.deserialize_any(super::BoolNoneIfVisitor {
898 none_if: self.none_if,
899 })
900 }
901 }
902 }
903 }
904
905 pub mod as_date {
906 use serde::Deserialize;
907 use serde::de::Error as DeError;
908
909 use crate::Date;
910
911 pub fn serialize<S>(value: &Date, serializer: S) -> Result<S::Ok, S::Error>
912 where
913 S: serde::Serializer,
914 {
915 serializer.serialize_str(&crate::format_date(value))
916 }
917
918 pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
919 where
920 D: serde::Deserializer<'de>,
921 {
922 let value = <String as Deserialize>::deserialize(deserializer)?;
923 crate::parse_date(&value).map_err(DeError::custom)
924 }
925
926 pub fn serialize_none_if<S>(
927 value: &Option<Date>,
928 none_value: &str,
929 serializer: S,
930 ) -> Result<S::Ok, S::Error>
931 where
932 S: serde::Serializer,
933 {
934 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
935 }
936
937 pub fn deserialize_none_if<'de, D>(
938 deserializer: D,
939 none_if: &[&str],
940 ) -> Result<Option<Date>, D::Error>
941 where
942 D: serde::Deserializer<'de>,
943 {
944 super::deserialize_none_if_with(deserializer, none_if, crate::parse_date)
945 }
946
947 pub mod option {
948 use serde::Deserialize;
949 use serde::de::Error as DeError;
950
951 use super::super as serde_string;
952 use crate::Date;
953
954 pub fn serialize<S>(value: &Option<Date>, serializer: S) -> Result<S::Ok, S::Error>
955 where
956 S: serde::Serializer,
957 {
958 match value {
959 Some(value) => super::serialize(value, serializer),
960 None => serializer.serialize_none(),
961 }
962 }
963
964 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Date>, D::Error>
965 where
966 D: serde::Deserializer<'de>,
967 {
968 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
969 value
970 .map(|value| crate::parse_date(&value).map_err(DeError::custom))
971 .transpose()
972 }
973
974 pub fn deserialize_none_if<'de, D>(
975 deserializer: D,
976 none_if: &[&str],
977 ) -> Result<Option<Date>, D::Error>
978 where
979 D: serde::Deserializer<'de>,
980 {
981 serde_string::deserialize_option_none_if_with(
982 deserializer,
983 none_if,
984 crate::parse_date,
985 )
986 }
987 }
988 }
989
990 pub mod as_naive_datetime {
991 use serde::Deserialize;
992 use serde::de::Error as DeError;
993
994 use crate::PrimitiveDateTime;
995
996 pub fn serialize<S>(value: &PrimitiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
997 where
998 S: serde::Serializer,
999 {
1000 serializer.serialize_str(&crate::format_naive_datetime(value))
1001 }
1002
1003 pub fn deserialize<'de, D>(deserializer: D) -> Result<PrimitiveDateTime, D::Error>
1004 where
1005 D: serde::Deserializer<'de>,
1006 {
1007 let value = <String as Deserialize>::deserialize(deserializer)?;
1008 crate::parse_naive_datetime(&value).map_err(DeError::custom)
1009 }
1010
1011 pub fn serialize_none_if<S>(
1012 value: &Option<PrimitiveDateTime>,
1013 none_value: &str,
1014 serializer: S,
1015 ) -> Result<S::Ok, S::Error>
1016 where
1017 S: serde::Serializer,
1018 {
1019 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1020 }
1021
1022 pub fn deserialize_none_if<'de, D>(
1023 deserializer: D,
1024 none_if: &[&str],
1025 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1026 where
1027 D: serde::Deserializer<'de>,
1028 {
1029 super::deserialize_none_if_with(deserializer, none_if, crate::parse_naive_datetime)
1030 }
1031
1032 pub mod option {
1033 use serde::Deserialize;
1034 use serde::de::Error as DeError;
1035
1036 use super::super as serde_string;
1037 use crate::PrimitiveDateTime;
1038
1039 pub fn serialize<S>(
1040 value: &Option<PrimitiveDateTime>,
1041 serializer: S,
1042 ) -> Result<S::Ok, S::Error>
1043 where
1044 S: serde::Serializer,
1045 {
1046 match value {
1047 Some(value) => super::serialize(value, serializer),
1048 None => serializer.serialize_none(),
1049 }
1050 }
1051
1052 pub fn deserialize<'de, D>(
1053 deserializer: D,
1054 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1055 where
1056 D: serde::Deserializer<'de>,
1057 {
1058 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1059 value
1060 .map(|value| crate::parse_naive_datetime(&value).map_err(DeError::custom))
1061 .transpose()
1062 }
1063
1064 pub fn deserialize_none_if<'de, D>(
1065 deserializer: D,
1066 none_if: &[&str],
1067 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1068 where
1069 D: serde::Deserializer<'de>,
1070 {
1071 serde_string::deserialize_option_none_if_with(
1072 deserializer,
1073 none_if,
1074 crate::parse_naive_datetime,
1075 )
1076 }
1077 }
1078 }
1079
1080 pub mod as_offset_datetime {
1081 use serde::Deserialize;
1082 use serde::de::Error as DeError;
1083 use serde::ser::Error as SerError;
1084
1085 use crate::OffsetDateTime;
1086 use time::format_description::well_known::Rfc3339;
1087
1088 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1089 where
1090 S: serde::Serializer,
1091 {
1092 let value = value.format(&Rfc3339).map_err(SerError::custom)?;
1093 serializer.serialize_str(&value)
1094 }
1095
1096 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1097 where
1098 D: serde::Deserializer<'de>,
1099 {
1100 let value = <String as Deserialize>::deserialize(deserializer)?;
1101 OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom)
1102 }
1103
1104 pub fn serialize_none_if<S>(
1105 value: &Option<OffsetDateTime>,
1106 none_value: &str,
1107 serializer: S,
1108 ) -> Result<S::Ok, S::Error>
1109 where
1110 S: serde::Serializer,
1111 {
1112 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1113 }
1114
1115 pub fn deserialize_none_if<'de, D>(
1116 deserializer: D,
1117 none_if: &[&str],
1118 ) -> Result<Option<OffsetDateTime>, D::Error>
1119 where
1120 D: serde::Deserializer<'de>,
1121 {
1122 super::deserialize_none_if_with(deserializer, none_if, |value| {
1123 OffsetDateTime::parse(value, &Rfc3339)
1124 })
1125 }
1126
1127 pub mod option {
1128 use serde::Deserialize;
1129 use serde::de::Error as DeError;
1130
1131 use super::super as serde_string;
1132 use crate::OffsetDateTime;
1133 use time::format_description::well_known::Rfc3339;
1134
1135 pub fn serialize<S>(
1136 value: &Option<OffsetDateTime>,
1137 serializer: S,
1138 ) -> Result<S::Ok, S::Error>
1139 where
1140 S: serde::Serializer,
1141 {
1142 match value {
1143 Some(value) => super::serialize(value, serializer),
1144 None => serializer.serialize_none(),
1145 }
1146 }
1147
1148 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1149 where
1150 D: serde::Deserializer<'de>,
1151 {
1152 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1153 value
1154 .map(|value| OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom))
1155 .transpose()
1156 }
1157
1158 pub fn deserialize_none_if<'de, D>(
1159 deserializer: D,
1160 none_if: &[&str],
1161 ) -> Result<Option<OffsetDateTime>, D::Error>
1162 where
1163 D: serde::Deserializer<'de>,
1164 {
1165 serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1166 OffsetDateTime::parse(value, &Rfc3339)
1167 })
1168 }
1169 }
1170 }
1171
1172 pub mod as_unix_time {
1173 use serde::Deserialize;
1174 use serde::de::Error as DeError;
1175
1176 use crate::OffsetDateTime;
1177
1178 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1179 where
1180 S: serde::Serializer,
1181 {
1182 serializer.serialize_str(&crate::format_unix_time(value))
1183 }
1184
1185 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1186 where
1187 D: serde::Deserializer<'de>,
1188 {
1189 let value = <String as Deserialize>::deserialize(deserializer)?;
1190 let value = value.parse::<i64>().map_err(DeError::custom)?;
1191 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1192 }
1193
1194 pub fn serialize_none_if<S>(
1195 value: &Option<OffsetDateTime>,
1196 none_value: &str,
1197 serializer: S,
1198 ) -> Result<S::Ok, S::Error>
1199 where
1200 S: serde::Serializer,
1201 {
1202 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1203 }
1204
1205 pub fn deserialize_none_if<'de, D>(
1206 deserializer: D,
1207 none_if: &[&str],
1208 ) -> Result<Option<OffsetDateTime>, D::Error>
1209 where
1210 D: serde::Deserializer<'de>,
1211 {
1212 super::deserialize_none_if_with(deserializer, none_if, |value| {
1213 let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1214 OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1215 })
1216 }
1217
1218 pub mod option {
1219 use serde::Deserialize;
1220 use serde::de::Error as DeError;
1221
1222 use super::super as serde_string;
1223 use crate::OffsetDateTime;
1224
1225 pub fn serialize<S>(
1226 value: &Option<OffsetDateTime>,
1227 serializer: S,
1228 ) -> Result<S::Ok, S::Error>
1229 where
1230 S: serde::Serializer,
1231 {
1232 match value {
1233 Some(value) => super::serialize(value, serializer),
1234 None => serializer.serialize_none(),
1235 }
1236 }
1237
1238 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1239 where
1240 D: serde::Deserializer<'de>,
1241 {
1242 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1243 value
1244 .map(|value| {
1245 let value = value.parse::<i64>().map_err(DeError::custom)?;
1246 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1247 })
1248 .transpose()
1249 }
1250
1251 pub fn deserialize_none_if<'de, D>(
1252 deserializer: D,
1253 none_if: &[&str],
1254 ) -> Result<Option<OffsetDateTime>, D::Error>
1255 where
1256 D: serde::Deserializer<'de>,
1257 {
1258 serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1259 let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1260 OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1261 })
1262 }
1263 }
1264 }
1265
1266 pub mod as_time {
1267 use serde::Deserialize;
1268 use serde::de::Error as DeError;
1269
1270 use crate::Time;
1271
1272 pub fn serialize<S>(value: &Time, serializer: S) -> Result<S::Ok, S::Error>
1273 where
1274 S: serde::Serializer,
1275 {
1276 serializer.serialize_str(&crate::format_time(value))
1277 }
1278
1279 pub fn deserialize<'de, D>(deserializer: D) -> Result<Time, D::Error>
1280 where
1281 D: serde::Deserializer<'de>,
1282 {
1283 let value = <String as Deserialize>::deserialize(deserializer)?;
1284 crate::parse_time(&value).map_err(DeError::custom)
1285 }
1286
1287 pub fn serialize_none_if<S>(
1288 value: &Option<Time>,
1289 none_value: &str,
1290 serializer: S,
1291 ) -> Result<S::Ok, S::Error>
1292 where
1293 S: serde::Serializer,
1294 {
1295 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1296 }
1297
1298 pub fn deserialize_none_if<'de, D>(
1299 deserializer: D,
1300 none_if: &[&str],
1301 ) -> Result<Option<Time>, D::Error>
1302 where
1303 D: serde::Deserializer<'de>,
1304 {
1305 super::deserialize_none_if_with(deserializer, none_if, crate::parse_time)
1306 }
1307
1308 pub mod option {
1309 use serde::Deserialize;
1310 use serde::de::Error as DeError;
1311
1312 use crate::Time;
1313
1314 pub fn serialize<S>(value: &Option<Time>, serializer: S) -> Result<S::Ok, S::Error>
1315 where
1316 S: serde::Serializer,
1317 {
1318 match value {
1319 Some(value) => super::serialize(value, serializer),
1320 None => serializer.serialize_none(),
1321 }
1322 }
1323
1324 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Time>, D::Error>
1325 where
1326 D: serde::Deserializer<'de>,
1327 {
1328 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1329 let Some(value) = value else {
1330 return Ok(None);
1331 };
1332 let value = value.trim();
1333 if value.is_empty() {
1334 return Ok(None);
1335 }
1336 crate::parse_time(value).map(Some).map_err(DeError::custom)
1337 }
1338
1339 pub fn deserialize_none_if<'de, D>(
1340 deserializer: D,
1341 none_if: &[&str],
1342 ) -> Result<Option<Time>, D::Error>
1343 where
1344 D: serde::Deserializer<'de>,
1345 {
1346 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1347 let Some(value) = value else {
1348 return Ok(None);
1349 };
1350 if none_if.contains(&value.as_str()) {
1351 return Ok(None);
1352 }
1353 let value = value.trim();
1354 if value.is_empty() {
1355 return Ok(None);
1356 }
1357 crate::parse_time(value).map(Some).map_err(DeError::custom)
1358 }
1359 }
1360 }
1361
1362 fn serialize_display<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
1363 where
1364 T: fmt::Display,
1365 S: serde::Serializer,
1366 {
1367 serializer.serialize_str(&value.to_string())
1368 }
1369
1370 fn serialize_option_display<T, S>(value: Option<&T>, serializer: S) -> Result<S::Ok, S::Error>
1371 where
1372 T: fmt::Display,
1373 S: serde::Serializer,
1374 {
1375 match value {
1376 Some(value) => serialize_display(value, serializer),
1377 None => serializer.serialize_none(),
1378 }
1379 }
1380
1381 fn serialize_option_or_sentinel<T, S>(
1382 value: Option<&T>,
1383 none_value: &str,
1384 serializer: S,
1385 serialize: impl FnOnce(&T, S) -> Result<S::Ok, S::Error>,
1386 ) -> Result<S::Ok, S::Error>
1387 where
1388 S: serde::Serializer,
1389 {
1390 match value {
1391 Some(value) => serialize(value, serializer),
1392 None => serializer.serialize_str(none_value),
1393 }
1394 }
1395
1396 fn deserialize_from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1397 where
1398 T: FromStr,
1399 T::Err: fmt::Display,
1400 D: serde::Deserializer<'de>,
1401 {
1402 let value = String::deserialize(deserializer)?;
1403 value.parse::<T>().map_err(DeError::custom)
1404 }
1405
1406 fn deserialize_option_from_str<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
1407 where
1408 T: FromStr,
1409 T::Err: fmt::Display,
1410 D: serde::Deserializer<'de>,
1411 {
1412 let value = Option::<String>::deserialize(deserializer)?;
1413 value
1414 .map(|value| value.parse::<T>().map_err(DeError::custom))
1415 .transpose()
1416 }
1417
1418 fn deserialize_none_if_with<'de, T, D, E>(
1419 deserializer: D,
1420 none_if: &[&str],
1421 parse: impl FnOnce(&str) -> Result<T, E>,
1422 ) -> Result<Option<T>, D::Error>
1423 where
1424 D: serde::Deserializer<'de>,
1425 E: fmt::Display,
1426 {
1427 let value = String::deserialize(deserializer)?;
1428 parse_none_if_with(&value, none_if, parse)
1429 }
1430
1431 fn deserialize_option_none_if_with<'de, T, D, E>(
1432 deserializer: D,
1433 none_if: &[&str],
1434 parse: impl FnOnce(&str) -> Result<T, E>,
1435 ) -> Result<Option<T>, D::Error>
1436 where
1437 D: serde::Deserializer<'de>,
1438 E: fmt::Display,
1439 {
1440 let Some(value) = Option::<String>::deserialize(deserializer)? else {
1441 return Ok(None);
1442 };
1443 parse_none_if_with(&value, none_if, parse)
1444 }
1445
1446 fn parse_none_if_with<T, E, D>(
1447 value: &str,
1448 none_if: &[&str],
1449 parse: impl FnOnce(&str) -> Result<T, E>,
1450 ) -> Result<Option<T>, D>
1451 where
1452 E: fmt::Display,
1453 D: DeError,
1454 {
1455 if none_if.contains(&value) {
1456 return Ok(None);
1457 }
1458 parse(value).map(Some).map_err(DeError::custom)
1459 }
1460
1461 fn deserialize_bool(value: &str) -> Result<bool, &'static str> {
1462 match value {
1463 "1" => Ok(true),
1464 "0" => Ok(false),
1465 value if value.eq_ignore_ascii_case("true") => Ok(true),
1466 value if value.eq_ignore_ascii_case("false") => Ok(false),
1467 _ => Err("invalid boolean string"),
1468 }
1469 }
1470}
1471
1472#[cfg(feature = "serde")]
1473#[allow(clippy::missing_errors_doc)]
1474pub mod serde_integer {
1475 pub mod as_unix_time {
1476 use serde::Deserialize;
1477 use serde::de::Error as DeError;
1478
1479 use crate::OffsetDateTime;
1480
1481 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1482 where
1483 S: serde::Serializer,
1484 {
1485 serializer.serialize_i64(value.unix_timestamp())
1486 }
1487
1488 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1489 where
1490 D: serde::Deserializer<'de>,
1491 {
1492 let value = <i64 as Deserialize>::deserialize(deserializer)?;
1493 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1494 }
1495
1496 pub mod option {
1497 use serde::Deserialize;
1498 use serde::de::Error as DeError;
1499
1500 use crate::OffsetDateTime;
1501
1502 pub fn serialize<S>(
1503 value: &Option<OffsetDateTime>,
1504 serializer: S,
1505 ) -> Result<S::Ok, S::Error>
1506 where
1507 S: serde::Serializer,
1508 {
1509 match value {
1510 Some(value) => super::serialize(value, serializer),
1511 None => serializer.serialize_none(),
1512 }
1513 }
1514
1515 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1516 where
1517 D: serde::Deserializer<'de>,
1518 {
1519 let value = <Option<i64> as Deserialize>::deserialize(deserializer)?;
1520 value
1521 .map(|value| {
1522 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1523 })
1524 .transpose()
1525 }
1526 }
1527 }
1528
1529 pub mod as_bool {
1530 use crate::serde_string::as_bool as string_bool;
1531
1532 pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
1533 where
1534 S: serde::Serializer,
1535 {
1536 serializer.serialize_u8(u8::from(*value))
1537 }
1538
1539 pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
1540 where
1541 D: serde::Deserializer<'de>,
1542 {
1543 string_bool::deserialize(deserializer)
1544 }
1545
1546 pub mod option {
1547 use crate::serde_string::as_bool::option as string_bool_option;
1548
1549 pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
1550 where
1551 S: serde::Serializer,
1552 {
1553 match value {
1554 Some(value) => super::serialize(value, serializer),
1555 None => serializer.serialize_none(),
1556 }
1557 }
1558
1559 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
1560 where
1561 D: serde::Deserializer<'de>,
1562 {
1563 string_bool_option::deserialize(deserializer)
1564 }
1565 }
1566 }
1567}
1568
1569#[cfg(feature = "json")]
1570pub mod treat_error_as_none {
1571 use serde::de::DeserializeOwned;
1572 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1573
1574 pub fn serialize<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
1580 where
1581 S: Serializer,
1582 T: Serialize,
1583 {
1584 match value {
1585 Some(inner) => inner.serialize(serializer),
1586 None => serializer.serialize_none(),
1587 }
1588 }
1589
1590 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
1596 where
1597 D: Deserializer<'de>,
1598 T: DeserializeOwned,
1599 {
1600 let value = serde_json::Value::deserialize(deserializer)?;
1601 match T::deserialize(value) {
1602 Ok(parsed) => Ok(Some(parsed)),
1603 Err(_) => Ok(None),
1604 }
1605 }
1606}
1607
1608pub fn insert_header(
1614 headers: &mut http::HeaderMap,
1615 name: &'static str,
1616 value: &str,
1617) -> Result<(), Error> {
1618 headers.insert(
1619 HeaderName::from_bytes(name.as_bytes())?,
1620 HeaderValue::from_str(value)?,
1621 );
1622 Ok(())
1623}
1624
1625#[must_use]
1626pub fn has_json_content_type(headers: &http::HeaderMap) -> bool {
1627 headers
1628 .get(CONTENT_TYPE)
1629 .and_then(|value| value.to_str().ok())
1630 .is_some_and(is_json_media_type)
1631}
1632
1633fn is_json_media_type(value: &str) -> bool {
1634 let media_type = value.split(';').next().unwrap_or(value).trim();
1635 if media_type.eq_ignore_ascii_case("application/json") {
1636 return true;
1637 }
1638
1639 let Some((_, subtype)) = media_type.rsplit_once('/') else {
1640 return false;
1641 };
1642 ends_with_ignore_ascii_case(subtype, "+json")
1643}
1644
1645fn ends_with_ignore_ascii_case(value: &str, suffix: &str) -> bool {
1646 let value = value.as_bytes();
1647 let suffix = suffix.as_bytes();
1648 value.len() >= suffix.len() && value[value.len() - suffix.len()..].eq_ignore_ascii_case(suffix)
1649}
1650
1651fn append_percent_encoded(out: &mut String, bytes: &[u8]) {
1652 const HEX: &[u8; 16] = b"0123456789ABCDEF";
1653
1654 for &byte in bytes {
1655 if is_unreserved(byte) {
1656 out.push(byte as char);
1657 } else {
1658 out.push('%');
1659 out.push(HEX[(byte >> 4) as usize] as char);
1660 out.push(HEX[(byte & 0x0f) as usize] as char);
1661 }
1662 }
1663}
1664
1665const fn is_unreserved(byte: u8) -> bool {
1666 matches!(
1667 byte,
1668 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~'
1669 )
1670}
1671
1672#[cfg(test)]
1673mod tests {
1674 use super::*;
1675
1676 #[cfg(all(feature = "serde", feature = "json"))]
1677 mod required_f64_none_if {
1678 use crate::serde_string::as_f64;
1679
1680 #[allow(clippy::ref_option)]
1681 pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
1682 where
1683 S: serde::Serializer,
1684 {
1685 as_f64::serialize_none_if(value, "NA", serializer)
1686 }
1687
1688 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
1689 where
1690 D: serde::Deserializer<'de>,
1691 {
1692 as_f64::deserialize_none_if(deserializer, &["NA", "-"])
1693 }
1694 }
1695
1696 #[cfg(all(feature = "serde", feature = "json"))]
1697 mod optional_f64_none_if {
1698 use crate::serde_string::as_f64::{self, option};
1699
1700 #[allow(clippy::ref_option)]
1701 pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
1702 where
1703 S: serde::Serializer,
1704 {
1705 as_f64::serialize_none_if(value, "NA", serializer)
1706 }
1707
1708 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
1709 where
1710 D: serde::Deserializer<'de>,
1711 {
1712 option::deserialize_none_if(deserializer, &["NA", "-"])
1713 }
1714 }
1715
1716 #[test]
1717 fn encodes_path_segments() {
1718 let mut out = String::new();
1719 append_path_segment(&mut out, "a/b c");
1720 assert_eq!(out, "a%2Fb%20c");
1721 }
1722
1723 #[test]
1724 fn appends_query_pairs() {
1725 let mut out = String::from("/pets");
1726 let mut first = true;
1727 append_query_pair(&mut out, &mut first, "tag name", "small/dog");
1728 append_query_pair(&mut out, &mut first, "limit", "10");
1729 assert_eq!(out, "/pets?tag%20name=small%2Fdog&limit=10");
1730 }
1731
1732 #[test]
1733 fn parses_range_strings() {
1734 assert_eq!(parse_range::<u8>("14-17").unwrap(), (Some(14), Some(17)));
1735 assert_eq!(parse_range::<u8>("14-").unwrap(), (Some(14), None));
1736 assert_eq!(parse_range::<u8>("-17").unwrap(), (None, Some(17)));
1737 assert_eq!(parse_range::<u8>("").unwrap(), (None, None));
1738 assert!(matches!(
1739 parse_range::<u8>("14-17-20"),
1740 Err(ParseRangeError::TooManySeparators)
1741 ));
1742 }
1743
1744 #[test]
1745 fn formats_range_strings() {
1746 assert_eq!(format_range(&Some(14), &Some(17)), "14-17");
1747 assert_eq!(format_range(&Some(14), &None::<u8>), "14-");
1748 assert_eq!(format_range(&None::<u8>, &Some(17)), "-17");
1749 assert_eq!(format_range(&None::<u8>, &None::<u8>), "");
1750 }
1751
1752 #[test]
1753 fn parses_and_formats_time_strings() {
1754 let time = parse_time("0620").unwrap();
1755 assert_eq!(time.hour(), 6);
1756 assert_eq!(time.minute(), 20);
1757 assert_eq!(format_time(&time), "0620");
1758 assert_eq!(parse_time("6:20"), Err(ParseTimeError::InvalidFormat));
1759 assert_eq!(parse_time("2400"), Err(ParseTimeError::ComponentRange));
1760 }
1761
1762 #[test]
1763 fn parses_and_formats_date_strings() {
1764 let date = parse_date("2024-07-16").unwrap();
1765 assert_eq!(
1766 date,
1767 Date::from_calendar_date(2024, Month::July, 16).unwrap()
1768 );
1769 assert_eq!(format_date(&date), "2024-07-16");
1770 assert_eq!(parse_date("2024-7-16"), Err(ParseDateError::InvalidFormat));
1771 assert_eq!(parse_date("not-a-date"), Err(ParseDateError::InvalidFormat));
1772 }
1773
1774 #[test]
1775 fn parses_and_formats_naive_datetime_strings() {
1776 let datetime = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
1777 assert_eq!(datetime.hour(), 23);
1778 assert_eq!(datetime.minute(), 59);
1779 assert_eq!(datetime.second(), 0);
1780 assert_eq!(format_naive_datetime(&datetime), "2024-07-16T23:59:00");
1781 assert_eq!(
1782 parse_naive_datetime("2024-07-16T23:59"),
1783 Err(ParseNaiveDateTimeError::InvalidFormat)
1784 );
1785 assert_eq!(
1786 parse_naive_datetime("2024-07-16 23:59:00"),
1787 Err(ParseNaiveDateTimeError::InvalidFormat)
1788 );
1789 }
1790
1791 #[test]
1792 fn formats_unix_time_seconds() {
1793 let datetime = OffsetDateTime::from_unix_timestamp(-1).unwrap();
1794 assert_eq!(format_unix_time(&datetime), "-1");
1795 }
1796
1797 #[cfg(all(feature = "serde", feature = "json"))]
1798 #[test]
1799 fn serde_string_naive_datetime_round_trips() {
1800 #[derive(serde::Deserialize, serde::Serialize)]
1801 struct Value {
1802 #[serde(with = "crate::serde_string::as_naive_datetime")]
1803 at: PrimitiveDateTime,
1804 }
1805
1806 let at = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
1807 let encoded = serde_json::to_value(Value { at }).unwrap();
1808 assert_eq!(encoded, serde_json::json!({ "at": "2024-07-16T23:59:00" }));
1809
1810 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
1811 assert_eq!(decoded.at, at);
1812 }
1813
1814 #[cfg(all(feature = "serde", feature = "json"))]
1815 #[test]
1816 fn serde_string_date_round_trips() {
1817 #[derive(serde::Deserialize, serde::Serialize)]
1818 struct Value {
1819 #[serde(with = "crate::serde_string::as_date")]
1820 day: Date,
1821 }
1822
1823 let date = Date::from_calendar_date(2024, Month::July, 16).unwrap();
1824 let encoded = serde_json::to_value(Value { day: date }).unwrap();
1825 assert_eq!(encoded, serde_json::json!({ "day": "2024-07-16" }));
1826
1827 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
1828 assert_eq!(decoded.day, date);
1829 }
1830
1831 #[test]
1832 fn recognizes_json_content_types() {
1833 let mut headers = http::HeaderMap::new();
1834 headers.insert(
1835 CONTENT_TYPE,
1836 http::HeaderValue::from_static("application/problem+json; charset=utf-8"),
1837 );
1838 assert!(has_json_content_type(&headers));
1839 }
1840
1841 #[test]
1842 fn response_parts_holds_status_headers_body() {
1843 let mut headers = http::HeaderMap::new();
1844 headers.insert(
1845 CONTENT_TYPE,
1846 http::HeaderValue::from_static("application/json"),
1847 );
1848 let body = br#"{"ok":true}"#.to_vec();
1849 let parts = ResponseParts {
1850 status: http::StatusCode::OK,
1851 headers,
1852 body,
1853 };
1854 assert_eq!(parts.status, http::StatusCode::OK);
1855 assert_eq!(parts.headers.get(CONTENT_TYPE).unwrap(), "application/json");
1856 assert_eq!(parts.body, br#"{"ok":true}"#);
1857 }
1858
1859 #[cfg(all(feature = "serde", feature = "json"))]
1860 #[test]
1861 fn serde_string_bool_accepts_string_and_numeric_values() {
1862 #[derive(serde::Deserialize, serde::Serialize)]
1863 struct Value {
1864 #[serde(with = "crate::serde_string::as_bool")]
1865 monitored: bool,
1866 }
1867
1868 let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
1869 assert!(!numeric.monitored);
1870
1871 let string = serde_json::from_str::<Value>(r#"{"monitored":"1"}"#).unwrap();
1872 assert!(string.monitored);
1873
1874 let encoded = serde_json::to_value(Value { monitored: false }).unwrap();
1875 assert_eq!(encoded, serde_json::json!({ "monitored": "0" }));
1876 }
1877
1878 #[cfg(all(feature = "serde", feature = "json"))]
1879 #[test]
1880 fn serde_string_none_if_is_strict_and_canonical() {
1881 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1882 struct Value {
1883 #[serde(with = "required_f64_none_if")]
1884 required: Option<f64>,
1885 #[serde(default, with = "optional_f64_none_if")]
1886 optional: Option<f64>,
1887 }
1888
1889 let valid =
1890 serde_json::from_str::<Value>(r#"{"required":"28.7","optional":"10.5"}"#).unwrap();
1891 assert_eq!(valid.required, Some(28.7));
1892 assert_eq!(valid.optional, Some(10.5));
1893
1894 let sentinel =
1895 serde_json::from_str::<Value>(r#"{"required":"-","optional":"NA"}"#).unwrap();
1896 assert_eq!(sentinel.required, None);
1897 assert_eq!(sentinel.optional, None);
1898
1899 let null_optional =
1900 serde_json::from_str::<Value>(r#"{"required":"28.7","optional":null}"#).unwrap();
1901 assert_eq!(null_optional.optional, None);
1902 assert!(serde_json::from_str::<Value>(r#"{"required":null}"#).is_err());
1903 assert!(serde_json::from_str::<Value>(r#"{"required":"unexpected"}"#).is_err());
1904
1905 let encoded = serde_json::to_value(Value {
1906 required: None,
1907 optional: Some(10.5),
1908 })
1909 .unwrap();
1910 assert_eq!(
1911 encoded,
1912 serde_json::json!({"required": "NA", "optional": "10.5"})
1913 );
1914 }
1915
1916 #[cfg(all(feature = "serde", feature = "json"))]
1917 #[test]
1918 fn serde_string_none_if_preserves_bool_and_time_parsers() {
1919 use crate::serde_string::{as_bool, as_time::option as time_option};
1920 use serde_json::Value;
1921
1922 let bool_sentinel =
1923 as_bool::deserialize_none_if(Value::String("NA".to_owned()), &["NA"]).unwrap();
1924 assert_eq!(bool_sentinel, None);
1925
1926 let bool_numeric = as_bool::deserialize_none_if(Value::from(1), &["NA"]).unwrap();
1927 assert_eq!(bool_numeric, Some(true));
1928
1929 let empty_time =
1930 time_option::deserialize_none_if(Value::String(" ".to_owned()), &["-"]).unwrap();
1931 assert_eq!(empty_time, None);
1932 assert!(
1933 time_option::deserialize_none_if(Value::String("invalid".to_owned()), &["-"]).is_err()
1934 );
1935 }
1936
1937 #[cfg(all(feature = "serde", feature = "json"))]
1938 #[test]
1939 fn serde_integer_bool_accepts_numeric_values() {
1940 #[derive(serde::Deserialize, serde::Serialize)]
1941 struct Value {
1942 #[serde(with = "crate::serde_integer::as_bool")]
1943 monitored: bool,
1944 }
1945
1946 let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
1947 assert!(!numeric.monitored);
1948
1949 let encoded = serde_json::to_value(Value { monitored: true }).unwrap();
1950 assert_eq!(encoded, serde_json::json!({ "monitored": 1 }));
1951 }
1952
1953 #[cfg(all(feature = "serde", feature = "json"))]
1954 #[test]
1955 fn serde_string_unix_time_round_trips() {
1956 #[derive(serde::Deserialize, serde::Serialize)]
1957 struct Value {
1958 #[serde(with = "crate::serde_string::as_unix_time")]
1959 at: OffsetDateTime,
1960 }
1961
1962 let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
1963 let encoded = serde_json::to_value(Value { at }).unwrap();
1964 assert_eq!(encoded, serde_json::json!({ "at": "1719892800" }));
1965
1966 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
1967 assert_eq!(decoded.at, at);
1968 }
1969
1970 #[cfg(all(feature = "serde", feature = "json"))]
1971 #[test]
1972 fn serde_integer_unix_time_round_trips_and_handles_null() {
1973 #[derive(serde::Deserialize, serde::Serialize)]
1974 struct Value {
1975 #[serde(with = "crate::serde_integer::as_unix_time")]
1976 at: OffsetDateTime,
1977 #[serde(with = "crate::serde_integer::as_unix_time::option")]
1978 maybe_at: Option<OffsetDateTime>,
1979 }
1980
1981 let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
1982 let encoded = serde_json::to_value(Value { at, maybe_at: None }).unwrap();
1983 assert_eq!(
1984 encoded,
1985 serde_json::json!({ "at": 1_719_892_800, "maybe_at": null })
1986 );
1987
1988 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
1989 assert_eq!(decoded.at, at);
1990 assert_eq!(decoded.maybe_at, None);
1991 }
1992
1993 #[cfg(all(feature = "serde", feature = "json"))]
1994 #[test]
1995 fn serde_integer_unix_time_rejects_out_of_range_values() {
1996 #[derive(serde::Deserialize)]
1997 #[allow(dead_code)]
1998 struct Value {
1999 #[serde(with = "crate::serde_integer::as_unix_time")]
2000 at: OffsetDateTime,
2001 }
2002
2003 assert!(serde_json::from_str::<Value>(r#"{"at":9223372036854775807}"#).is_err());
2004 }
2005}