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
234#[cfg(feature = "json")]
246#[instrument(skip_all)]
247pub fn from_projected_json_slice<T>(
248 body: &[u8],
249 unwrap_field: &str,
250 map_field: Option<&str>,
251) -> Result<T, Error>
252where
253 T: de::DeserializeOwned,
254{
255 debug!(
256 unwrap_field,
257 map_field, "deserializing projected JSON response"
258 );
259 let mut value = serde_json::from_slice::<JsonValue>(body)?;
260 let object = value.as_object_mut().ok_or(Error::InvalidResponse(
261 "response projection expected a top-level JSON object",
262 ))?;
263 let mut projected = object.remove(unwrap_field).unwrap_or(JsonValue::Null);
264
265 if let Some(map_field) = map_field {
266 projected = match projected {
267 JsonValue::Null => JsonValue::Null,
268 JsonValue::Array(items) => JsonValue::Array(
269 items
270 .into_iter()
271 .map(|mut item| {
272 let object = item.as_object_mut().ok_or(Error::InvalidResponse(
273 "response projection expected array items to be JSON objects",
274 ))?;
275 Ok(object.remove(map_field).unwrap_or(JsonValue::Null))
276 })
277 .collect::<Result<Vec<_>, Error>>()?,
278 ),
279 _ => {
280 return Err(Error::InvalidResponse(
281 "response projection expected the unwrapped field to be a JSON array",
282 ));
283 }
284 };
285 }
286
287 Ok(serde_json::from_value(projected)?)
288}
289
290pub fn append_path_segment(out: &mut String, value: &str) {
291 append_percent_encoded(out, value.as_bytes());
292}
293
294pub fn append_query_pair(out: &mut String, first: &mut bool, key: &str, value: &str) {
295 if *first {
296 out.push('?');
297 *first = false;
298 } else {
299 out.push('&');
300 }
301 append_percent_encoded(out, key.as_bytes());
302 out.push('=');
303 append_percent_encoded(out, value.as_bytes());
304}
305
306#[must_use]
307pub fn format_offset_datetime(value: &OffsetDateTime) -> String {
308 value.format(&Rfc3339).unwrap_or_else(|_| value.to_string())
309}
310
311#[must_use]
312pub fn format_unix_time(value: &OffsetDateTime) -> String {
313 value.unix_timestamp().to_string()
314}
315
316#[must_use]
317pub fn format_date(value: &Date) -> String {
318 format!(
319 "{:04}-{:02}-{:02}",
320 value.year(),
321 u8::from(value.month()),
322 value.day()
323 )
324}
325
326pub fn parse_date(value: &str) -> Result<Date, ParseDateError> {
332 let value = value.trim().as_bytes();
333 if value.len() != 10 || value[4] != b'-' || value[7] != b'-' {
334 return Err(ParseDateError::InvalidFormat);
335 }
336
337 for (index, byte) in value.iter().enumerate() {
338 if matches!(index, 4 | 7) {
339 if *byte != b'-' {
340 return Err(ParseDateError::InvalidFormat);
341 }
342 } else if !byte.is_ascii_digit() {
343 return Err(ParseDateError::InvalidFormat);
344 }
345 }
346
347 let year = parse_date_year(&value[0..4])?;
348 let month = parse_date_u8(&value[5..7])?;
349 let day = parse_date_u8(&value[8..10])?;
350 let month = Month::try_from(month).map_err(|_| ParseDateError::InvalidFormat)?;
351 Date::from_calendar_date(year, month, day).map_err(|_| ParseDateError::InvalidFormat)
352}
353
354fn parse_date_year(bytes: &[u8]) -> Result<i32, ParseDateError> {
355 let mut value = 0i32;
356 for byte in bytes {
357 value = value
358 .checked_mul(10)
359 .and_then(|value| value.checked_add(i32::from(*byte - b'0')))
360 .ok_or(ParseDateError::InvalidFormat)?;
361 }
362 Ok(value)
363}
364
365fn parse_date_u8(bytes: &[u8]) -> Result<u8, ParseDateError> {
366 let mut value = 0u16;
367 for byte in bytes {
368 value = value
369 .checked_mul(10)
370 .and_then(|value| value.checked_add(u16::from(*byte - b'0')))
371 .ok_or(ParseDateError::InvalidFormat)?;
372 }
373 u8::try_from(value).map_err(|_| ParseDateError::InvalidFormat)
374}
375
376#[must_use]
377pub fn format_naive_datetime(value: &PrimitiveDateTime) -> String {
378 format!(
379 "{}T{:02}:{:02}:{:02}",
380 format_date(&value.date()),
381 value.hour(),
382 value.minute(),
383 value.second()
384 )
385}
386
387pub fn parse_naive_datetime(value: &str) -> Result<PrimitiveDateTime, ParseNaiveDateTimeError> {
393 let value = value.trim();
394 let bytes = value.as_bytes();
395 if bytes.len() != 19
396 || bytes[4] != b'-'
397 || bytes[7] != b'-'
398 || bytes[10] != b'T'
399 || bytes[13] != b':'
400 || bytes[16] != b':'
401 {
402 return Err(ParseNaiveDateTimeError::InvalidFormat);
403 }
404
405 for (index, byte) in bytes.iter().enumerate() {
406 if matches!(index, 4 | 7 | 10 | 13 | 16) {
407 continue;
408 }
409 if !byte.is_ascii_digit() {
410 return Err(ParseNaiveDateTimeError::InvalidFormat);
411 }
412 }
413
414 let date = parse_date(&value[0..10]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
415 let hour = parse_date_u8(&bytes[11..13]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
416 let minute =
417 parse_date_u8(&bytes[14..16]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
418 let second =
419 parse_date_u8(&bytes[17..19]).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
420 let time =
421 Time::from_hms(hour, minute, second).map_err(|_| ParseNaiveDateTimeError::InvalidFormat)?;
422 Ok(PrimitiveDateTime::new(date, time))
423}
424
425pub fn parse_time(value: &str) -> Result<Time, ParseTimeError> {
431 let value = value.trim();
432 let bytes = value.as_bytes();
433 if bytes.len() != 4 || !bytes.iter().all(u8::is_ascii_digit) {
434 return Err(ParseTimeError::InvalidFormat);
435 }
436
437 let hour = (bytes[0] - b'0') * 10 + (bytes[1] - b'0');
438 let minute = (bytes[2] - b'0') * 10 + (bytes[3] - b'0');
439 Time::from_hms(hour, minute, 0).map_err(|_| ParseTimeError::ComponentRange)
440}
441
442#[must_use]
443pub fn format_time(value: &Time) -> String {
444 format!("{:02}{:02}", value.hour(), value.minute())
445}
446
447#[must_use]
448pub fn format_bool(value: &bool) -> &'static str {
449 if *value { "1" } else { "0" }
450}
451
452pub fn parse_range<T>(value: &str) -> Result<(Option<T>, Option<T>), ParseRangeError>
458where
459 T: FromStr,
460 T::Err: fmt::Display,
461{
462 let value = value.trim();
463 if value.is_empty() {
464 return Ok((None, None));
465 }
466
467 let (min, max) = match value.split_once('-') {
468 Some((min, max)) => {
469 if max.contains('-') {
470 return Err(ParseRangeError::TooManySeparators);
471 }
472 (min, max)
473 }
474 None => (value, value),
475 };
476
477 Ok((parse_range_min(min)?, parse_range_max(max)?))
478}
479
480#[must_use]
481pub fn format_range<T>(min: &Option<T>, max: &Option<T>) -> String
482where
483 T: fmt::Display,
484{
485 match (min, max) {
486 (Some(min), Some(max)) => format!("{min}-{max}"),
487 (Some(min), None) => format!("{min}-"),
488 (None, Some(max)) => format!("-{max}"),
489 (None, None) => String::new(),
490 }
491}
492
493fn parse_range_min<T>(value: &str) -> Result<Option<T>, ParseRangeError>
494where
495 T: FromStr,
496 T::Err: fmt::Display,
497{
498 parse_range_bound(value, |value, message| ParseRangeError::InvalidMinimum {
499 value,
500 message,
501 })
502}
503
504fn parse_range_max<T>(value: &str) -> Result<Option<T>, ParseRangeError>
505where
506 T: FromStr,
507 T::Err: fmt::Display,
508{
509 parse_range_bound(value, |value, message| ParseRangeError::InvalidMaximum {
510 value,
511 message,
512 })
513}
514
515fn parse_range_bound<T>(
516 value: &str,
517 invalid: impl FnOnce(String, String) -> ParseRangeError,
518) -> Result<Option<T>, ParseRangeError>
519where
520 T: FromStr,
521 T::Err: fmt::Display,
522{
523 let value = value.trim();
524 if value.is_empty() {
525 return Ok(None);
526 }
527
528 value
529 .parse::<T>()
530 .map(Some)
531 .map_err(|err| invalid(value.to_owned(), err.to_string()))
532}
533
534#[cfg(feature = "serde")]
535#[allow(clippy::missing_errors_doc)]
536pub mod serde_string {
537 use std::fmt;
538 use std::str::FromStr;
539
540 use serde::Deserialize;
541 use serde::de::Error as DeError;
542 macro_rules! string_from_str_module {
543 ($module:ident, $ty:ty) => {
544 pub mod $module {
545 pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
546 where
547 S: serde::Serializer,
548 {
549 super::serialize_display(value, serializer)
550 }
551
552 pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
553 where
554 D: serde::Deserializer<'de>,
555 {
556 super::deserialize_from_str(deserializer)
557 }
558
559 pub fn serialize_none_if<S>(
560 value: &Option<$ty>,
561 none_value: &str,
562 serializer: S,
563 ) -> Result<S::Ok, S::Error>
564 where
565 S: serde::Serializer,
566 {
567 super::serialize_option_or_sentinel(
568 value.as_ref(),
569 none_value,
570 serializer,
571 serialize,
572 )
573 }
574
575 pub fn deserialize_none_if<'de, D>(
576 deserializer: D,
577 none_if: &[&str],
578 ) -> Result<Option<$ty>, D::Error>
579 where
580 D: serde::Deserializer<'de>,
581 {
582 super::deserialize_none_if_with(deserializer, none_if, str::parse::<$ty>)
583 }
584
585 pub mod option {
586 pub fn serialize<S>(
587 value: &Option<$ty>,
588 serializer: S,
589 ) -> Result<S::Ok, S::Error>
590 where
591 S: serde::Serializer,
592 {
593 super::super::serialize_option_display(value.as_ref(), serializer)
594 }
595
596 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
597 where
598 D: serde::Deserializer<'de>,
599 {
600 super::super::deserialize_option_from_str(deserializer)
601 }
602
603 pub fn deserialize_none_if<'de, D>(
604 deserializer: D,
605 none_if: &[&str],
606 ) -> Result<Option<$ty>, D::Error>
607 where
608 D: serde::Deserializer<'de>,
609 {
610 super::super::deserialize_option_none_if_with(
611 deserializer,
612 none_if,
613 str::parse::<$ty>,
614 )
615 }
616 }
617 }
618 };
619 }
620
621 macro_rules! string_float_module {
622 ($module:ident, $ty:ty) => {
623 pub mod $module {
624 use serde::Deserialize;
625 use serde::de::Error as DeError;
626
627 pub fn serialize<S>(value: &$ty, serializer: S) -> Result<S::Ok, S::Error>
628 where
629 S: serde::Serializer,
630 {
631 super::serialize_display(value, serializer)
632 }
633
634 pub fn deserialize<'de, D>(deserializer: D) -> Result<$ty, D::Error>
635 where
636 D: serde::Deserializer<'de>,
637 {
638 let value = <String as Deserialize>::deserialize(deserializer)?;
639 fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
640 }
641
642 pub fn serialize_none_if<S>(
643 value: &Option<$ty>,
644 none_value: &str,
645 serializer: S,
646 ) -> Result<S::Ok, S::Error>
647 where
648 S: serde::Serializer,
649 {
650 super::serialize_option_or_sentinel(
651 value.as_ref(),
652 none_value,
653 serializer,
654 serialize,
655 )
656 }
657
658 pub fn deserialize_none_if<'de, D>(
659 deserializer: D,
660 none_if: &[&str],
661 ) -> Result<Option<$ty>, D::Error>
662 where
663 D: serde::Deserializer<'de>,
664 {
665 super::deserialize_none_if_with(deserializer, none_if, |value| {
666 fast_float2::parse::<$ty, _>(value)
667 })
668 }
669
670 pub mod option {
671 use serde::Deserialize;
672 use serde::de::Error as DeError;
673
674 pub fn serialize<S>(
675 value: &Option<$ty>,
676 serializer: S,
677 ) -> Result<S::Ok, S::Error>
678 where
679 S: serde::Serializer,
680 {
681 super::super::serialize_option_display(value.as_ref(), serializer)
682 }
683
684 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$ty>, D::Error>
685 where
686 D: serde::Deserializer<'de>,
687 {
688 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
689 value
690 .map(|value| {
691 fast_float2::parse::<$ty, _>(&value).map_err(DeError::custom)
692 })
693 .transpose()
694 }
695
696 pub fn deserialize_none_if<'de, D>(
697 deserializer: D,
698 none_if: &[&str],
699 ) -> Result<Option<$ty>, D::Error>
700 where
701 D: serde::Deserializer<'de>,
702 {
703 super::super::deserialize_option_none_if_with(
704 deserializer,
705 none_if,
706 |value| fast_float2::parse::<$ty, _>(value),
707 )
708 }
709 }
710 }
711 };
712 }
713
714 string_from_str_module!(as_u8, u8);
715 string_from_str_module!(as_u16, u16);
716 string_from_str_module!(as_u32, u32);
717 string_from_str_module!(as_u64, u64);
718 string_from_str_module!(as_i8, i8);
719 string_from_str_module!(as_i16, i16);
720 string_from_str_module!(as_i32, i32);
721 string_from_str_module!(as_i64, i64);
722 string_float_module!(as_f32, f32);
723 string_float_module!(as_f64, f64);
724
725 pub mod as_bool {
726 use std::fmt;
727
728 use serde::de::{Error as DeError, Visitor};
729
730 pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
731 where
732 S: serde::Serializer,
733 {
734 serializer.serialize_str(crate::format_bool(value))
735 }
736
737 pub fn serialize_mapped<S>(
738 value: &bool,
739 true_value: &str,
740 false_value: &str,
741 serializer: S,
742 ) -> Result<S::Ok, S::Error>
743 where
744 S: serde::Serializer,
745 {
746 serializer.serialize_str(if *value { true_value } else { false_value })
747 }
748
749 pub fn serialize_mapped_none_if<S>(
750 value: &Option<bool>,
751 true_value: &str,
752 false_value: &str,
753 none_value: &str,
754 serializer: S,
755 ) -> Result<S::Ok, S::Error>
756 where
757 S: serde::Serializer,
758 {
759 super::serialize_option_or_sentinel(
760 value.as_ref(),
761 none_value,
762 serializer,
763 |value, serializer| serialize_mapped(value, true_value, false_value, serializer),
764 )
765 }
766
767 pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
768 where
769 D: serde::Deserializer<'de>,
770 {
771 deserializer.deserialize_any(BoolVisitor)
772 }
773
774 pub fn deserialize_mapped<'de, D>(
775 deserializer: D,
776 true_values: &[&str],
777 false_values: &[&str],
778 unknown_as: Option<bool>,
779 ) -> Result<bool, D::Error>
780 where
781 D: serde::Deserializer<'de>,
782 {
783 deserializer.deserialize_any(MappedBoolVisitor {
784 true_values,
785 false_values,
786 unknown_as,
787 })
788 }
789
790 pub fn serialize_none_if<S>(
791 value: &Option<bool>,
792 none_value: &str,
793 serializer: S,
794 ) -> Result<S::Ok, S::Error>
795 where
796 S: serde::Serializer,
797 {
798 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
799 }
800
801 pub fn deserialize_none_if<'de, D>(
802 deserializer: D,
803 none_if: &[&str],
804 ) -> Result<Option<bool>, D::Error>
805 where
806 D: serde::Deserializer<'de>,
807 {
808 deserializer.deserialize_any(BoolNoneIfVisitor { none_if })
809 }
810
811 pub fn deserialize_mapped_none_if<'de, D>(
812 deserializer: D,
813 true_values: &[&str],
814 false_values: &[&str],
815 unknown_as: Option<bool>,
816 none_if: &[&str],
817 ) -> Result<Option<bool>, D::Error>
818 where
819 D: serde::Deserializer<'de>,
820 {
821 deserializer.deserialize_any(MappedBoolNoneIfVisitor {
822 true_values,
823 false_values,
824 unknown_as,
825 none_if,
826 })
827 }
828
829 struct BoolVisitor;
830
831 impl Visitor<'_> for BoolVisitor {
832 type Value = bool;
833
834 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
835 formatter.write_str("a boolean string or numeric boolean")
836 }
837
838 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
839 Ok(value)
840 }
841
842 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
843 where
844 E: DeError,
845 {
846 super::deserialize_bool(value).map_err(DeError::custom)
847 }
848
849 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
850 where
851 E: DeError,
852 {
853 self.visit_str(&value)
854 }
855
856 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
857 where
858 E: DeError,
859 {
860 match value {
861 0 => Ok(false),
862 1 => Ok(true),
863 _ => Err(DeError::custom("invalid boolean number")),
864 }
865 }
866
867 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
868 where
869 E: DeError,
870 {
871 match value {
872 0 => Ok(false),
873 1 => Ok(true),
874 _ => Err(DeError::custom("invalid boolean number")),
875 }
876 }
877 }
878
879 struct MappedBoolVisitor<'a> {
880 true_values: &'a [&'a str],
881 false_values: &'a [&'a str],
882 unknown_as: Option<bool>,
883 }
884
885 impl Visitor<'_> for MappedBoolVisitor<'_> {
886 type Value = bool;
887
888 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
889 formatter.write_str("a configured boolean string or numeric boolean")
890 }
891
892 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
893 Ok(value)
894 }
895
896 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
897 where
898 E: DeError,
899 {
900 mapped_bool(value, self.true_values, self.false_values, self.unknown_as)
901 .ok_or_else(|| DeError::custom("invalid mapped boolean string"))
902 }
903
904 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
905 where
906 E: DeError,
907 {
908 self.visit_str(&value)
909 }
910
911 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
912 where
913 E: DeError,
914 {
915 match value {
916 0 => Ok(false),
917 1 => Ok(true),
918 _ => Err(DeError::custom("invalid boolean number")),
919 }
920 }
921
922 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
923 where
924 E: DeError,
925 {
926 match value {
927 0 => Ok(false),
928 1 => Ok(true),
929 _ => Err(DeError::custom("invalid boolean number")),
930 }
931 }
932 }
933
934 struct BoolNoneIfVisitor<'a> {
935 none_if: &'a [&'a str],
936 }
937
938 impl Visitor<'_> for BoolNoneIfVisitor<'_> {
939 type Value = Option<bool>;
940
941 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
942 formatter.write_str("a boolean string, numeric boolean, or configured sentinel")
943 }
944
945 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
946 Ok(Some(value))
947 }
948
949 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
950 where
951 E: DeError,
952 {
953 if self.none_if.contains(&value) {
954 Ok(None)
955 } else {
956 super::deserialize_bool(value)
957 .map(Some)
958 .map_err(DeError::custom)
959 }
960 }
961
962 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
963 where
964 E: DeError,
965 {
966 self.visit_str(&value)
967 }
968
969 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
970 where
971 E: DeError,
972 {
973 match value {
974 0 => Ok(Some(false)),
975 1 => Ok(Some(true)),
976 _ => Err(DeError::custom("invalid boolean number")),
977 }
978 }
979
980 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
981 where
982 E: DeError,
983 {
984 match value {
985 0 => Ok(Some(false)),
986 1 => Ok(Some(true)),
987 _ => Err(DeError::custom("invalid boolean number")),
988 }
989 }
990 }
991
992 struct MappedBoolNoneIfVisitor<'a> {
993 true_values: &'a [&'a str],
994 false_values: &'a [&'a str],
995 unknown_as: Option<bool>,
996 none_if: &'a [&'a str],
997 }
998
999 impl Visitor<'_> for MappedBoolNoneIfVisitor<'_> {
1000 type Value = Option<bool>;
1001
1002 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1003 formatter.write_str(
1004 "a configured boolean string, numeric boolean, or configured sentinel",
1005 )
1006 }
1007
1008 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
1009 Ok(Some(value))
1010 }
1011
1012 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1013 where
1014 E: DeError,
1015 {
1016 if self.none_if.contains(&value) {
1017 Ok(None)
1018 } else {
1019 mapped_bool(value, self.true_values, self.false_values, self.unknown_as)
1020 .map(Some)
1021 .ok_or_else(|| DeError::custom("invalid mapped boolean string"))
1022 }
1023 }
1024
1025 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1026 where
1027 E: DeError,
1028 {
1029 self.visit_str(&value)
1030 }
1031
1032 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1033 where
1034 E: DeError,
1035 {
1036 match value {
1037 0 => Ok(Some(false)),
1038 1 => Ok(Some(true)),
1039 _ => Err(DeError::custom("invalid boolean number")),
1040 }
1041 }
1042
1043 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
1044 where
1045 E: DeError,
1046 {
1047 match value {
1048 0 => Ok(Some(false)),
1049 1 => Ok(Some(true)),
1050 _ => Err(DeError::custom("invalid boolean number")),
1051 }
1052 }
1053 }
1054
1055 pub mod option {
1056 use std::fmt;
1057
1058 use serde::de::Visitor;
1059
1060 pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
1061 where
1062 S: serde::Serializer,
1063 {
1064 match value {
1065 Some(value) => super::serialize(value, serializer),
1066 None => serializer.serialize_none(),
1067 }
1068 }
1069
1070 pub fn serialize_mapped<S>(
1071 value: &Option<bool>,
1072 true_value: &str,
1073 false_value: &str,
1074 serializer: S,
1075 ) -> Result<S::Ok, S::Error>
1076 where
1077 S: serde::Serializer,
1078 {
1079 match value {
1080 Some(value) => {
1081 super::serialize_mapped(value, true_value, false_value, serializer)
1082 }
1083 None => serializer.serialize_none(),
1084 }
1085 }
1086
1087 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
1088 where
1089 D: serde::Deserializer<'de>,
1090 {
1091 deserializer.deserialize_option(BoolOptionVisitor)
1092 }
1093
1094 pub fn deserialize_mapped<'de, D>(
1095 deserializer: D,
1096 true_values: &[&str],
1097 false_values: &[&str],
1098 unknown_as: Option<bool>,
1099 ) -> Result<Option<bool>, D::Error>
1100 where
1101 D: serde::Deserializer<'de>,
1102 {
1103 deserializer.deserialize_option(MappedBoolOptionVisitor {
1104 true_values,
1105 false_values,
1106 unknown_as,
1107 none_if: None,
1108 })
1109 }
1110
1111 pub fn deserialize_none_if<'de, D>(
1112 deserializer: D,
1113 none_if: &[&str],
1114 ) -> Result<Option<bool>, D::Error>
1115 where
1116 D: serde::Deserializer<'de>,
1117 {
1118 deserializer.deserialize_option(BoolOptionNoneIfVisitor { none_if })
1119 }
1120
1121 pub fn deserialize_mapped_none_if<'de, D>(
1122 deserializer: D,
1123 true_values: &[&str],
1124 false_values: &[&str],
1125 unknown_as: Option<bool>,
1126 none_if: &[&str],
1127 ) -> Result<Option<bool>, D::Error>
1128 where
1129 D: serde::Deserializer<'de>,
1130 {
1131 deserializer.deserialize_option(MappedBoolOptionVisitor {
1132 true_values,
1133 false_values,
1134 unknown_as,
1135 none_if: Some(none_if),
1136 })
1137 }
1138
1139 struct BoolOptionVisitor;
1140
1141 impl<'de> Visitor<'de> for BoolOptionVisitor {
1142 type Value = Option<bool>;
1143
1144 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1145 formatter.write_str("an optional boolean string or numeric boolean")
1146 }
1147
1148 fn visit_none<E>(self) -> Result<Self::Value, E> {
1149 Ok(None)
1150 }
1151
1152 fn visit_unit<E>(self) -> Result<Self::Value, E> {
1153 Ok(None)
1154 }
1155
1156 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1157 where
1158 D: serde::Deserializer<'de>,
1159 {
1160 super::deserialize(deserializer).map(Some)
1161 }
1162 }
1163
1164 struct BoolOptionNoneIfVisitor<'a> {
1165 none_if: &'a [&'a str],
1166 }
1167
1168 impl<'de> Visitor<'de> for BoolOptionNoneIfVisitor<'_> {
1169 type Value = Option<bool>;
1170
1171 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1172 formatter.write_str("an optional boolean string, numeric boolean, or sentinel")
1173 }
1174
1175 fn visit_none<E>(self) -> Result<Self::Value, E> {
1176 Ok(None)
1177 }
1178
1179 fn visit_unit<E>(self) -> Result<Self::Value, E> {
1180 Ok(None)
1181 }
1182
1183 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1184 where
1185 D: serde::Deserializer<'de>,
1186 {
1187 deserializer.deserialize_any(super::BoolNoneIfVisitor {
1188 none_if: self.none_if,
1189 })
1190 }
1191 }
1192
1193 struct MappedBoolOptionVisitor<'a> {
1194 true_values: &'a [&'a str],
1195 false_values: &'a [&'a str],
1196 unknown_as: Option<bool>,
1197 none_if: Option<&'a [&'a str]>,
1198 }
1199
1200 impl<'de> Visitor<'de> for MappedBoolOptionVisitor<'_> {
1201 type Value = Option<bool>;
1202
1203 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1204 formatter.write_str("an optional configured boolean string or numeric boolean")
1205 }
1206
1207 fn visit_none<E>(self) -> Result<Self::Value, E> {
1208 Ok(None)
1209 }
1210
1211 fn visit_unit<E>(self) -> Result<Self::Value, E> {
1212 Ok(None)
1213 }
1214
1215 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1216 where
1217 D: serde::Deserializer<'de>,
1218 {
1219 match self.none_if {
1220 Some(none_if) => {
1221 deserializer.deserialize_any(super::MappedBoolNoneIfVisitor {
1222 true_values: self.true_values,
1223 false_values: self.false_values,
1224 unknown_as: self.unknown_as,
1225 none_if,
1226 })
1227 }
1228 None => super::deserialize_mapped(
1229 deserializer,
1230 self.true_values,
1231 self.false_values,
1232 self.unknown_as,
1233 )
1234 .map(Some),
1235 }
1236 }
1237 }
1238 }
1239
1240 fn mapped_bool(
1241 value: &str,
1242 true_values: &[&str],
1243 false_values: &[&str],
1244 unknown_as: Option<bool>,
1245 ) -> Option<bool> {
1246 if true_values.contains(&value) {
1247 Some(true)
1248 } else if false_values.contains(&value) {
1249 Some(false)
1250 } else {
1251 unknown_as
1252 }
1253 }
1254 }
1255
1256 pub mod as_date {
1257 use serde::Deserialize;
1258 use serde::de::Error as DeError;
1259
1260 use crate::Date;
1261
1262 pub fn serialize<S>(value: &Date, serializer: S) -> Result<S::Ok, S::Error>
1263 where
1264 S: serde::Serializer,
1265 {
1266 serializer.serialize_str(&crate::format_date(value))
1267 }
1268
1269 pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
1270 where
1271 D: serde::Deserializer<'de>,
1272 {
1273 let value = <String as Deserialize>::deserialize(deserializer)?;
1274 crate::parse_date(&value).map_err(DeError::custom)
1275 }
1276
1277 pub fn serialize_none_if<S>(
1278 value: &Option<Date>,
1279 none_value: &str,
1280 serializer: S,
1281 ) -> Result<S::Ok, S::Error>
1282 where
1283 S: serde::Serializer,
1284 {
1285 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1286 }
1287
1288 pub fn deserialize_none_if<'de, D>(
1289 deserializer: D,
1290 none_if: &[&str],
1291 ) -> Result<Option<Date>, D::Error>
1292 where
1293 D: serde::Deserializer<'de>,
1294 {
1295 super::deserialize_none_if_with(deserializer, none_if, crate::parse_date)
1296 }
1297
1298 pub mod option {
1299 use serde::Deserialize;
1300 use serde::de::Error as DeError;
1301
1302 use super::super as serde_string;
1303 use crate::Date;
1304
1305 pub fn serialize<S>(value: &Option<Date>, serializer: S) -> Result<S::Ok, S::Error>
1306 where
1307 S: serde::Serializer,
1308 {
1309 match value {
1310 Some(value) => super::serialize(value, serializer),
1311 None => serializer.serialize_none(),
1312 }
1313 }
1314
1315 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Date>, D::Error>
1316 where
1317 D: serde::Deserializer<'de>,
1318 {
1319 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1320 value
1321 .map(|value| crate::parse_date(&value).map_err(DeError::custom))
1322 .transpose()
1323 }
1324
1325 pub fn deserialize_none_if<'de, D>(
1326 deserializer: D,
1327 none_if: &[&str],
1328 ) -> Result<Option<Date>, D::Error>
1329 where
1330 D: serde::Deserializer<'de>,
1331 {
1332 serde_string::deserialize_option_none_if_with(
1333 deserializer,
1334 none_if,
1335 crate::parse_date,
1336 )
1337 }
1338 }
1339 }
1340
1341 pub mod as_naive_datetime {
1342 use serde::Deserialize;
1343 use serde::de::Error as DeError;
1344
1345 use crate::PrimitiveDateTime;
1346
1347 pub fn serialize<S>(value: &PrimitiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
1348 where
1349 S: serde::Serializer,
1350 {
1351 serializer.serialize_str(&crate::format_naive_datetime(value))
1352 }
1353
1354 pub fn deserialize<'de, D>(deserializer: D) -> Result<PrimitiveDateTime, D::Error>
1355 where
1356 D: serde::Deserializer<'de>,
1357 {
1358 let value = <String as Deserialize>::deserialize(deserializer)?;
1359 crate::parse_naive_datetime(&value).map_err(DeError::custom)
1360 }
1361
1362 pub fn serialize_none_if<S>(
1363 value: &Option<PrimitiveDateTime>,
1364 none_value: &str,
1365 serializer: S,
1366 ) -> Result<S::Ok, S::Error>
1367 where
1368 S: serde::Serializer,
1369 {
1370 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1371 }
1372
1373 pub fn deserialize_none_if<'de, D>(
1374 deserializer: D,
1375 none_if: &[&str],
1376 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1377 where
1378 D: serde::Deserializer<'de>,
1379 {
1380 super::deserialize_none_if_with(deserializer, none_if, crate::parse_naive_datetime)
1381 }
1382
1383 pub mod option {
1384 use serde::Deserialize;
1385 use serde::de::Error as DeError;
1386
1387 use super::super as serde_string;
1388 use crate::PrimitiveDateTime;
1389
1390 pub fn serialize<S>(
1391 value: &Option<PrimitiveDateTime>,
1392 serializer: S,
1393 ) -> Result<S::Ok, S::Error>
1394 where
1395 S: serde::Serializer,
1396 {
1397 match value {
1398 Some(value) => super::serialize(value, serializer),
1399 None => serializer.serialize_none(),
1400 }
1401 }
1402
1403 pub fn deserialize<'de, D>(
1404 deserializer: D,
1405 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1406 where
1407 D: serde::Deserializer<'de>,
1408 {
1409 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1410 value
1411 .map(|value| crate::parse_naive_datetime(&value).map_err(DeError::custom))
1412 .transpose()
1413 }
1414
1415 pub fn deserialize_none_if<'de, D>(
1416 deserializer: D,
1417 none_if: &[&str],
1418 ) -> Result<Option<PrimitiveDateTime>, D::Error>
1419 where
1420 D: serde::Deserializer<'de>,
1421 {
1422 serde_string::deserialize_option_none_if_with(
1423 deserializer,
1424 none_if,
1425 crate::parse_naive_datetime,
1426 )
1427 }
1428 }
1429 }
1430
1431 pub mod as_offset_datetime {
1432 use serde::Deserialize;
1433 use serde::de::Error as DeError;
1434 use serde::ser::Error as SerError;
1435
1436 use crate::OffsetDateTime;
1437 use time::format_description::well_known::Rfc3339;
1438
1439 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1440 where
1441 S: serde::Serializer,
1442 {
1443 let value = value.format(&Rfc3339).map_err(SerError::custom)?;
1444 serializer.serialize_str(&value)
1445 }
1446
1447 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1448 where
1449 D: serde::Deserializer<'de>,
1450 {
1451 let value = <String as Deserialize>::deserialize(deserializer)?;
1452 OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom)
1453 }
1454
1455 pub fn serialize_none_if<S>(
1456 value: &Option<OffsetDateTime>,
1457 none_value: &str,
1458 serializer: S,
1459 ) -> Result<S::Ok, S::Error>
1460 where
1461 S: serde::Serializer,
1462 {
1463 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1464 }
1465
1466 pub fn deserialize_none_if<'de, D>(
1467 deserializer: D,
1468 none_if: &[&str],
1469 ) -> Result<Option<OffsetDateTime>, D::Error>
1470 where
1471 D: serde::Deserializer<'de>,
1472 {
1473 super::deserialize_none_if_with(deserializer, none_if, |value| {
1474 OffsetDateTime::parse(value, &Rfc3339)
1475 })
1476 }
1477
1478 pub mod option {
1479 use serde::Deserialize;
1480 use serde::de::Error as DeError;
1481
1482 use super::super as serde_string;
1483 use crate::OffsetDateTime;
1484 use time::format_description::well_known::Rfc3339;
1485
1486 pub fn serialize<S>(
1487 value: &Option<OffsetDateTime>,
1488 serializer: S,
1489 ) -> Result<S::Ok, S::Error>
1490 where
1491 S: serde::Serializer,
1492 {
1493 match value {
1494 Some(value) => super::serialize(value, serializer),
1495 None => serializer.serialize_none(),
1496 }
1497 }
1498
1499 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1500 where
1501 D: serde::Deserializer<'de>,
1502 {
1503 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1504 value
1505 .map(|value| OffsetDateTime::parse(&value, &Rfc3339).map_err(DeError::custom))
1506 .transpose()
1507 }
1508
1509 pub fn deserialize_none_if<'de, D>(
1510 deserializer: D,
1511 none_if: &[&str],
1512 ) -> Result<Option<OffsetDateTime>, D::Error>
1513 where
1514 D: serde::Deserializer<'de>,
1515 {
1516 serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1517 OffsetDateTime::parse(value, &Rfc3339)
1518 })
1519 }
1520 }
1521 }
1522
1523 pub mod as_unix_time {
1524 use serde::Deserialize;
1525 use serde::de::Error as DeError;
1526
1527 use crate::OffsetDateTime;
1528
1529 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1530 where
1531 S: serde::Serializer,
1532 {
1533 serializer.serialize_str(&crate::format_unix_time(value))
1534 }
1535
1536 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1537 where
1538 D: serde::Deserializer<'de>,
1539 {
1540 let value = <String as Deserialize>::deserialize(deserializer)?;
1541 let value = value.parse::<i64>().map_err(DeError::custom)?;
1542 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1543 }
1544
1545 pub fn serialize_none_if<S>(
1546 value: &Option<OffsetDateTime>,
1547 none_value: &str,
1548 serializer: S,
1549 ) -> Result<S::Ok, S::Error>
1550 where
1551 S: serde::Serializer,
1552 {
1553 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1554 }
1555
1556 pub fn deserialize_none_if<'de, D>(
1557 deserializer: D,
1558 none_if: &[&str],
1559 ) -> Result<Option<OffsetDateTime>, D::Error>
1560 where
1561 D: serde::Deserializer<'de>,
1562 {
1563 super::deserialize_none_if_with(deserializer, none_if, |value| {
1564 let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1565 OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1566 })
1567 }
1568
1569 pub mod option {
1570 use serde::Deserialize;
1571 use serde::de::Error as DeError;
1572
1573 use super::super as serde_string;
1574 use crate::OffsetDateTime;
1575
1576 pub fn serialize<S>(
1577 value: &Option<OffsetDateTime>,
1578 serializer: S,
1579 ) -> Result<S::Ok, S::Error>
1580 where
1581 S: serde::Serializer,
1582 {
1583 match value {
1584 Some(value) => super::serialize(value, serializer),
1585 None => serializer.serialize_none(),
1586 }
1587 }
1588
1589 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1590 where
1591 D: serde::Deserializer<'de>,
1592 {
1593 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1594 value
1595 .map(|value| {
1596 let value = value.parse::<i64>().map_err(DeError::custom)?;
1597 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1598 })
1599 .transpose()
1600 }
1601
1602 pub fn deserialize_none_if<'de, D>(
1603 deserializer: D,
1604 none_if: &[&str],
1605 ) -> Result<Option<OffsetDateTime>, D::Error>
1606 where
1607 D: serde::Deserializer<'de>,
1608 {
1609 serde_string::deserialize_option_none_if_with(deserializer, none_if, |value| {
1610 let value = value.parse::<i64>().map_err(|error| error.to_string())?;
1611 OffsetDateTime::from_unix_timestamp(value).map_err(|error| error.to_string())
1612 })
1613 }
1614 }
1615 }
1616
1617 pub mod as_time {
1618 use serde::Deserialize;
1619 use serde::de::Error as DeError;
1620
1621 use crate::Time;
1622
1623 pub fn serialize<S>(value: &Time, serializer: S) -> Result<S::Ok, S::Error>
1624 where
1625 S: serde::Serializer,
1626 {
1627 serializer.serialize_str(&crate::format_time(value))
1628 }
1629
1630 pub fn deserialize<'de, D>(deserializer: D) -> Result<Time, D::Error>
1631 where
1632 D: serde::Deserializer<'de>,
1633 {
1634 let value = <String as Deserialize>::deserialize(deserializer)?;
1635 crate::parse_time(&value).map_err(DeError::custom)
1636 }
1637
1638 pub fn serialize_none_if<S>(
1639 value: &Option<Time>,
1640 none_value: &str,
1641 serializer: S,
1642 ) -> Result<S::Ok, S::Error>
1643 where
1644 S: serde::Serializer,
1645 {
1646 super::serialize_option_or_sentinel(value.as_ref(), none_value, serializer, serialize)
1647 }
1648
1649 pub fn deserialize_none_if<'de, D>(
1650 deserializer: D,
1651 none_if: &[&str],
1652 ) -> Result<Option<Time>, D::Error>
1653 where
1654 D: serde::Deserializer<'de>,
1655 {
1656 super::deserialize_none_if_with(deserializer, none_if, crate::parse_time)
1657 }
1658
1659 pub mod option {
1660 use serde::Deserialize;
1661 use serde::de::Error as DeError;
1662
1663 use crate::Time;
1664
1665 pub fn serialize<S>(value: &Option<Time>, serializer: S) -> Result<S::Ok, S::Error>
1666 where
1667 S: serde::Serializer,
1668 {
1669 match value {
1670 Some(value) => super::serialize(value, serializer),
1671 None => serializer.serialize_none(),
1672 }
1673 }
1674
1675 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Time>, D::Error>
1676 where
1677 D: serde::Deserializer<'de>,
1678 {
1679 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1680 let Some(value) = value else {
1681 return Ok(None);
1682 };
1683 let value = value.trim();
1684 if value.is_empty() {
1685 return Ok(None);
1686 }
1687 crate::parse_time(value).map(Some).map_err(DeError::custom)
1688 }
1689
1690 pub fn deserialize_none_if<'de, D>(
1691 deserializer: D,
1692 none_if: &[&str],
1693 ) -> Result<Option<Time>, D::Error>
1694 where
1695 D: serde::Deserializer<'de>,
1696 {
1697 let value = <Option<String> as Deserialize>::deserialize(deserializer)?;
1698 let Some(value) = value else {
1699 return Ok(None);
1700 };
1701 if none_if.contains(&value.as_str()) {
1702 return Ok(None);
1703 }
1704 let value = value.trim();
1705 if value.is_empty() {
1706 return Ok(None);
1707 }
1708 crate::parse_time(value).map(Some).map_err(DeError::custom)
1709 }
1710 }
1711 }
1712
1713 fn serialize_display<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
1714 where
1715 T: fmt::Display,
1716 S: serde::Serializer,
1717 {
1718 serializer.serialize_str(&value.to_string())
1719 }
1720
1721 fn serialize_option_display<T, S>(value: Option<&T>, serializer: S) -> Result<S::Ok, S::Error>
1722 where
1723 T: fmt::Display,
1724 S: serde::Serializer,
1725 {
1726 match value {
1727 Some(value) => serialize_display(value, serializer),
1728 None => serializer.serialize_none(),
1729 }
1730 }
1731
1732 fn serialize_option_or_sentinel<T, S>(
1733 value: Option<&T>,
1734 none_value: &str,
1735 serializer: S,
1736 serialize: impl FnOnce(&T, S) -> Result<S::Ok, S::Error>,
1737 ) -> Result<S::Ok, S::Error>
1738 where
1739 S: serde::Serializer,
1740 {
1741 match value {
1742 Some(value) => serialize(value, serializer),
1743 None => serializer.serialize_str(none_value),
1744 }
1745 }
1746
1747 fn deserialize_from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1748 where
1749 T: FromStr,
1750 T::Err: fmt::Display,
1751 D: serde::Deserializer<'de>,
1752 {
1753 let value = String::deserialize(deserializer)?;
1754 value.parse::<T>().map_err(DeError::custom)
1755 }
1756
1757 fn deserialize_option_from_str<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
1758 where
1759 T: FromStr,
1760 T::Err: fmt::Display,
1761 D: serde::Deserializer<'de>,
1762 {
1763 let value = Option::<String>::deserialize(deserializer)?;
1764 value
1765 .map(|value| value.parse::<T>().map_err(DeError::custom))
1766 .transpose()
1767 }
1768
1769 fn deserialize_none_if_with<'de, T, D, E>(
1770 deserializer: D,
1771 none_if: &[&str],
1772 parse: impl FnOnce(&str) -> Result<T, E>,
1773 ) -> Result<Option<T>, D::Error>
1774 where
1775 D: serde::Deserializer<'de>,
1776 E: fmt::Display,
1777 {
1778 let value = String::deserialize(deserializer)?;
1779 parse_none_if_with(&value, none_if, parse)
1780 }
1781
1782 fn deserialize_option_none_if_with<'de, T, D, E>(
1783 deserializer: D,
1784 none_if: &[&str],
1785 parse: impl FnOnce(&str) -> Result<T, E>,
1786 ) -> Result<Option<T>, D::Error>
1787 where
1788 D: serde::Deserializer<'de>,
1789 E: fmt::Display,
1790 {
1791 let Some(value) = Option::<String>::deserialize(deserializer)? else {
1792 return Ok(None);
1793 };
1794 parse_none_if_with(&value, none_if, parse)
1795 }
1796
1797 fn parse_none_if_with<T, E, D>(
1798 value: &str,
1799 none_if: &[&str],
1800 parse: impl FnOnce(&str) -> Result<T, E>,
1801 ) -> Result<Option<T>, D>
1802 where
1803 E: fmt::Display,
1804 D: DeError,
1805 {
1806 if none_if.contains(&value) {
1807 return Ok(None);
1808 }
1809 parse(value).map(Some).map_err(DeError::custom)
1810 }
1811
1812 fn deserialize_bool(value: &str) -> Result<bool, &'static str> {
1813 match value {
1814 "1" => Ok(true),
1815 "0" => Ok(false),
1816 value if value.eq_ignore_ascii_case("true") => Ok(true),
1817 value if value.eq_ignore_ascii_case("false") => Ok(false),
1818 _ => Err("invalid boolean string"),
1819 }
1820 }
1821}
1822
1823#[cfg(feature = "serde")]
1824#[allow(clippy::missing_errors_doc)]
1825pub mod serde_integer {
1826 pub mod as_unix_time {
1827 use serde::Deserialize;
1828 use serde::de::Error as DeError;
1829
1830 use crate::OffsetDateTime;
1831
1832 pub fn serialize<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
1833 where
1834 S: serde::Serializer,
1835 {
1836 serializer.serialize_i64(value.unix_timestamp())
1837 }
1838
1839 pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
1840 where
1841 D: serde::Deserializer<'de>,
1842 {
1843 let value = <i64 as Deserialize>::deserialize(deserializer)?;
1844 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1845 }
1846
1847 pub mod option {
1848 use serde::Deserialize;
1849 use serde::de::Error as DeError;
1850
1851 use crate::OffsetDateTime;
1852
1853 pub fn serialize<S>(
1854 value: &Option<OffsetDateTime>,
1855 serializer: S,
1856 ) -> Result<S::Ok, S::Error>
1857 where
1858 S: serde::Serializer,
1859 {
1860 match value {
1861 Some(value) => super::serialize(value, serializer),
1862 None => serializer.serialize_none(),
1863 }
1864 }
1865
1866 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
1867 where
1868 D: serde::Deserializer<'de>,
1869 {
1870 let value = <Option<i64> as Deserialize>::deserialize(deserializer)?;
1871 value
1872 .map(|value| {
1873 OffsetDateTime::from_unix_timestamp(value).map_err(DeError::custom)
1874 })
1875 .transpose()
1876 }
1877 }
1878 }
1879
1880 pub mod as_bool {
1881 use crate::serde_string::as_bool as string_bool;
1882
1883 pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
1884 where
1885 S: serde::Serializer,
1886 {
1887 serializer.serialize_u8(u8::from(*value))
1888 }
1889
1890 pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
1891 where
1892 D: serde::Deserializer<'de>,
1893 {
1894 string_bool::deserialize(deserializer)
1895 }
1896
1897 pub mod option {
1898 use crate::serde_string::as_bool::option as string_bool_option;
1899
1900 pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
1901 where
1902 S: serde::Serializer,
1903 {
1904 match value {
1905 Some(value) => super::serialize(value, serializer),
1906 None => serializer.serialize_none(),
1907 }
1908 }
1909
1910 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
1911 where
1912 D: serde::Deserializer<'de>,
1913 {
1914 string_bool_option::deserialize(deserializer)
1915 }
1916 }
1917 }
1918}
1919
1920#[cfg(feature = "json")]
1921pub mod treat_error_as_none {
1922 use serde::de::DeserializeOwned;
1923 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1924
1925 pub fn serialize<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
1931 where
1932 S: Serializer,
1933 T: Serialize,
1934 {
1935 match value {
1936 Some(inner) => inner.serialize(serializer),
1937 None => serializer.serialize_none(),
1938 }
1939 }
1940
1941 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
1947 where
1948 D: Deserializer<'de>,
1949 T: DeserializeOwned,
1950 {
1951 let value = serde_json::Value::deserialize(deserializer)?;
1952 match T::deserialize(value) {
1953 Ok(parsed) => Ok(Some(parsed)),
1954 Err(_) => Ok(None),
1955 }
1956 }
1957}
1958
1959pub fn insert_header(
1965 headers: &mut http::HeaderMap,
1966 name: &'static str,
1967 value: &str,
1968) -> Result<(), Error> {
1969 headers.insert(
1970 HeaderName::from_bytes(name.as_bytes())?,
1971 HeaderValue::from_str(value)?,
1972 );
1973 Ok(())
1974}
1975
1976#[must_use]
1977pub fn has_json_content_type(headers: &http::HeaderMap) -> bool {
1978 headers
1979 .get(CONTENT_TYPE)
1980 .and_then(|value| value.to_str().ok())
1981 .is_some_and(is_json_media_type)
1982}
1983
1984fn is_json_media_type(value: &str) -> bool {
1985 let media_type = value.split(';').next().unwrap_or(value).trim();
1986 if media_type.eq_ignore_ascii_case("application/json") {
1987 return true;
1988 }
1989
1990 let Some((_, subtype)) = media_type.rsplit_once('/') else {
1991 return false;
1992 };
1993 ends_with_ignore_ascii_case(subtype, "+json")
1994}
1995
1996fn ends_with_ignore_ascii_case(value: &str, suffix: &str) -> bool {
1997 let value = value.as_bytes();
1998 let suffix = suffix.as_bytes();
1999 value.len() >= suffix.len() && value[value.len() - suffix.len()..].eq_ignore_ascii_case(suffix)
2000}
2001
2002fn append_percent_encoded(out: &mut String, bytes: &[u8]) {
2003 const HEX: &[u8; 16] = b"0123456789ABCDEF";
2004
2005 for &byte in bytes {
2006 if is_unreserved(byte) {
2007 out.push(byte as char);
2008 } else {
2009 out.push('%');
2010 out.push(HEX[(byte >> 4) as usize] as char);
2011 out.push(HEX[(byte & 0x0f) as usize] as char);
2012 }
2013 }
2014}
2015
2016const fn is_unreserved(byte: u8) -> bool {
2017 matches!(
2018 byte,
2019 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~'
2020 )
2021}
2022
2023#[cfg(test)]
2024mod tests {
2025 use super::*;
2026
2027 #[cfg(all(feature = "serde", feature = "json"))]
2028 mod required_f64_none_if {
2029 use crate::serde_string::as_f64;
2030
2031 #[allow(clippy::ref_option)]
2032 pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
2033 where
2034 S: serde::Serializer,
2035 {
2036 as_f64::serialize_none_if(value, "NA", serializer)
2037 }
2038
2039 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
2040 where
2041 D: serde::Deserializer<'de>,
2042 {
2043 as_f64::deserialize_none_if(deserializer, &["NA", "-"])
2044 }
2045 }
2046
2047 #[cfg(all(feature = "serde", feature = "json"))]
2048 mod optional_f64_none_if {
2049 use crate::serde_string::as_f64::{self, option};
2050
2051 #[allow(clippy::ref_option)]
2052 pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
2053 where
2054 S: serde::Serializer,
2055 {
2056 as_f64::serialize_none_if(value, "NA", serializer)
2057 }
2058
2059 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
2060 where
2061 D: serde::Deserializer<'de>,
2062 {
2063 option::deserialize_none_if(deserializer, &["NA", "-"])
2064 }
2065 }
2066
2067 #[test]
2068 fn encodes_path_segments() {
2069 let mut out = String::new();
2070 append_path_segment(&mut out, "a/b c");
2071 assert_eq!(out, "a%2Fb%20c");
2072 }
2073
2074 #[test]
2075 fn appends_query_pairs() {
2076 let mut out = String::from("/pets");
2077 let mut first = true;
2078 append_query_pair(&mut out, &mut first, "tag name", "small/dog");
2079 append_query_pair(&mut out, &mut first, "limit", "10");
2080 assert_eq!(out, "/pets?tag%20name=small%2Fdog&limit=10");
2081 }
2082
2083 #[test]
2084 fn parses_range_strings() {
2085 assert_eq!(parse_range::<u8>("14-17").unwrap(), (Some(14), Some(17)));
2086 assert_eq!(parse_range::<u8>("14-").unwrap(), (Some(14), None));
2087 assert_eq!(parse_range::<u8>("-17").unwrap(), (None, Some(17)));
2088 assert_eq!(parse_range::<u8>("").unwrap(), (None, None));
2089 assert!(matches!(
2090 parse_range::<u8>("14-17-20"),
2091 Err(ParseRangeError::TooManySeparators)
2092 ));
2093 }
2094
2095 #[test]
2096 fn formats_range_strings() {
2097 assert_eq!(format_range(&Some(14), &Some(17)), "14-17");
2098 assert_eq!(format_range(&Some(14), &None::<u8>), "14-");
2099 assert_eq!(format_range(&None::<u8>, &Some(17)), "-17");
2100 assert_eq!(format_range(&None::<u8>, &None::<u8>), "");
2101 }
2102
2103 #[test]
2104 fn parses_and_formats_time_strings() {
2105 let time = parse_time("0620").unwrap();
2106 assert_eq!(time.hour(), 6);
2107 assert_eq!(time.minute(), 20);
2108 assert_eq!(format_time(&time), "0620");
2109 assert_eq!(parse_time("6:20"), Err(ParseTimeError::InvalidFormat));
2110 assert_eq!(parse_time("2400"), Err(ParseTimeError::ComponentRange));
2111 }
2112
2113 #[test]
2114 fn parses_and_formats_date_strings() {
2115 let date = parse_date("2024-07-16").unwrap();
2116 assert_eq!(
2117 date,
2118 Date::from_calendar_date(2024, Month::July, 16).unwrap()
2119 );
2120 assert_eq!(format_date(&date), "2024-07-16");
2121 assert_eq!(parse_date("2024-7-16"), Err(ParseDateError::InvalidFormat));
2122 assert_eq!(parse_date("not-a-date"), Err(ParseDateError::InvalidFormat));
2123 }
2124
2125 #[test]
2126 fn parses_and_formats_naive_datetime_strings() {
2127 let datetime = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
2128 assert_eq!(datetime.hour(), 23);
2129 assert_eq!(datetime.minute(), 59);
2130 assert_eq!(datetime.second(), 0);
2131 assert_eq!(format_naive_datetime(&datetime), "2024-07-16T23:59:00");
2132 assert_eq!(
2133 parse_naive_datetime("2024-07-16T23:59"),
2134 Err(ParseNaiveDateTimeError::InvalidFormat)
2135 );
2136 assert_eq!(
2137 parse_naive_datetime("2024-07-16 23:59:00"),
2138 Err(ParseNaiveDateTimeError::InvalidFormat)
2139 );
2140 }
2141
2142 #[test]
2143 fn formats_unix_time_seconds() {
2144 let datetime = OffsetDateTime::from_unix_timestamp(-1).unwrap();
2145 assert_eq!(format_unix_time(&datetime), "-1");
2146 }
2147
2148 #[cfg(all(feature = "serde", feature = "json"))]
2149 #[test]
2150 fn serde_string_naive_datetime_round_trips() {
2151 #[derive(serde::Deserialize, serde::Serialize)]
2152 struct Value {
2153 #[serde(with = "crate::serde_string::as_naive_datetime")]
2154 at: PrimitiveDateTime,
2155 }
2156
2157 let at = parse_naive_datetime("2024-07-16T23:59:00").unwrap();
2158 let encoded = serde_json::to_value(Value { at }).unwrap();
2159 assert_eq!(encoded, serde_json::json!({ "at": "2024-07-16T23:59:00" }));
2160
2161 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2162 assert_eq!(decoded.at, at);
2163 }
2164
2165 #[cfg(all(feature = "serde", feature = "json"))]
2166 #[test]
2167 fn serde_string_date_round_trips() {
2168 #[derive(serde::Deserialize, serde::Serialize)]
2169 struct Value {
2170 #[serde(with = "crate::serde_string::as_date")]
2171 day: Date,
2172 }
2173
2174 let date = Date::from_calendar_date(2024, Month::July, 16).unwrap();
2175 let encoded = serde_json::to_value(Value { day: date }).unwrap();
2176 assert_eq!(encoded, serde_json::json!({ "day": "2024-07-16" }));
2177
2178 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2179 assert_eq!(decoded.day, date);
2180 }
2181
2182 #[test]
2183 fn recognizes_json_content_types() {
2184 let mut headers = http::HeaderMap::new();
2185 headers.insert(
2186 CONTENT_TYPE,
2187 http::HeaderValue::from_static("application/problem+json; charset=utf-8"),
2188 );
2189 assert!(has_json_content_type(&headers));
2190 }
2191
2192 #[test]
2193 fn response_parts_holds_status_headers_body() {
2194 let mut headers = http::HeaderMap::new();
2195 headers.insert(
2196 CONTENT_TYPE,
2197 http::HeaderValue::from_static("application/json"),
2198 );
2199 let body = br#"{"ok":true}"#.to_vec();
2200 let parts = ResponseParts {
2201 status: http::StatusCode::OK,
2202 headers,
2203 body,
2204 };
2205 assert_eq!(parts.status, http::StatusCode::OK);
2206 assert_eq!(parts.headers.get(CONTENT_TYPE).unwrap(), "application/json");
2207 assert_eq!(parts.body, br#"{"ok":true}"#);
2208 }
2209
2210 #[cfg(feature = "json")]
2211 #[test]
2212 fn projected_json_unwraps_and_maps_fields() {
2213 let body = br#"{
2214 "odata.metadata": "https://example.test/metadata",
2215 "value": [
2216 {"Link": "https://example.test/a", "Name": "A"},
2217 {"Link": "https://example.test/b", "Name": "B"}
2218 ]
2219 }"#;
2220
2221 let rows = from_projected_json_slice::<Vec<JsonValue>>(body, "value", None).unwrap();
2222 assert_eq!(rows.len(), 2);
2223 assert_eq!(rows[0]["Name"], "A");
2224
2225 let links = from_projected_json_slice::<Vec<String>>(body, "value", Some("Link")).unwrap();
2226 assert_eq!(
2227 links,
2228 vec![
2229 "https://example.test/a".to_owned(),
2230 "https://example.test/b".to_owned()
2231 ]
2232 );
2233 }
2234
2235 #[cfg(feature = "json")]
2236 #[test]
2237 fn projected_json_preserves_optional_missing_fields_as_null() {
2238 let missing = br#"{"metadata":"present"}"#;
2239 let value = from_projected_json_slice::<Option<Vec<String>>>(missing, "value", None)
2240 .expect("optional missing projection");
2241 assert_eq!(value, None);
2242
2243 let rows = br#"{"value":[{}, {"Link":"present"}]}"#;
2244 let links = from_projected_json_slice::<Vec<Option<String>>>(rows, "value", Some("Link"))
2245 .expect("optional mapped field");
2246 assert_eq!(links, vec![None, Some("present".to_owned())]);
2247 }
2248
2249 #[cfg(feature = "json")]
2250 #[test]
2251 fn projected_json_rejects_invalid_container_shapes() {
2252 let scalar = from_projected_json_slice::<Vec<String>>(
2253 br#"{"value":"not-an-array"}"#,
2254 "value",
2255 Some("Link"),
2256 );
2257 assert!(matches!(scalar, Err(Error::InvalidResponse(_))));
2258
2259 let scalar_item = from_projected_json_slice::<Vec<String>>(
2260 br#"{"value":["not-an-object"]}"#,
2261 "value",
2262 Some("Link"),
2263 );
2264 assert!(matches!(scalar_item, Err(Error::InvalidResponse(_))));
2265 }
2266
2267 #[cfg(all(feature = "serde", feature = "json"))]
2268 #[test]
2269 fn serde_string_bool_accepts_string_and_numeric_values() {
2270 #[derive(serde::Deserialize, serde::Serialize)]
2271 struct Value {
2272 #[serde(with = "crate::serde_string::as_bool")]
2273 monitored: bool,
2274 }
2275
2276 let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
2277 assert!(!numeric.monitored);
2278
2279 let string = serde_json::from_str::<Value>(r#"{"monitored":"1"}"#).unwrap();
2280 assert!(string.monitored);
2281
2282 let encoded = serde_json::to_value(Value { monitored: false }).unwrap();
2283 assert_eq!(encoded, serde_json::json!({ "monitored": "0" }));
2284 }
2285
2286 #[cfg(all(feature = "serde", feature = "json"))]
2287 #[test]
2288 fn serde_string_none_if_is_strict_and_canonical() {
2289 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2290 struct Value {
2291 #[serde(with = "required_f64_none_if")]
2292 required: Option<f64>,
2293 #[serde(default, with = "optional_f64_none_if")]
2294 optional: Option<f64>,
2295 }
2296
2297 let valid =
2298 serde_json::from_str::<Value>(r#"{"required":"28.7","optional":"10.5"}"#).unwrap();
2299 assert_eq!(valid.required, Some(28.7));
2300 assert_eq!(valid.optional, Some(10.5));
2301
2302 let sentinel =
2303 serde_json::from_str::<Value>(r#"{"required":"-","optional":"NA"}"#).unwrap();
2304 assert_eq!(sentinel.required, None);
2305 assert_eq!(sentinel.optional, None);
2306
2307 let null_optional =
2308 serde_json::from_str::<Value>(r#"{"required":"28.7","optional":null}"#).unwrap();
2309 assert_eq!(null_optional.optional, None);
2310 assert!(serde_json::from_str::<Value>(r#"{"required":null}"#).is_err());
2311 assert!(serde_json::from_str::<Value>(r#"{"required":"unexpected"}"#).is_err());
2312
2313 let encoded = serde_json::to_value(Value {
2314 required: None,
2315 optional: Some(10.5),
2316 })
2317 .unwrap();
2318 assert_eq!(
2319 encoded,
2320 serde_json::json!({"required": "NA", "optional": "10.5"})
2321 );
2322 }
2323
2324 #[cfg(all(feature = "serde", feature = "json"))]
2325 #[test]
2326 fn serde_string_none_if_preserves_bool_and_time_parsers() {
2327 use crate::serde_string::{as_bool, as_time::option as time_option};
2328 use serde_json::Value;
2329
2330 let bool_sentinel =
2331 as_bool::deserialize_none_if(Value::String("NA".to_owned()), &["NA"]).unwrap();
2332 assert_eq!(bool_sentinel, None);
2333
2334 let bool_numeric = as_bool::deserialize_none_if(Value::from(1), &["NA"]).unwrap();
2335 assert_eq!(bool_numeric, Some(true));
2336
2337 let empty_time =
2338 time_option::deserialize_none_if(Value::String(" ".to_owned()), &["-"]).unwrap();
2339 assert_eq!(empty_time, None);
2340 assert!(
2341 time_option::deserialize_none_if(Value::String("invalid".to_owned()), &["-"]).is_err()
2342 );
2343 }
2344
2345 #[cfg(all(feature = "serde", feature = "json"))]
2346 #[test]
2347 fn serde_integer_bool_accepts_numeric_values() {
2348 #[derive(serde::Deserialize, serde::Serialize)]
2349 struct Value {
2350 #[serde(with = "crate::serde_integer::as_bool")]
2351 monitored: bool,
2352 }
2353
2354 let numeric = serde_json::from_str::<Value>(r#"{"monitored":0}"#).unwrap();
2355 assert!(!numeric.monitored);
2356
2357 let encoded = serde_json::to_value(Value { monitored: true }).unwrap();
2358 assert_eq!(encoded, serde_json::json!({ "monitored": 1 }));
2359 }
2360
2361 #[cfg(all(feature = "serde", feature = "json"))]
2362 #[test]
2363 fn serde_string_unix_time_round_trips() {
2364 #[derive(serde::Deserialize, serde::Serialize)]
2365 struct Value {
2366 #[serde(with = "crate::serde_string::as_unix_time")]
2367 at: OffsetDateTime,
2368 }
2369
2370 let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
2371 let encoded = serde_json::to_value(Value { at }).unwrap();
2372 assert_eq!(encoded, serde_json::json!({ "at": "1719892800" }));
2373
2374 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2375 assert_eq!(decoded.at, at);
2376 }
2377
2378 #[cfg(all(feature = "serde", feature = "json"))]
2379 #[test]
2380 fn serde_integer_unix_time_round_trips_and_handles_null() {
2381 #[derive(serde::Deserialize, serde::Serialize)]
2382 struct Value {
2383 #[serde(with = "crate::serde_integer::as_unix_time")]
2384 at: OffsetDateTime,
2385 #[serde(with = "crate::serde_integer::as_unix_time::option")]
2386 maybe_at: Option<OffsetDateTime>,
2387 }
2388
2389 let at = OffsetDateTime::from_unix_timestamp(1_719_892_800).unwrap();
2390 let encoded = serde_json::to_value(Value { at, maybe_at: None }).unwrap();
2391 assert_eq!(
2392 encoded,
2393 serde_json::json!({ "at": 1_719_892_800, "maybe_at": null })
2394 );
2395
2396 let decoded = serde_json::from_value::<Value>(encoded).unwrap();
2397 assert_eq!(decoded.at, at);
2398 assert_eq!(decoded.maybe_at, None);
2399 }
2400
2401 #[cfg(all(feature = "serde", feature = "json"))]
2402 #[test]
2403 fn serde_integer_unix_time_rejects_out_of_range_values() {
2404 #[derive(serde::Deserialize)]
2405 #[allow(dead_code)]
2406 struct Value {
2407 #[serde(with = "crate::serde_integer::as_unix_time")]
2408 at: OffsetDateTime,
2409 }
2410
2411 assert!(serde_json::from_str::<Value>(r#"{"at":9223372036854775807}"#).is_err());
2412 }
2413}