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 rstest::*;
812 use rust_decimal::Decimal;
813 use rust_decimal_macros::dec;
814 use serde::{Deserialize, Serialize};
815 use ustr::Ustr;
816
817 use super::{
818 Serializable, deserialize_decimal, deserialize_decimal_from_str,
819 deserialize_decimal_or_zero, deserialize_empty_string_as_none,
820 deserialize_empty_ustr_as_none, deserialize_optional_decimal,
821 deserialize_optional_decimal_or_zero, deserialize_optional_decimal_str,
822 deserialize_optional_string_to_u64, deserialize_string_to_u8, deserialize_string_to_u64,
823 deserialize_vec_decimal_from_str,
824 msgpack::{FromMsgPack, ToMsgPack},
825 parse_decimal, parse_optional_decimal, serialize_decimal, serialize_decimal_as_str,
826 serialize_optional_decimal, serialize_optional_decimal_as_str,
827 serialize_vec_decimal_as_str,
828 };
829
830 #[derive(Serialize, Deserialize, PartialEq, Debug)]
831 struct SerializableTestStruct {
832 id: u32,
833 name: String,
834 value: f64,
835 }
836
837 impl Serializable for SerializableTestStruct {}
838
839 #[rstest]
840 fn test_serializable_json_roundtrip() {
841 let original = SerializableTestStruct {
842 id: 42,
843 name: "test".to_string(),
844 value: std::f64::consts::PI,
845 };
846
847 let json_bytes = original.to_json_bytes().unwrap();
848 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
849
850 assert_eq!(original, deserialized);
851 }
852
853 #[rstest]
854 fn test_serializable_msgpack_roundtrip() {
855 let original = SerializableTestStruct {
856 id: 123,
857 name: "msgpack_test".to_string(),
858 value: std::f64::consts::E,
859 };
860
861 let msgpack_bytes = original.to_msgpack_bytes().unwrap();
862 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
863
864 assert_eq!(original, deserialized);
865 }
866
867 #[rstest]
868 fn test_serializable_json_invalid_data() {
869 let invalid_json = b"invalid json data";
870 let result = SerializableTestStruct::from_json_bytes(invalid_json);
871 assert!(result.is_err());
872 }
873
874 #[rstest]
875 fn test_serializable_msgpack_invalid_data() {
876 let invalid_msgpack = b"invalid msgpack data";
877 let result = SerializableTestStruct::from_msgpack_bytes(invalid_msgpack);
878 assert!(result.is_err());
879 }
880
881 #[rstest]
882 fn test_serializable_json_empty_values() {
883 let test_struct = SerializableTestStruct {
884 id: 0,
885 name: String::new(),
886 value: 0.0,
887 };
888
889 let json_bytes = test_struct.to_json_bytes().unwrap();
890 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
891
892 assert_eq!(test_struct, deserialized);
893 }
894
895 #[rstest]
896 fn test_serializable_msgpack_empty_values() {
897 let test_struct = SerializableTestStruct {
898 id: 0,
899 name: String::new(),
900 value: 0.0,
901 };
902
903 let msgpack_bytes = test_struct.to_msgpack_bytes().unwrap();
904 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
905
906 assert_eq!(test_struct, deserialized);
907 }
908
909 #[derive(Deserialize)]
910 struct TestOptionalDecimalStr {
911 #[serde(deserialize_with = "deserialize_optional_decimal_str")]
912 value: Option<Decimal>,
913 }
914
915 #[derive(Deserialize)]
916 struct TestDecimalOrZero {
917 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
918 value: Decimal,
919 }
920
921 #[derive(Deserialize)]
922 struct TestOptionalDecimalOrZero {
923 #[serde(deserialize_with = "deserialize_optional_decimal_or_zero")]
924 value: Decimal,
925 }
926
927 #[derive(Serialize, Deserialize, PartialEq, Debug)]
928 struct TestDecimalRoundtrip {
929 #[serde(
930 serialize_with = "serialize_decimal_as_str",
931 deserialize_with = "deserialize_decimal_from_str"
932 )]
933 value: Decimal,
934 #[serde(
935 serialize_with = "serialize_optional_decimal_as_str",
936 deserialize_with = "super::deserialize_optional_decimal_from_str"
937 )]
938 optional_value: Option<Decimal>,
939 }
940
941 #[rstest]
942 #[case(r#"{"value":"123.45"}"#, Some(dec!(123.45)))]
943 #[case(r#"{"value":"0"}"#, None)]
944 #[case(r#"{"value":""}"#, None)]
945 fn test_deserialize_optional_decimal_str(
946 #[case] json: &str,
947 #[case] expected: Option<Decimal>,
948 ) {
949 let result: TestOptionalDecimalStr = serde_json::from_str(json).unwrap();
950 assert_eq!(result.value, expected);
951 }
952
953 #[rstest]
954 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
955 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
956 #[case(r#"{"value":""}"#, Decimal::ZERO)]
957 fn test_deserialize_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
958 let result: TestDecimalOrZero = serde_json::from_str(json).unwrap();
959 assert_eq!(result.value, expected);
960 }
961
962 #[rstest]
963 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
964 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
965 #[case(r#"{"value":null}"#, Decimal::ZERO)]
966 fn test_deserialize_optional_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
967 let result: TestOptionalDecimalOrZero = serde_json::from_str(json).unwrap();
968 assert_eq!(result.value, expected);
969 }
970
971 #[rstest]
972 fn test_decimal_serialization_roundtrip() {
973 let original = TestDecimalRoundtrip {
974 value: dec!(123.456789012345678),
975 optional_value: Some(dec!(0.000000001)),
976 };
977
978 let json = serde_json::to_string(&original).unwrap();
979
980 assert!(json.contains("\"123.456789012345678\""));
982 assert!(json.contains("\"0.000000001\""));
983
984 let deserialized: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
985 assert_eq!(original.value, deserialized.value);
986 assert_eq!(original.optional_value, deserialized.optional_value);
987 }
988
989 #[rstest]
990 fn test_decimal_optional_none_handling() {
991 let test_struct = TestDecimalRoundtrip {
992 value: dec!(42.0),
993 optional_value: None,
994 };
995
996 let json = serde_json::to_string(&test_struct).unwrap();
997 assert!(json.contains("null"));
998
999 let parsed: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1000 assert_eq!(test_struct.value, parsed.value);
1001 assert_eq!(None, parsed.optional_value);
1002 }
1003
1004 #[derive(Deserialize)]
1005 struct TestEmptyStringAsNone {
1006 #[serde(deserialize_with = "deserialize_empty_string_as_none")]
1007 value: Option<String>,
1008 }
1009
1010 #[rstest]
1011 #[case(r#"{"value":"hello"}"#, Some("hello".to_string()))]
1012 #[case(r#"{"value":""}"#, None)]
1013 #[case(r#"{"value":null}"#, None)]
1014 fn test_deserialize_empty_string_as_none(#[case] json: &str, #[case] expected: Option<String>) {
1015 let result: TestEmptyStringAsNone = serde_json::from_str(json).unwrap();
1016 assert_eq!(result.value, expected);
1017 }
1018
1019 #[derive(Deserialize)]
1020 struct TestEmptyUstrAsNone {
1021 #[serde(deserialize_with = "deserialize_empty_ustr_as_none")]
1022 value: Option<Ustr>,
1023 }
1024
1025 #[rstest]
1026 #[case(r#"{"value":"hello"}"#, Some(Ustr::from("hello")))]
1027 #[case(r#"{"value":""}"#, None)]
1028 #[case(r#"{"value":null}"#, None)]
1029 fn test_deserialize_empty_ustr_as_none(#[case] json: &str, #[case] expected: Option<Ustr>) {
1030 let result: TestEmptyUstrAsNone = serde_json::from_str(json).unwrap();
1031 assert_eq!(result.value, expected);
1032 }
1033
1034 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1035 struct TestVecDecimal {
1036 #[serde(
1037 serialize_with = "serialize_vec_decimal_as_str",
1038 deserialize_with = "deserialize_vec_decimal_from_str"
1039 )]
1040 values: Vec<Decimal>,
1041 }
1042
1043 #[rstest]
1044 fn test_vec_decimal_roundtrip() {
1045 let original = TestVecDecimal {
1046 values: vec![dec!(1.5), dec!(2.25), dec!(100.001)],
1047 };
1048
1049 let json = serde_json::to_string(&original).unwrap();
1050 assert!(json.contains("[\"1.5\",\"2.25\",\"100.001\"]"));
1051
1052 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1053 assert_eq!(original.values, parsed.values);
1054 }
1055
1056 #[rstest]
1057 fn test_vec_decimal_empty() {
1058 let original = TestVecDecimal { values: vec![] };
1059
1060 let json = serde_json::to_string(&original).unwrap();
1061 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1062 assert_eq!(original.values, parsed.values);
1063 }
1064
1065 #[derive(Deserialize)]
1066 struct TestStringToU8 {
1067 #[serde(deserialize_with = "deserialize_string_to_u8")]
1068 value: u8,
1069 }
1070
1071 #[rstest]
1072 #[case(r#"{"value":"42"}"#, 42)]
1073 #[case(r#"{"value":"0"}"#, 0)]
1074 #[case(r#"{"value":"255"}"#, 255)]
1075 #[case(r#"{"value":""}"#, 0)]
1076 fn test_deserialize_string_to_u8(#[case] json: &str, #[case] expected: u8) {
1077 let result: TestStringToU8 = serde_json::from_str(json).unwrap();
1078 assert_eq!(result.value, expected);
1079 }
1080
1081 #[rstest]
1082 #[case(r#"{"value":"256"}"#)]
1083 #[case(r#"{"value":"999"}"#)]
1084 #[case(r#"{"value":"abc"}"#)]
1085 fn test_deserialize_string_to_u8_invalid(#[case] json: &str) {
1086 let result: Result<TestStringToU8, _> = serde_json::from_str(json);
1087 assert!(result.is_err());
1088 }
1089
1090 #[derive(Deserialize)]
1091 struct TestStringToU64 {
1092 #[serde(deserialize_with = "deserialize_string_to_u64")]
1093 value: u64,
1094 }
1095
1096 #[rstest]
1097 #[case(r#"{"value":"12345678901234"}"#, 12_345_678_901_234)]
1098 #[case(r#"{"value":"0"}"#, 0)]
1099 #[case(r#"{"value":"18446744073709551615"}"#, u64::MAX)]
1100 #[case(r#"{"value":""}"#, 0)]
1101 fn test_deserialize_string_to_u64(#[case] json: &str, #[case] expected: u64) {
1102 let result: TestStringToU64 = serde_json::from_str(json).unwrap();
1103 assert_eq!(result.value, expected);
1104 }
1105
1106 #[rstest]
1107 #[case(r#"{"value":"18446744073709551616"}"#)]
1108 #[case(r#"{"value":"abc"}"#)]
1109 #[case(r#"{"value":"-1"}"#)]
1110 fn test_deserialize_string_to_u64_invalid(#[case] json: &str) {
1111 let result: Result<TestStringToU64, _> = serde_json::from_str(json);
1112 assert!(result.is_err());
1113 }
1114
1115 #[derive(Deserialize)]
1116 struct TestOptionalStringToU64 {
1117 #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
1118 value: Option<u64>,
1119 }
1120
1121 #[rstest]
1122 #[case(r#"{"value":"12345678901234"}"#, Some(12_345_678_901_234))]
1123 #[case(r#"{"value":"0"}"#, Some(0))]
1124 #[case(r#"{"value":""}"#, None)]
1125 #[case(r#"{"value":null}"#, None)]
1126 fn test_deserialize_optional_string_to_u64(#[case] json: &str, #[case] expected: Option<u64>) {
1127 let result: TestOptionalStringToU64 = serde_json::from_str(json).unwrap();
1128 assert_eq!(result.value, expected);
1129 }
1130
1131 #[rstest]
1132 #[case("123.45", dec!(123.45))]
1133 #[case("0", Decimal::ZERO)]
1134 #[case("0.0", Decimal::ZERO)]
1135 fn test_parse_decimal(#[case] input: &str, #[case] expected: Decimal) {
1136 let result = parse_decimal(input).unwrap();
1137 assert_eq!(result, expected);
1138 }
1139
1140 #[rstest]
1141 fn test_parse_decimal_invalid() {
1142 assert!(parse_decimal("invalid").is_err());
1143 assert!(parse_decimal("").is_err());
1144 }
1145
1146 #[rstest]
1147 #[case(&Some("123.45".to_string()), Some(dec!(123.45)))]
1148 #[case(&Some("0".to_string()), Some(Decimal::ZERO))]
1149 #[case(&Some(String::new()), None)]
1150 #[case(&None, None)]
1151 fn test_parse_optional_decimal(
1152 #[case] input: &Option<String>,
1153 #[case] expected: Option<Decimal>,
1154 ) {
1155 let result = parse_optional_decimal(input).unwrap();
1156 assert_eq!(result, expected);
1157 }
1158
1159 #[derive(Debug, Serialize, Deserialize, PartialEq)]
1162 struct TestFlexibleDecimal {
1163 #[serde(
1164 serialize_with = "serialize_decimal",
1165 deserialize_with = "deserialize_decimal"
1166 )]
1167 value: Decimal,
1168 #[serde(
1169 serialize_with = "serialize_optional_decimal",
1170 deserialize_with = "deserialize_optional_decimal"
1171 )]
1172 optional_value: Option<Decimal>,
1173 }
1174
1175 #[rstest]
1176 #[case(r#"{"value": 123.456, "optional_value": 789.012}"#, dec!(123.456), Some(dec!(789.012)))]
1177 #[case(r#"{"value": "123.456", "optional_value": "789.012"}"#, dec!(123.456), Some(dec!(789.012)))]
1178 #[case(r#"{"value": 100, "optional_value": null}"#, dec!(100), None)]
1179 #[case(r#"{"value": null, "optional_value": null}"#, Decimal::ZERO, None)]
1180 fn test_deserialize_flexible_decimal(
1181 #[case] json: &str,
1182 #[case] expected_value: Decimal,
1183 #[case] expected_optional: Option<Decimal>,
1184 ) {
1185 let result: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1186 assert_eq!(result.value, expected_value);
1187 assert_eq!(result.optional_value, expected_optional);
1188 }
1189
1190 #[rstest]
1191 fn test_flexible_decimal_roundtrip() {
1192 let original = TestFlexibleDecimal {
1193 value: dec!(123.456),
1194 optional_value: Some(dec!(789.012)),
1195 };
1196
1197 let json = serde_json::to_string(&original).unwrap();
1198 let deserialized: TestFlexibleDecimal = serde_json::from_str(&json).unwrap();
1199
1200 assert_eq!(original.value, deserialized.value);
1201 assert_eq!(original.optional_value, deserialized.optional_value);
1202 }
1203
1204 #[rstest]
1205 fn test_flexible_decimal_scientific_notation() {
1206 let json = r#"{"value": 0.00000001, "optional_value": 12345678.12345}"#;
1210 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1211 assert_eq!(parsed.value, dec!(0.00000001));
1212 assert_eq!(parsed.optional_value, Some(dec!(12345678.12345)));
1213 }
1214
1215 #[rstest]
1216 fn test_flexible_decimal_empty_string_optional() {
1217 let json = r#"{"value": 100, "optional_value": ""}"#;
1218 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1219 assert_eq!(parsed.value, dec!(100));
1220 assert_eq!(parsed.optional_value, None);
1221 }
1222
1223 #[derive(Debug, Deserialize)]
1226 struct TestDecimalOnly {
1227 #[serde(deserialize_with = "deserialize_decimal")]
1228 value: Decimal,
1229 }
1230
1231 #[rstest]
1232 #[case(r#"{"value": "1.5e-8"}"#, dec!(0.000000015))]
1233 #[case(r#"{"value": "1E10"}"#, dec!(10000000000))]
1234 #[case(r#"{"value": "-1.23e5"}"#, dec!(-123000))]
1235 fn test_deserialize_decimal_scientific_string(#[case] json: &str, #[case] expected: Decimal) {
1236 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1237 assert_eq!(result.value, expected);
1238 }
1239
1240 #[rstest]
1241 #[case(r#"{"value": 9223372036854775807}"#, dec!(9223372036854775807))] #[case(r#"{"value": -9223372036854775808}"#, dec!(-9223372036854775808))] #[case(r#"{"value": 0}"#, Decimal::ZERO)]
1244 fn test_deserialize_decimal_large_integers(#[case] json: &str, #[case] expected: Decimal) {
1245 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1246 assert_eq!(result.value, expected);
1247 }
1248
1249 #[rstest]
1250 #[case(r#"{"value": "-123.456789"}"#, dec!(-123.456789))]
1251 #[case(r#"{"value": -999.99}"#, dec!(-999.99))]
1252 fn test_deserialize_decimal_negative(#[case] json: &str, #[case] expected: Decimal) {
1253 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1254 assert_eq!(result.value, expected);
1255 }
1256
1257 #[rstest]
1258 #[case(r#"{"value": "123456789.123456789012345678"}"#)] fn test_deserialize_decimal_high_precision(#[case] json: &str) {
1260 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1261 assert_eq!(result.value, dec!(123456789.123456789012345678));
1262 }
1263
1264 #[rstest]
1265 #[case(
1266 r#"{"value": "1.234567890123456789012345678912345e-1"}"#,
1267 "0.1234567890123456789012345679"
1268 )]
1269 #[case(
1270 r#"{"value": "0.1234567890123456789012345678912345"}"#,
1271 "0.1234567890123456789012345679"
1272 )]
1273 #[case(
1274 r#"{"value": "999999999999999999999999999995e-29"}"#,
1275 "10.000000000000000000000000000"
1276 )]
1277 #[case(r#"{"value": "0.5e29"}"#, "50000000000000000000000000000")]
1278 #[case(r#"{"value": "-4e-29"}"#, "0.0000000000000000000000000000")]
1279 #[case(r#"{"value": "1.5e-999999"}"#, "0.0000000000000000000000000000")]
1280 #[case(r#"{"value": "9e-999999"}"#, "0.0000000000000000000000000000")]
1281 #[case(r#"{"value": "0e2000000000"}"#, "0")]
1282 fn test_deserialize_decimal_rounds_high_scale_values(
1283 #[case] json: &str,
1284 #[case] expected: &str,
1285 ) {
1286 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1288 assert_eq!(result.value.to_string(), expected);
1289 }
1290
1291 #[rstest]
1292 #[case(r#"{"value": "8e28"}"#)] #[case(r#"{"value": "1e1000000000"}"#)] #[case(r#"{"value": "not-a-number"}"#)]
1295 fn test_deserialize_decimal_rejects_unrepresentable_values(#[case] json: &str) {
1296 let result: Result<TestDecimalOnly, _> = serde_json::from_str(json);
1299 assert!(result.is_err());
1300 }
1301
1302 #[rstest]
1303 fn test_deserialize_optional_decimal_rounds_high_scale_values() {
1304 let json = r#"{"value": "51.234567890123456789012345678912345e-1"}"#;
1305 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1306 assert_eq!(
1307 result.value.map(|v| v.to_string()),
1308 Some("5.1234567890123456789012345679".into()),
1309 );
1310 }
1311
1312 #[derive(Debug, Deserialize)]
1313 struct TestOptionalDecimalOnly {
1314 #[serde(deserialize_with = "deserialize_optional_decimal")]
1315 value: Option<Decimal>,
1316 }
1317
1318 #[rstest]
1319 #[case(r#"{"value": "1.5e-8"}"#, Some(dec!(0.000000015)))]
1320 #[case(r#"{"value": null}"#, None)]
1321 #[case(r#"{"value": ""}"#, None)]
1322 #[case(r#"{"value": 42}"#, Some(dec!(42)))]
1323 #[case(r#"{"value": -100.5}"#, Some(dec!(-100.5)))]
1324 fn test_deserialize_optional_decimal_various(
1325 #[case] json: &str,
1326 #[case] expected: Option<Decimal>,
1327 ) {
1328 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1329 assert_eq!(result.value, expected);
1330 }
1331
1332 use proptest::prelude::*;
1333 use rust_decimal::prelude::ToPrimitive;
1334
1335 fn representable_decimal_strategy() -> impl Strategy<Value = Decimal> {
1336 (
1339 -79_228_162_514_264_337_593_543_950_335i128
1340 ..=79_228_162_514_264_337_593_543_950_335i128,
1341 0u32..=28u32,
1342 )
1343 .prop_map(|(mantissa, scale)| Decimal::from_i128_with_scale(mantissa, scale))
1344 }
1345
1346 fn numeric_string_strategy() -> impl Strategy<Value = String> {
1347 (
1350 proptest::bool::ANY,
1351 "[0-9]{1,30}",
1352 proptest::option::of("[0-9]{1,40}"),
1353 proptest::option::of(-40i32..=40),
1354 )
1355 .prop_map(|(negative, integer, fraction, exponent)| {
1356 let mut value = String::new();
1357 if negative {
1358 value.push('-');
1359 }
1360 value.push_str(&integer);
1361 if let Some(fraction) = fraction {
1362 value.push('.');
1363 value.push_str(&fraction);
1364 }
1365
1366 if let Some(exponent) = exponent {
1367 value.push('e');
1368 value.push_str(&exponent.to_string());
1369 }
1370 value
1371 })
1372 }
1373
1374 proptest! {
1375 #[rstest]
1376 fn prop_deserialize_decimal_roundtrips_representable_values(
1377 expected in representable_decimal_strategy()
1378 ) {
1379 let json = format!(r#"{{"value": "{expected}"}}"#);
1382 let parsed: TestDecimalOnly = serde_json::from_str(&json).unwrap();
1383 prop_assert_eq!(parsed.value, expected);
1384 }
1385
1386 #[rstest]
1387 fn prop_deserialize_decimal_total_on_arbitrary_strings(value in "\\PC{0,64}") {
1388 let json = serde_json::to_string(&serde_json::json!({"value": value})).unwrap();
1390 let _ = serde_json::from_str::<TestDecimalOnly>(&json);
1391 }
1392
1393 #[rstest]
1394 fn prop_deserialize_decimal_tracks_f64_reference(value in numeric_string_strategy()) {
1395 let json = format!(r#"{{"value": "{value}"}}"#);
1398 if let Ok(parsed) = serde_json::from_str::<TestDecimalOnly>(&json) {
1399 let reference: f64 = value.parse().unwrap();
1400 let decoded = parsed.value.to_f64().unwrap();
1401 prop_assert!(
1402 (decoded - reference).abs() <= reference.abs() * 1e-9 + 1e-27,
1403 "decoded {decoded} diverges from reference {reference} for input {value}",
1404 );
1405 }
1406 }
1407
1408 #[rstest]
1409 fn prop_deserialize_optional_decimal_matches_required(
1410 value in numeric_string_strategy()
1411 ) {
1412 let json = format!(r#"{{"value": "{value}"}}"#);
1413 let required = serde_json::from_str::<TestDecimalOnly>(&json);
1414 let optional = serde_json::from_str::<TestOptionalDecimalOnly>(&json);
1415 match (required, optional) {
1416 (Ok(required), Ok(optional)) => {
1417 prop_assert_eq!(optional.value, Some(required.value));
1418 }
1419 (Err(_), Err(_)) => {}
1420 (required, optional) => prop_assert!(
1421 false,
1422 "required and optional decoding disagree for input {}: {:?} vs {:?}",
1423 value,
1424 required.map(|r| r.value),
1425 optional.map(|o| o.value),
1426 ),
1427 }
1428 }
1429 }
1430}