1use std::str::FromStr;
26
27use bytes::Bytes;
28use rust_decimal::Decimal;
29use serde::{
30 Deserialize, Deserializer, Serialize, Serializer,
31 de::{Error, Unexpected, Visitor},
32 ser::SerializeSeq,
33};
34use ustr::Ustr;
35
36pub mod sorted_hashset {
40 use ahash::AHashSet;
41 use serde::{Deserialize, Deserializer, Serialize, Serializer};
42
43 pub fn serialize<T, S>(set: &AHashSet<T>, s: S) -> Result<S::Ok, S::Error>
50 where
51 T: Serialize + Ord,
52 S: Serializer,
53 {
54 let mut sorted: Vec<&T> = set.iter().collect();
55 sorted.sort_unstable();
56 sorted.serialize(s)
57 }
58
59 pub fn deserialize<'de, T, D>(d: D) -> Result<AHashSet<T>, D::Error>
66 where
67 T: Deserialize<'de> + Eq + std::hash::Hash,
68 D: Deserializer<'de>,
69 {
70 let vec = Vec::<T>::deserialize(d)?;
71 Ok(vec.into_iter().collect())
72 }
73}
74
75struct DecimalVisitor;
80
81impl Visitor<'_> for DecimalVisitor {
82 type Value = Decimal;
83
84 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
85 formatter.write_str("a decimal number as string, integer, or float")
86 }
87
88 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
90 if v.is_empty() {
91 return Ok(Decimal::ZERO);
92 }
93 parse_decimal_str(v).map_err(E::custom)
94 }
95
96 fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
98 self.visit_str(&v)
99 }
100
101 fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
103 Ok(Decimal::from(v))
104 }
105
106 fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
107 Ok(Decimal::from(v))
108 }
109
110 fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
111 Ok(Decimal::from(v))
112 }
113
114 fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
115 Ok(Decimal::from(v))
116 }
117
118 fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
120 if !v.is_finite() {
121 return Err(E::invalid_value(Unexpected::Float(v), &self));
122 }
123 Decimal::try_from(v).map_err(E::custom)
124 }
125
126 fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
128 Ok(Decimal::ZERO)
129 }
130
131 fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
132 Ok(Decimal::ZERO)
133 }
134}
135
136struct OptionalDecimalVisitor;
141
142impl Visitor<'_> for OptionalDecimalVisitor {
143 type Value = Option<Decimal>;
144
145 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
146 formatter.write_str("null or a decimal number as string, integer, or float")
147 }
148
149 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
152 if v.is_empty() {
153 return Ok(None);
154 }
155 DecimalVisitor.visit_str(v).map(Some)
156 }
157
158 fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
159 self.visit_str(&v)
160 }
161
162 fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
163 DecimalVisitor.visit_i64(v).map(Some)
164 }
165
166 fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
167 DecimalVisitor.visit_u64(v).map(Some)
168 }
169
170 fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
171 DecimalVisitor.visit_i128(v).map(Some)
172 }
173
174 fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
175 DecimalVisitor.visit_u128(v).map(Some)
176 }
177
178 fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
179 DecimalVisitor.visit_f64(v).map(Some)
180 }
181
182 fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
184 Ok(None)
185 }
186
187 fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
188 Ok(None)
189 }
190}
191
192fn parse_decimal_str(value: &str) -> Result<Decimal, String> {
193 let parsed = if value.contains('e') || value.contains('E') {
194 Decimal::from_scientific(value)
195 } else {
196 Decimal::from_str(value)
197 };
198
199 match parsed {
200 Ok(decimal) => Ok(decimal),
201 Err(e) => {
202 for scale in (0..=Decimal::MAX_SCALE as usize).rev() {
206 let clamped =
207 decimal_string_clamped_to_scale(value, scale).ok_or_else(|| e.to_string())?;
208
209 if let Ok(decimal) = Decimal::from_str(&clamped) {
210 return Ok(decimal);
211 }
212 }
213 Err(e.to_string())
214 }
215 }
216}
217
218fn decimal_string_clamped_to_scale(value: &str, max_scale: usize) -> Option<String> {
219 let (coefficient, exponent) = match value.find(['e', 'E']) {
220 Some(index) => {
221 let exponent = value[index + 1..].parse::<i32>().ok()?;
222 (&value[..index], exponent)
223 }
224 None => (value, 0),
225 };
226
227 let (sign, unsigned) = match coefficient.as_bytes().first()? {
228 b'+' => ("", &coefficient[1..]),
229 b'-' => ("-", &coefficient[1..]),
230 _ => ("", coefficient),
231 };
232 let (integer, fractional) = decimal_components(unsigned)?;
233 let digits = format!("{integer}{fractional}");
234 if decimal_digits_are_zero(&digits, "") {
235 return Some("0".to_string());
236 }
237
238 let point = i32::try_from(integer.len()).ok()?.checked_add(exponent)?;
239
240 if decimal_integer_digits_exceed_max(&digits, point) {
245 return None;
246 }
247
248 let (integer, fractional) = if point <= 0 {
249 let zero_count = usize::try_from(-point).ok()?.min(max_scale + 1);
252 (
253 "0".to_string(),
254 format!("{}{digits}", "0".repeat(zero_count)),
255 )
256 } else {
257 let point = usize::try_from(point).ok()?;
258 if point >= digits.len() {
259 (
260 format!("{}{}", digits, "0".repeat(point - digits.len())),
261 String::new(),
262 )
263 } else {
264 (digits[..point].to_string(), digits[point..].to_string())
265 }
266 };
267
268 let (integer, fractional) = round_decimal_components(integer, fractional, max_scale);
269 let sign = if sign == "-" && decimal_digits_are_zero(&integer, &fractional) {
270 ""
271 } else {
272 sign
273 };
274
275 if fractional.is_empty() {
276 Some(format!("{sign}{integer}"))
277 } else {
278 Some(format!("{sign}{integer}.{fractional}"))
279 }
280}
281
282fn decimal_components(value: &str) -> Option<(&str, &str)> {
283 let mut split = value.split('.');
284 let integer = split.next()?;
285 let fractional = split.next().unwrap_or("");
286 if split.next().is_some()
287 || (integer.is_empty() && fractional.is_empty())
288 || !integer.chars().all(|c| c.is_ascii_digit())
289 || !fractional.chars().all(|c| c.is_ascii_digit())
290 {
291 return None;
292 }
293 Some((integer, fractional))
294}
295
296fn decimal_integer_digits_exceed_max(digits: &str, point: i32) -> bool {
297 const DECIMAL_MAX_INTEGER_DIGITS: i32 = 29;
298
299 if point <= 0 {
300 return false;
301 }
302
303 let Some(first_non_zero) = digits.bytes().position(|digit| digit != b'0') else {
304 return false;
305 };
306 let Ok(first_non_zero) = i32::try_from(first_non_zero) else {
307 return true;
308 };
309
310 point.saturating_sub(first_non_zero) > DECIMAL_MAX_INTEGER_DIGITS
311}
312
313fn round_decimal_components(
314 mut integer: String,
315 fractional: String,
316 max_scale: usize,
317) -> (String, String) {
318 if fractional.len() <= max_scale {
319 return (integer, fractional);
320 }
321
322 let mut rounded = fractional.as_bytes()[..max_scale].to_vec();
323 if fractional.as_bytes()[max_scale] >= b'5' {
324 increment_decimal_digits(&mut integer, &mut rounded);
325 }
326
327 (
328 integer,
329 String::from_utf8(rounded).expect("decimal digits are ASCII"),
330 )
331}
332
333fn increment_decimal_digits(integer: &mut String, fractional: &mut [u8]) {
334 for digit in fractional.iter_mut().rev() {
335 if *digit < b'9' {
336 *digit += 1;
337 return;
338 }
339 *digit = b'0';
340 }
341
342 let mut integer_digits = integer.as_bytes().to_vec();
343 for digit in integer_digits.iter_mut().rev() {
344 if *digit < b'9' {
345 *digit += 1;
346 *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
347 return;
348 }
349 *digit = b'0';
350 }
351 integer_digits.insert(0, b'1');
352 *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
353}
354
355fn decimal_digits_are_zero(integer: &str, fractional: &str) -> bool {
356 integer
357 .bytes()
358 .chain(fractional.bytes())
359 .all(|digit| digit == b'0')
360}
361
362pub trait Serializable: Serialize + for<'de> Deserialize<'de> {
364 fn from_json_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
370 serde_json::from_slice(data)
371 }
372
373 fn to_json_bytes(&self) -> Result<Bytes, serde_json::Error> {
379 serde_json::to_vec(self).map(Bytes::from)
380 }
381}
382
383pub use self::msgpack::{FromMsgPack, MsgPackSerializable, ToMsgPack};
384
385pub mod msgpack {
390 use bytes::Bytes;
391 use serde::{Deserialize, Serialize};
392
393 use super::Serializable;
394
395 pub trait FromMsgPack: for<'de> Deserialize<'de> + Sized {
397 fn from_msgpack_bytes(data: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
403 rmp_serde::from_slice(data)
404 }
405 }
406
407 pub trait ToMsgPack: Serialize {
409 fn to_msgpack_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
415 rmp_serde::to_vec_named(self).map(Bytes::from)
416 }
417 }
418
419 pub trait MsgPackSerializable: Serializable + FromMsgPack + ToMsgPack {}
423
424 impl<T> FromMsgPack for T where T: Serializable {}
425
426 impl<T> ToMsgPack for T where T: Serializable {}
427
428 impl<T> MsgPackSerializable for T where T: Serializable {}
429}
430
431#[must_use]
435pub const fn default_true() -> bool {
436 true
437}
438
439#[must_use]
443pub const fn default_false() -> bool {
444 false
445}
446
447pub fn deserialize_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
470where
471 D: Deserializer<'de>,
472{
473 deserializer.deserialize_any(DecimalVisitor)
474}
475
476pub fn deserialize_optional_decimal<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
500where
501 D: Deserializer<'de>,
502{
503 deserializer.deserialize_any(OptionalDecimalVisitor)
506}
507
508pub fn serialize_decimal<S: Serializer>(d: &Decimal, s: S) -> Result<S::Ok, S::Error> {
516 rust_decimal::serde::float::serialize(d, s)
517}
518
519pub fn serialize_optional_decimal<S: Serializer>(
525 d: &Option<Decimal>,
526 s: S,
527) -> Result<S::Ok, S::Error> {
528 match d {
529 Some(decimal) => rust_decimal::serde::float::serialize(decimal, s),
530 None => s.serialize_none(),
531 }
532}
533
534pub fn deserialize_decimal_from_str<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
543where
544 D: Deserializer<'de>,
545{
546 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
547 Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
548}
549
550pub fn deserialize_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
558where
559 D: Deserializer<'de>,
560{
561 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
562 if s.is_empty() || s == "0" {
563 Ok(Decimal::ZERO)
564 } else {
565 Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
566 }
567}
568
569pub fn deserialize_optional_decimal_str<'de, D>(
579 deserializer: D,
580) -> Result<Option<Decimal>, D::Error>
581where
582 D: Deserializer<'de>,
583{
584 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
585 if s.is_empty() || s == "0" {
586 Ok(None)
587 } else {
588 Decimal::from_str(s.as_ref())
589 .map(Some)
590 .map_err(D::Error::custom)
591 }
592}
593
594pub fn deserialize_optional_decimal_from_str<'de, D>(
603 deserializer: D,
604) -> Result<Option<Decimal>, D::Error>
605where
606 D: Deserializer<'de>,
607{
608 let opt = Option::<String>::deserialize(deserializer)?;
609 match opt {
610 Some(s) if !s.is_empty() => Decimal::from_str(&s).map(Some).map_err(D::Error::custom),
611 _ => Ok(None),
612 }
613}
614
615pub fn deserialize_optional_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
623where
624 D: Deserializer<'de>,
625{
626 let opt: Option<String> = Deserialize::deserialize(deserializer)?;
627 match opt {
628 None => Ok(Decimal::ZERO),
629 Some(s) if s.is_empty() || s == "0" => Ok(Decimal::ZERO),
630 Some(s) => Decimal::from_str(&s).map_err(D::Error::custom),
631 }
632}
633
634pub fn deserialize_vec_decimal_from_str<'de, D>(deserializer: D) -> Result<Vec<Decimal>, D::Error>
640where
641 D: Deserializer<'de>,
642{
643 let strings = Vec::<String>::deserialize(deserializer)?;
644 strings
645 .into_iter()
646 .map(|s| Decimal::from_str(&s).map_err(D::Error::custom))
647 .collect()
648}
649
650pub fn serialize_decimal_as_str<S>(decimal: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
656where
657 S: Serializer,
658{
659 serializer.serialize_str(&decimal.to_string())
660}
661
662pub fn serialize_optional_decimal_as_str<S>(
668 decimal: &Option<Decimal>,
669 serializer: S,
670) -> Result<S::Ok, S::Error>
671where
672 S: Serializer,
673{
674 match decimal {
675 Some(d) => serializer.serialize_str(&d.to_string()),
676 None => serializer.serialize_none(),
677 }
678}
679
680pub fn serialize_vec_decimal_as_str<S>(
686 decimals: &Vec<Decimal>,
687 serializer: S,
688) -> Result<S::Ok, S::Error>
689where
690 S: Serializer,
691{
692 let mut seq = serializer.serialize_seq(Some(decimals.len()))?;
693 for decimal in decimals {
694 seq.serialize_element(&decimal.to_string())?;
695 }
696 seq.end()
697}
698
699pub fn parse_decimal(s: &str) -> anyhow::Result<Decimal> {
705 Decimal::from_str(s).map_err(|e| anyhow::anyhow!("Failed to parse decimal from '{s}': {e}"))
706}
707
708pub fn parse_optional_decimal(s: &Option<String>) -> anyhow::Result<Option<Decimal>> {
714 match s {
715 None => Ok(None),
716 Some(s) if s.is_empty() => Ok(None),
717 Some(s) => parse_decimal(s).map(Some),
718 }
719}
720
721pub fn deserialize_empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
733where
734 D: Deserializer<'de>,
735{
736 let opt = Option::<String>::deserialize(deserializer)?;
737 Ok(opt.filter(|s| !s.is_empty()))
738}
739
740pub fn deserialize_empty_ustr_as_none<'de, D>(deserializer: D) -> Result<Option<Ustr>, D::Error>
746where
747 D: Deserializer<'de>,
748{
749 let opt = Option::<Ustr>::deserialize(deserializer)?;
750 Ok(opt.filter(|s| !s.is_empty()))
751}
752
753pub fn deserialize_string_to_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
761where
762 D: Deserializer<'de>,
763{
764 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
765 if s.is_empty() {
766 return Ok(0);
767 }
768 s.as_ref().parse::<u8>().map_err(D::Error::custom)
769}
770
771pub fn deserialize_string_to_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
779where
780 D: Deserializer<'de>,
781{
782 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
783 if s.is_empty() {
784 Ok(0)
785 } else {
786 s.as_ref().parse::<u64>().map_err(D::Error::custom)
787 }
788}
789
790pub fn deserialize_optional_string_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
798where
799 D: Deserializer<'de>,
800{
801 let s: Option<String> = Option::deserialize(deserializer)?;
802 match s {
803 Some(s) if s.is_empty() => Ok(None),
804 Some(s) => s.parse().map(Some).map_err(D::Error::custom),
805 None => Ok(None),
806 }
807}
808
809#[cfg(test)]
810mod tests {
811 use ahash::AHashSet;
812 use rstest::*;
813 use rust_decimal::Decimal;
814 use rust_decimal_macros::dec;
815 use serde::{
816 Deserialize, Serialize,
817 de::{Visitor, value::Error as ValueError},
818 };
819 use serde_json::json;
820 use ustr::Ustr;
821
822 use super::{
823 DecimalVisitor, OptionalDecimalVisitor, Serializable, default_false, default_true,
824 deserialize_decimal, deserialize_decimal_from_str, deserialize_decimal_or_zero,
825 deserialize_empty_string_as_none, deserialize_empty_ustr_as_none,
826 deserialize_optional_decimal, deserialize_optional_decimal_or_zero,
827 deserialize_optional_decimal_str, deserialize_optional_string_to_u64,
828 deserialize_string_to_u8, deserialize_string_to_u64, deserialize_vec_decimal_from_str,
829 msgpack::{FromMsgPack, ToMsgPack},
830 parse_decimal, parse_optional_decimal, serialize_decimal, serialize_decimal_as_str,
831 serialize_optional_decimal, serialize_optional_decimal_as_str,
832 serialize_vec_decimal_as_str, sorted_hashset,
833 };
834
835 #[derive(Debug, PartialEq, Serialize, Deserialize)]
836 struct SortedSetPayload {
837 #[serde(with = "sorted_hashset")]
838 values: AHashSet<i32>,
839 }
840
841 #[derive(Serialize, Deserialize, PartialEq, Debug)]
842 struct SerializableTestStruct {
843 id: u32,
844 name: String,
845 value: f64,
846 }
847
848 impl Serializable for SerializableTestStruct {}
849
850 #[rstest]
851 fn test_sorted_hashset_serialization_is_deterministic() {
852 let payload = SortedSetPayload {
853 values: [29, 3, 17, 5, 23, 11].into_iter().collect(),
854 };
855
856 let encoded = serde_json::to_string(&payload).unwrap();
857 let expected = serde_json::to_string(&json!({ "values": [3, 5, 11, 17, 23, 29] })).unwrap();
858 let restored: SortedSetPayload = serde_json::from_str(&encoded).unwrap();
859
860 assert_eq!(encoded, expected);
861 assert_eq!(restored, payload);
862 }
863
864 #[rstest]
865 fn test_serializable_json_roundtrip() {
866 let original = SerializableTestStruct {
867 id: 42,
868 name: "test".to_string(),
869 value: std::f64::consts::PI,
870 };
871
872 let json_bytes = original.to_json_bytes().unwrap();
873 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
874
875 assert_eq!(original, deserialized);
876 }
877
878 #[rstest]
879 fn test_serializable_msgpack_roundtrip() {
880 let original = SerializableTestStruct {
881 id: 123,
882 name: "msgpack_test".to_string(),
883 value: std::f64::consts::E,
884 };
885
886 let msgpack_bytes = original.to_msgpack_bytes().unwrap();
887 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
888
889 assert_eq!(original, deserialized);
890 }
891
892 #[rstest]
893 fn test_serializable_json_invalid_data() {
894 let invalid_json = b"invalid json data";
895 let result = SerializableTestStruct::from_json_bytes(invalid_json);
896 assert!(result.is_err());
897 }
898
899 #[rstest]
900 fn test_serializable_msgpack_invalid_data() {
901 let invalid_msgpack = b"invalid msgpack data";
902 let result = SerializableTestStruct::from_msgpack_bytes(invalid_msgpack);
903 assert!(result.is_err());
904 }
905
906 #[rstest]
907 fn test_serializable_json_empty_values() {
908 let test_struct = SerializableTestStruct {
909 id: 0,
910 name: String::new(),
911 value: 0.0,
912 };
913
914 let json_bytes = test_struct.to_json_bytes().unwrap();
915 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
916
917 assert_eq!(test_struct, deserialized);
918 }
919
920 #[rstest]
921 fn test_serializable_msgpack_empty_values() {
922 let test_struct = SerializableTestStruct {
923 id: 0,
924 name: String::new(),
925 value: 0.0,
926 };
927
928 let msgpack_bytes = test_struct.to_msgpack_bytes().unwrap();
929 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
930
931 assert_eq!(test_struct, deserialized);
932 }
933
934 #[derive(Deserialize)]
935 struct TestOptionalDecimalStr {
936 #[serde(deserialize_with = "deserialize_optional_decimal_str")]
937 value: Option<Decimal>,
938 }
939
940 #[derive(Deserialize)]
941 struct TestDecimalOrZero {
942 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
943 value: Decimal,
944 }
945
946 #[derive(Deserialize)]
947 struct TestOptionalDecimalOrZero {
948 #[serde(deserialize_with = "deserialize_optional_decimal_or_zero")]
949 value: Decimal,
950 }
951
952 #[derive(Serialize, Deserialize, PartialEq, Debug)]
953 struct TestDecimalRoundtrip {
954 #[serde(
955 serialize_with = "serialize_decimal_as_str",
956 deserialize_with = "deserialize_decimal_from_str"
957 )]
958 value: Decimal,
959 #[serde(
960 serialize_with = "serialize_optional_decimal_as_str",
961 deserialize_with = "super::deserialize_optional_decimal_from_str"
962 )]
963 optional_value: Option<Decimal>,
964 }
965
966 #[rstest]
967 #[case(r#"{"value":"123.45"}"#, Some(dec!(123.45)))]
968 #[case(r#"{"value":"0"}"#, None)]
969 #[case(r#"{"value":""}"#, None)]
970 fn test_deserialize_optional_decimal_str(
971 #[case] json: &str,
972 #[case] expected: Option<Decimal>,
973 ) {
974 let result: TestOptionalDecimalStr = serde_json::from_str(json).unwrap();
975 assert_eq!(result.value, expected);
976 }
977
978 #[rstest]
979 fn test_deserialize_optional_decimal_from_str_empty_is_none() {
980 let json = r#"{"value": "1.5", "optional_value": ""}"#;
981 let result: TestDecimalRoundtrip = serde_json::from_str(json).unwrap();
982 assert_eq!(result.optional_value, None);
983 }
984
985 #[rstest]
986 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
987 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
988 #[case(r#"{"value":""}"#, Decimal::ZERO)]
989 fn test_deserialize_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
990 let result: TestDecimalOrZero = serde_json::from_str(json).unwrap();
991 assert_eq!(result.value, expected);
992 }
993
994 #[rstest]
995 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
996 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
997 #[case(r#"{"value":null}"#, Decimal::ZERO)]
998 #[case(r#"{"value":""}"#, Decimal::ZERO)]
999 fn test_deserialize_optional_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
1000 let result: TestOptionalDecimalOrZero = serde_json::from_str(json).unwrap();
1001 assert_eq!(result.value, expected);
1002 }
1003
1004 #[rstest]
1005 fn test_decimal_serialization_roundtrip() {
1006 let original = TestDecimalRoundtrip {
1007 value: dec!(123.456789012345678),
1008 optional_value: Some(dec!(0.000000001)),
1009 };
1010
1011 let json = serde_json::to_string(&original).unwrap();
1012
1013 assert!(json.contains("\"123.456789012345678\""));
1015 assert!(json.contains("\"0.000000001\""));
1016
1017 let deserialized: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1018 assert_eq!(original.value, deserialized.value);
1019 assert_eq!(original.optional_value, deserialized.optional_value);
1020 }
1021
1022 #[rstest]
1023 fn test_decimal_optional_none_handling() {
1024 let test_struct = TestDecimalRoundtrip {
1025 value: dec!(42.0),
1026 optional_value: None,
1027 };
1028
1029 let json = serde_json::to_string(&test_struct).unwrap();
1030 assert!(json.contains("null"));
1031
1032 let parsed: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1033 assert_eq!(test_struct.value, parsed.value);
1034 assert_eq!(None, parsed.optional_value);
1035 }
1036
1037 #[derive(Deserialize)]
1038 struct TestEmptyStringAsNone {
1039 #[serde(deserialize_with = "deserialize_empty_string_as_none")]
1040 value: Option<String>,
1041 }
1042
1043 #[rstest]
1044 #[case(r#"{"value":"hello"}"#, Some("hello".to_string()))]
1045 #[case(r#"{"value":""}"#, None)]
1046 #[case(r#"{"value":null}"#, None)]
1047 fn test_deserialize_empty_string_as_none(#[case] json: &str, #[case] expected: Option<String>) {
1048 let result: TestEmptyStringAsNone = serde_json::from_str(json).unwrap();
1049 assert_eq!(result.value, expected);
1050 }
1051
1052 #[derive(Deserialize)]
1053 struct TestEmptyUstrAsNone {
1054 #[serde(deserialize_with = "deserialize_empty_ustr_as_none")]
1055 value: Option<Ustr>,
1056 }
1057
1058 #[rstest]
1059 #[case(r#"{"value":"hello"}"#, Some(Ustr::from("hello")))]
1060 #[case(r#"{"value":""}"#, None)]
1061 #[case(r#"{"value":null}"#, None)]
1062 fn test_deserialize_empty_ustr_as_none(#[case] json: &str, #[case] expected: Option<Ustr>) {
1063 let result: TestEmptyUstrAsNone = serde_json::from_str(json).unwrap();
1064 assert_eq!(result.value, expected);
1065 }
1066
1067 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1068 struct TestVecDecimal {
1069 #[serde(
1070 serialize_with = "serialize_vec_decimal_as_str",
1071 deserialize_with = "deserialize_vec_decimal_from_str"
1072 )]
1073 values: Vec<Decimal>,
1074 }
1075
1076 #[rstest]
1077 fn test_vec_decimal_roundtrip() {
1078 let original = TestVecDecimal {
1079 values: vec![dec!(1.5), dec!(2.25), dec!(100.001)],
1080 };
1081
1082 let json = serde_json::to_string(&original).unwrap();
1083 assert!(json.contains("[\"1.5\",\"2.25\",\"100.001\"]"));
1084
1085 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1086 assert_eq!(original.values, parsed.values);
1087 }
1088
1089 #[rstest]
1090 fn test_vec_decimal_empty() {
1091 let original = TestVecDecimal { values: vec![] };
1092
1093 let json = serde_json::to_string(&original).unwrap();
1094 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1095 assert_eq!(original.values, parsed.values);
1096 }
1097
1098 #[derive(Deserialize)]
1099 struct TestStringToU8 {
1100 #[serde(deserialize_with = "deserialize_string_to_u8")]
1101 value: u8,
1102 }
1103
1104 #[rstest]
1105 #[case(r#"{"value":"42"}"#, 42)]
1106 #[case(r#"{"value":"0"}"#, 0)]
1107 #[case(r#"{"value":"255"}"#, 255)]
1108 #[case(r#"{"value":""}"#, 0)]
1109 fn test_deserialize_string_to_u8(#[case] json: &str, #[case] expected: u8) {
1110 let result: TestStringToU8 = serde_json::from_str(json).unwrap();
1111 assert_eq!(result.value, expected);
1112 }
1113
1114 #[rstest]
1115 #[case(r#"{"value":"256"}"#)]
1116 #[case(r#"{"value":"999"}"#)]
1117 #[case(r#"{"value":"abc"}"#)]
1118 fn test_deserialize_string_to_u8_invalid(#[case] json: &str) {
1119 let result: Result<TestStringToU8, _> = serde_json::from_str(json);
1120 assert!(result.is_err());
1121 }
1122
1123 #[derive(Deserialize)]
1124 struct TestStringToU64 {
1125 #[serde(deserialize_with = "deserialize_string_to_u64")]
1126 value: u64,
1127 }
1128
1129 #[rstest]
1130 #[case(r#"{"value":"12345678901234"}"#, 12_345_678_901_234)]
1131 #[case(r#"{"value":"0"}"#, 0)]
1132 #[case(r#"{"value":"18446744073709551615"}"#, u64::MAX)]
1133 #[case(r#"{"value":""}"#, 0)]
1134 fn test_deserialize_string_to_u64(#[case] json: &str, #[case] expected: u64) {
1135 let result: TestStringToU64 = serde_json::from_str(json).unwrap();
1136 assert_eq!(result.value, expected);
1137 }
1138
1139 #[rstest]
1140 #[case(r#"{"value":"18446744073709551616"}"#)]
1141 #[case(r#"{"value":"abc"}"#)]
1142 #[case(r#"{"value":"-1"}"#)]
1143 fn test_deserialize_string_to_u64_invalid(#[case] json: &str) {
1144 let result: Result<TestStringToU64, _> = serde_json::from_str(json);
1145 assert!(result.is_err());
1146 }
1147
1148 #[derive(Deserialize)]
1149 struct TestOptionalStringToU64 {
1150 #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
1151 value: Option<u64>,
1152 }
1153
1154 #[rstest]
1155 #[case(r#"{"value":"12345678901234"}"#, Some(12_345_678_901_234))]
1156 #[case(r#"{"value":"0"}"#, Some(0))]
1157 #[case(r#"{"value":""}"#, None)]
1158 #[case(r#"{"value":null}"#, None)]
1159 fn test_deserialize_optional_string_to_u64(#[case] json: &str, #[case] expected: Option<u64>) {
1160 let result: TestOptionalStringToU64 = serde_json::from_str(json).unwrap();
1161 assert_eq!(result.value, expected);
1162 }
1163
1164 #[rstest]
1165 #[case("123.45", dec!(123.45))]
1166 #[case("0", Decimal::ZERO)]
1167 #[case("0.0", Decimal::ZERO)]
1168 fn test_parse_decimal(#[case] input: &str, #[case] expected: Decimal) {
1169 let result = parse_decimal(input).unwrap();
1170 assert_eq!(result, expected);
1171 }
1172
1173 #[rstest]
1174 fn test_parse_decimal_invalid() {
1175 assert!(parse_decimal("invalid").is_err());
1176 assert!(parse_decimal("").is_err());
1177 }
1178
1179 #[rstest]
1180 #[case(&Some("123.45".to_string()), Some(dec!(123.45)))]
1181 #[case(&Some("0".to_string()), Some(Decimal::ZERO))]
1182 #[case(&Some(String::new()), None)]
1183 #[case(&None, None)]
1184 fn test_parse_optional_decimal(
1185 #[case] input: &Option<String>,
1186 #[case] expected: Option<Decimal>,
1187 ) {
1188 let result = parse_optional_decimal(input).unwrap();
1189 assert_eq!(result, expected);
1190 }
1191
1192 #[derive(Debug, Serialize, Deserialize, PartialEq)]
1195 struct TestFlexibleDecimal {
1196 #[serde(
1197 serialize_with = "serialize_decimal",
1198 deserialize_with = "deserialize_decimal"
1199 )]
1200 value: Decimal,
1201 #[serde(
1202 serialize_with = "serialize_optional_decimal",
1203 deserialize_with = "deserialize_optional_decimal"
1204 )]
1205 optional_value: Option<Decimal>,
1206 }
1207
1208 #[rstest]
1209 #[case(r#"{"value": 123.456, "optional_value": 789.012}"#, dec!(123.456), Some(dec!(789.012)))]
1210 #[case(r#"{"value": "123.456", "optional_value": "789.012"}"#, dec!(123.456), Some(dec!(789.012)))]
1211 #[case(r#"{"value": 100, "optional_value": null}"#, dec!(100), None)]
1212 #[case(r#"{"value": null, "optional_value": null}"#, Decimal::ZERO, None)]
1213 fn test_deserialize_flexible_decimal(
1214 #[case] json: &str,
1215 #[case] expected_value: Decimal,
1216 #[case] expected_optional: Option<Decimal>,
1217 ) {
1218 let result: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1219 assert_eq!(result.value, expected_value);
1220 assert_eq!(result.optional_value, expected_optional);
1221 }
1222
1223 #[rstest]
1224 fn test_flexible_decimal_roundtrip() {
1225 let original = TestFlexibleDecimal {
1226 value: dec!(123.456),
1227 optional_value: Some(dec!(789.012)),
1228 };
1229
1230 let json = serde_json::to_string(&original).unwrap();
1231 let deserialized: TestFlexibleDecimal = serde_json::from_str(&json).unwrap();
1232
1233 assert_eq!(original.value, deserialized.value);
1234 assert_eq!(original.optional_value, deserialized.optional_value);
1235 }
1236
1237 #[rstest]
1238 fn test_flexible_decimal_scientific_notation() {
1239 let json = r#"{"value": 0.00000001, "optional_value": 12345678.12345}"#;
1243 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1244 assert_eq!(parsed.value, dec!(0.00000001));
1245 assert_eq!(parsed.optional_value, Some(dec!(12345678.12345)));
1246 }
1247
1248 #[rstest]
1249 fn test_flexible_decimal_empty_string_optional() {
1250 let json = r#"{"value": 100, "optional_value": ""}"#;
1251 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1252 assert_eq!(parsed.value, dec!(100));
1253 assert_eq!(parsed.optional_value, None);
1254 }
1255
1256 #[derive(Debug, Deserialize)]
1259 struct TestDecimalOnly {
1260 #[serde(deserialize_with = "deserialize_decimal")]
1261 value: Decimal,
1262 }
1263
1264 #[rstest]
1265 #[case(r#"{"value": "1.5e-8"}"#, dec!(0.000000015))]
1266 #[case(r#"{"value": "1E10"}"#, dec!(10000000000))]
1267 #[case(r#"{"value": "-1.23e5"}"#, dec!(-123000))]
1268 fn test_deserialize_decimal_scientific_string(#[case] json: &str, #[case] expected: Decimal) {
1269 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1270 assert_eq!(result.value, expected);
1271 }
1272
1273 #[rstest]
1274 #[case(r#"{"value": 9223372036854775807}"#, dec!(9223372036854775807))] #[case(r#"{"value": -9223372036854775808}"#, dec!(-9223372036854775808))] #[case(r#"{"value": 0}"#, Decimal::ZERO)]
1277 fn test_deserialize_decimal_large_integers(#[case] json: &str, #[case] expected: Decimal) {
1278 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1279 assert_eq!(result.value, expected);
1280 }
1281
1282 #[rstest]
1283 #[case(r#"{"value": "-123.456789"}"#, dec!(-123.456789))]
1284 #[case(r#"{"value": -999.99}"#, dec!(-999.99))]
1285 fn test_deserialize_decimal_negative(#[case] json: &str, #[case] expected: Decimal) {
1286 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1287 assert_eq!(result.value, expected);
1288 }
1289
1290 #[rstest]
1291 #[case(r#"{"value": "123456789.123456789012345678"}"#)] fn test_deserialize_decimal_high_precision(#[case] json: &str) {
1293 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1294 assert_eq!(result.value, dec!(123456789.123456789012345678));
1295 }
1296
1297 #[rstest]
1298 #[case(
1299 r#"{"value": "1.234567890123456789012345678912345e-1"}"#,
1300 "0.1234567890123456789012345679"
1301 )]
1302 #[case(
1303 r#"{"value": "0.1234567890123456789012345678912345"}"#,
1304 "0.1234567890123456789012345679"
1305 )]
1306 #[case(
1307 r#"{"value": "999999999999999999999999999995e-29"}"#,
1308 "10.000000000000000000000000000"
1309 )]
1310 #[case(r#"{"value": "0.5e29"}"#, "50000000000000000000000000000")]
1311 #[case(r#"{"value": "-4e-29"}"#, "0.0000000000000000000000000000")]
1312 #[case(r#"{"value": "1.5e-999999"}"#, "0.0000000000000000000000000000")]
1313 #[case(r#"{"value": "9e-999999"}"#, "0.0000000000000000000000000000")]
1314 #[case(r#"{"value": "0e2000000000"}"#, "0")]
1315 fn test_deserialize_decimal_rounds_high_scale_values(
1316 #[case] json: &str,
1317 #[case] expected: &str,
1318 ) {
1319 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1321 assert_eq!(result.value.to_string(), expected);
1322 }
1323
1324 #[rstest]
1325 #[rstest]
1326 fn test_deserialize_decimal_negative_rounding_to_zero_loses_sign() {
1327 let json = r#"{"value": "-1.5e-999999"}"#;
1328 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1329 assert_eq!(result.value.to_string(), "0.0000000000000000000000000000");
1330 }
1331
1332 #[rstest]
1333 #[case(r#"{"value": "899999999999999999999999999995e-29"}"#)]
1334 #[case(r#"{"value": "+899999999999999999999999999995e-29"}"#)]
1335 fn test_deserialize_decimal_rounding_carries_into_integer(#[case] json: &str) {
1336 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1338 assert_eq!(result.value.to_string(), "9.000000000000000000000000000");
1339 }
1340
1341 #[rstest]
1342 #[case(r#"{"value": "8e28"}"#)] #[case(r#"{"value": "1e1000000000"}"#)] #[case(r#"{"value": "not-a-number"}"#)]
1345 #[case(r#"{"value": "1.2.3e1"}"#)] #[case(r#"{"value": ".e1"}"#)] fn test_deserialize_decimal_rejects_unrepresentable_values(#[case] json: &str) {
1348 let result: Result<TestDecimalOnly, _> = serde_json::from_str(json);
1351 assert!(result.is_err());
1352 }
1353
1354 #[rstest]
1355 fn test_deserialize_optional_decimal_rounds_high_scale_values() {
1356 let json = r#"{"value": "51.234567890123456789012345678912345e-1"}"#;
1357 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1358 assert_eq!(
1359 result.value.map(|v| v.to_string()),
1360 Some("5.1234567890123456789012345679".into()),
1361 );
1362 }
1363
1364 #[derive(Debug, Deserialize)]
1365 struct TestOptionalDecimalOnly {
1366 #[serde(deserialize_with = "deserialize_optional_decimal")]
1367 value: Option<Decimal>,
1368 }
1369
1370 #[rstest]
1371 #[case(r#"{"value": "1.5e-8"}"#, Some(dec!(0.000000015)))]
1372 #[case(r#"{"value": null}"#, None)]
1373 #[case(r#"{"value": ""}"#, None)]
1374 #[case(r#"{"value": 42}"#, Some(dec!(42)))]
1375 #[case(r#"{"value": -100.5}"#, Some(dec!(-100.5)))]
1376 fn test_deserialize_optional_decimal_various(
1377 #[case] json: &str,
1378 #[case] expected: Option<Decimal>,
1379 ) {
1380 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1381 assert_eq!(result.value, expected);
1382 }
1383
1384 #[rstest]
1385 fn test_decimal_visitor_direct_owned_and_wide_integer_paths() {
1386 assert_eq!(
1387 DecimalVisitor
1388 .visit_string::<ValueError>("1.5".to_string())
1389 .unwrap(),
1390 dec!(1.5)
1391 );
1392 assert_eq!(
1393 DecimalVisitor.visit_i128::<ValueError>(5).unwrap(),
1394 Decimal::from(5i128)
1395 );
1396 assert_eq!(
1397 DecimalVisitor.visit_u128::<ValueError>(5).unwrap(),
1398 Decimal::from(5u128)
1399 );
1400 assert_eq!(
1401 DecimalVisitor.visit_none::<ValueError>().unwrap(),
1402 Decimal::ZERO
1403 );
1404 }
1405
1406 #[rstest]
1407 fn test_decimal_visitor_rejects_non_finite_float() {
1408 assert!(
1409 DecimalVisitor
1410 .visit_f64::<ValueError>(f64::INFINITY)
1411 .is_err()
1412 );
1413 assert!(
1414 DecimalVisitor
1415 .visit_f64::<ValueError>(f64::NEG_INFINITY)
1416 .is_err()
1417 );
1418 assert!(DecimalVisitor.visit_f64::<ValueError>(f64::NAN).is_err());
1419 }
1420
1421 #[rstest]
1422 fn test_optional_decimal_visitor_direct_paths() {
1423 assert_eq!(
1424 OptionalDecimalVisitor
1425 .visit_string::<ValueError>("1.5".to_string())
1426 .unwrap(),
1427 Some(dec!(1.5))
1428 );
1429 assert_eq!(
1430 OptionalDecimalVisitor.visit_i64::<ValueError>(42).unwrap(),
1431 Some(dec!(42))
1432 );
1433 assert_eq!(
1434 OptionalDecimalVisitor.visit_i128::<ValueError>(5).unwrap(),
1435 Some(Decimal::from(5i128))
1436 );
1437 assert_eq!(
1438 OptionalDecimalVisitor.visit_u128::<ValueError>(5).unwrap(),
1439 Some(Decimal::from(5u128))
1440 );
1441 assert_eq!(
1442 OptionalDecimalVisitor.visit_none::<ValueError>().unwrap(),
1443 None
1444 );
1445 }
1446
1447 #[rstest]
1448 fn test_serialize_optional_decimal_none_serializes_null() {
1449 let original = TestFlexibleDecimal {
1450 value: dec!(1),
1451 optional_value: None,
1452 };
1453
1454 let json = serde_json::to_string(&original).unwrap();
1455 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1456 assert_eq!(parsed["optional_value"], json!(null));
1457 }
1458
1459 #[rstest]
1460 fn test_serde_bool_defaults() {
1461 assert!(default_true());
1462 assert!(!default_false());
1463 }
1464
1465 use proptest::prelude::*;
1466 use rust_decimal::prelude::ToPrimitive;
1467
1468 fn representable_decimal_strategy() -> impl Strategy<Value = Decimal> {
1469 (
1472 -79_228_162_514_264_337_593_543_950_335i128
1473 ..=79_228_162_514_264_337_593_543_950_335i128,
1474 0u32..=28u32,
1475 )
1476 .prop_map(|(mantissa, scale)| Decimal::from_i128_with_scale(mantissa, scale))
1477 }
1478
1479 fn numeric_string_strategy() -> impl Strategy<Value = String> {
1480 (
1483 proptest::bool::ANY,
1484 "[0-9]{1,30}",
1485 proptest::option::of("[0-9]{1,40}"),
1486 proptest::option::of(-40i32..=40),
1487 )
1488 .prop_map(|(negative, integer, fraction, exponent)| {
1489 let mut value = String::new();
1490 if negative {
1491 value.push('-');
1492 }
1493 value.push_str(&integer);
1494 if let Some(fraction) = fraction {
1495 value.push('.');
1496 value.push_str(&fraction);
1497 }
1498
1499 if let Some(exponent) = exponent {
1500 value.push('e');
1501 value.push_str(&exponent.to_string());
1502 }
1503 value
1504 })
1505 }
1506
1507 proptest! {
1508 #[rstest]
1509 fn prop_deserialize_decimal_roundtrips_representable_values(
1510 expected in representable_decimal_strategy()
1511 ) {
1512 let json = format!(r#"{{"value": "{expected}"}}"#);
1515 let parsed: TestDecimalOnly = serde_json::from_str(&json).unwrap();
1516 prop_assert_eq!(parsed.value, expected);
1517 }
1518
1519 #[rstest]
1520 fn prop_deserialize_decimal_total_on_arbitrary_strings(value in "\\PC{0,64}") {
1521 let json = serde_json::to_string(&serde_json::json!({"value": value})).unwrap();
1523 let _ = serde_json::from_str::<TestDecimalOnly>(&json);
1524 }
1525
1526 #[rstest]
1527 fn prop_deserialize_decimal_tracks_f64_reference(value in numeric_string_strategy()) {
1528 let json = format!(r#"{{"value": "{value}"}}"#);
1531 if let Ok(parsed) = serde_json::from_str::<TestDecimalOnly>(&json) {
1532 let reference: f64 = value.parse().unwrap();
1533 let decoded = parsed.value.to_f64().unwrap();
1534 prop_assert!(
1535 (decoded - reference).abs() <= reference.abs() * 1e-9 + 1e-27,
1536 "decoded {decoded} diverges from reference {reference} for input {value}",
1537 );
1538 }
1539 }
1540
1541 #[rstest]
1542 fn prop_deserialize_optional_decimal_matches_required(
1543 value in numeric_string_strategy()
1544 ) {
1545 let json = format!(r#"{{"value": "{value}"}}"#);
1546 let required = serde_json::from_str::<TestDecimalOnly>(&json);
1547 let optional = serde_json::from_str::<TestOptionalDecimalOnly>(&json);
1548 match (required, optional) {
1549 (Ok(required), Ok(optional)) => {
1550 prop_assert_eq!(optional.value, Some(required.value));
1551 }
1552 (Err(_), Err(_)) => {}
1553 (required, optional) => prop_assert!(
1554 false,
1555 "required and optional decoding disagree for input {}: {:?} vs {:?}",
1556 value,
1557 required.map(|r| r.value),
1558 optional.map(|o| o.value),
1559 ),
1560 }
1561 }
1562 }
1563}