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
21pub 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 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
55pub 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 pub fn new(parts: ResponseParts<B>) -> Self {
72 Self {
73 parts,
74 action: marker::PhantomData,
75 }
76 }
77
78 pub fn parts(&self) -> &ResponseParts<B> {
80 &self.parts
81 }
82
83 pub fn into_parts(self) -> ResponseParts<B> {
85 self.parts
86 }
87}
88
89impl<A: Action, B: AsRef<[u8]>> BufferedResponse<A, B> {
90 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 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 type RequestBody;
176 type Response<'de>;
178
179 fn request(self) -> Result<http::Request<Self::RequestBody>, Error>;
185
186 fn decode(response: ResponseParts<&[u8]>) -> Result<Self::Response<'_>, Error>;
192}
193
194pub trait OwnedAction: Action {
199 type OwnedResponse;
201
202 fn decode_owned(response: ResponseParts<&[u8]>) -> Result<Self::OwnedResponse, Error>;
207}
208
209#[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#[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#[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#[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#[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#[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
434pub 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
495pub 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
533pub 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
560pub 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 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 #[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 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 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
2258pub 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}