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_nan() {
121 return Err(E::invalid_value(Unexpected::Float(v), &self));
122 }
123
124 if v.is_infinite() {
125 return Err(E::invalid_value(Unexpected::Float(v), &self));
126 }
127 Decimal::try_from(v).map_err(E::custom)
128 }
129
130 fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
132 Ok(Decimal::ZERO)
133 }
134
135 fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
136 Ok(Decimal::ZERO)
137 }
138}
139
140struct OptionalDecimalVisitor;
145
146impl Visitor<'_> for OptionalDecimalVisitor {
147 type Value = Option<Decimal>;
148
149 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
150 formatter.write_str("null or a decimal number as string, integer, or float")
151 }
152
153 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
156 if v.is_empty() {
157 return Ok(None);
158 }
159 DecimalVisitor.visit_str(v).map(Some)
160 }
161
162 fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
163 self.visit_str(&v)
164 }
165
166 fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
167 DecimalVisitor.visit_i64(v).map(Some)
168 }
169
170 fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
171 DecimalVisitor.visit_u64(v).map(Some)
172 }
173
174 fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
175 DecimalVisitor.visit_i128(v).map(Some)
176 }
177
178 fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
179 DecimalVisitor.visit_u128(v).map(Some)
180 }
181
182 fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
183 DecimalVisitor.visit_f64(v).map(Some)
184 }
185
186 fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
188 Ok(None)
189 }
190
191 fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
192 Ok(None)
193 }
194}
195
196fn parse_decimal_str(value: &str) -> Result<Decimal, String> {
197 let parsed = if value.contains('e') || value.contains('E') {
198 Decimal::from_scientific(value)
199 } else {
200 Decimal::from_str(value)
201 };
202
203 match parsed {
204 Ok(decimal) => Ok(decimal),
205 Err(e) => {
206 for scale in (0..=Decimal::MAX_SCALE as usize).rev() {
210 let clamped =
211 decimal_string_clamped_to_scale(value, scale).ok_or_else(|| e.to_string())?;
212
213 if let Ok(decimal) = Decimal::from_str(&clamped) {
214 return Ok(decimal);
215 }
216 }
217 Err(e.to_string())
218 }
219 }
220}
221
222fn decimal_string_clamped_to_scale(value: &str, max_scale: usize) -> Option<String> {
223 let (coefficient, exponent) = match value.find(['e', 'E']) {
224 Some(index) => {
225 let exponent = value[index + 1..].parse::<i32>().ok()?;
226 (&value[..index], exponent)
227 }
228 None => (value, 0),
229 };
230
231 let (sign, unsigned) = match coefficient.as_bytes().first()? {
232 b'+' => ("", &coefficient[1..]),
233 b'-' => ("-", &coefficient[1..]),
234 _ => ("", coefficient),
235 };
236 let (integer, fractional) = decimal_components(unsigned)?;
237 let digits = format!("{integer}{fractional}");
238 if decimal_digits_are_zero(&digits, "") {
239 return Some("0".to_string());
240 }
241
242 let point = i32::try_from(integer.len()).ok()?.checked_add(exponent)?;
243
244 if decimal_integer_digits_exceed_max(&digits, point) {
249 return None;
250 }
251
252 let (integer, fractional) = if point <= 0 {
253 let zero_count = usize::try_from(-point).ok()?.min(max_scale + 1);
256 (
257 "0".to_string(),
258 format!("{}{digits}", "0".repeat(zero_count)),
259 )
260 } else {
261 let point = usize::try_from(point).ok()?;
262 if point >= digits.len() {
263 (
264 format!("{}{}", digits, "0".repeat(point - digits.len())),
265 String::new(),
266 )
267 } else {
268 (digits[..point].to_string(), digits[point..].to_string())
269 }
270 };
271
272 let (integer, fractional) = round_decimal_components(integer, fractional, max_scale);
273 let sign = if sign == "-" && decimal_digits_are_zero(&integer, &fractional) {
274 ""
275 } else {
276 sign
277 };
278
279 if fractional.is_empty() {
280 Some(format!("{sign}{integer}"))
281 } else {
282 Some(format!("{sign}{integer}.{fractional}"))
283 }
284}
285
286fn decimal_components(value: &str) -> Option<(&str, &str)> {
287 let mut split = value.split('.');
288 let integer = split.next()?;
289 let fractional = split.next().unwrap_or("");
290 if split.next().is_some()
291 || (integer.is_empty() && fractional.is_empty())
292 || !integer.chars().all(|c| c.is_ascii_digit())
293 || !fractional.chars().all(|c| c.is_ascii_digit())
294 {
295 return None;
296 }
297 Some((integer, fractional))
298}
299
300fn decimal_integer_digits_exceed_max(digits: &str, point: i32) -> bool {
301 const DECIMAL_MAX_INTEGER_DIGITS: i32 = 29;
302
303 if point <= 0 {
304 return false;
305 }
306
307 let Some(first_non_zero) = digits.bytes().position(|digit| digit != b'0') else {
308 return false;
309 };
310 let Ok(first_non_zero) = i32::try_from(first_non_zero) else {
311 return true;
312 };
313
314 point.saturating_sub(first_non_zero) > DECIMAL_MAX_INTEGER_DIGITS
315}
316
317fn round_decimal_components(
318 mut integer: String,
319 fractional: String,
320 max_scale: usize,
321) -> (String, String) {
322 if fractional.len() <= max_scale {
323 return (integer, fractional);
324 }
325
326 let mut rounded = fractional.as_bytes()[..max_scale].to_vec();
327 if fractional.as_bytes()[max_scale] >= b'5' {
328 increment_decimal_digits(&mut integer, &mut rounded);
329 }
330
331 (
332 integer,
333 String::from_utf8(rounded).expect("decimal digits are ASCII"),
334 )
335}
336
337fn increment_decimal_digits(integer: &mut String, fractional: &mut [u8]) {
338 for digit in fractional.iter_mut().rev() {
339 if *digit < b'9' {
340 *digit += 1;
341 return;
342 }
343 *digit = b'0';
344 }
345
346 let mut integer_digits = integer.as_bytes().to_vec();
347 for digit in integer_digits.iter_mut().rev() {
348 if *digit < b'9' {
349 *digit += 1;
350 *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
351 return;
352 }
353 *digit = b'0';
354 }
355 integer_digits.insert(0, b'1');
356 *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
357}
358
359fn decimal_digits_are_zero(integer: &str, fractional: &str) -> bool {
360 integer
361 .bytes()
362 .chain(fractional.bytes())
363 .all(|digit| digit == b'0')
364}
365
366pub trait Serializable: Serialize + for<'de> Deserialize<'de> {
368 fn from_json_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
374 serde_json::from_slice(data)
375 }
376
377 fn to_json_bytes(&self) -> Result<Bytes, serde_json::Error> {
383 serde_json::to_vec(self).map(Bytes::from)
384 }
385}
386
387pub use self::msgpack::{FromMsgPack, MsgPackSerializable, ToMsgPack};
388
389pub mod msgpack {
394 use bytes::Bytes;
395 use serde::{Deserialize, Serialize};
396
397 use super::Serializable;
398
399 pub trait FromMsgPack: for<'de> Deserialize<'de> + Sized {
401 fn from_msgpack_bytes(data: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
407 rmp_serde::from_slice(data)
408 }
409 }
410
411 pub trait ToMsgPack: Serialize {
413 fn to_msgpack_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
419 rmp_serde::to_vec_named(self).map(Bytes::from)
420 }
421 }
422
423 pub trait MsgPackSerializable: Serializable + FromMsgPack + ToMsgPack {}
427
428 impl<T> FromMsgPack for T where T: Serializable {}
429
430 impl<T> ToMsgPack for T where T: Serializable {}
431
432 impl<T> MsgPackSerializable for T where T: Serializable {}
433}
434
435#[must_use]
439pub const fn default_true() -> bool {
440 true
441}
442
443#[must_use]
447pub const fn default_false() -> bool {
448 false
449}
450
451pub fn deserialize_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
474where
475 D: Deserializer<'de>,
476{
477 deserializer.deserialize_any(DecimalVisitor)
478}
479
480pub fn deserialize_optional_decimal<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
504where
505 D: Deserializer<'de>,
506{
507 deserializer.deserialize_any(OptionalDecimalVisitor)
510}
511
512pub fn serialize_decimal<S: Serializer>(d: &Decimal, s: S) -> Result<S::Ok, S::Error> {
520 rust_decimal::serde::float::serialize(d, s)
521}
522
523pub fn serialize_optional_decimal<S: Serializer>(
529 d: &Option<Decimal>,
530 s: S,
531) -> Result<S::Ok, S::Error> {
532 match d {
533 Some(decimal) => rust_decimal::serde::float::serialize(decimal, s),
534 None => s.serialize_none(),
535 }
536}
537
538pub fn deserialize_decimal_from_str<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
547where
548 D: Deserializer<'de>,
549{
550 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
551 Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
552}
553
554pub fn deserialize_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
562where
563 D: Deserializer<'de>,
564{
565 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
566 if s.is_empty() || s == "0" {
567 Ok(Decimal::ZERO)
568 } else {
569 Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
570 }
571}
572
573pub fn deserialize_optional_decimal_str<'de, D>(
583 deserializer: D,
584) -> Result<Option<Decimal>, D::Error>
585where
586 D: Deserializer<'de>,
587{
588 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
589 if s.is_empty() || s == "0" {
590 Ok(None)
591 } else {
592 Decimal::from_str(s.as_ref())
593 .map(Some)
594 .map_err(D::Error::custom)
595 }
596}
597
598pub fn deserialize_optional_decimal_from_str<'de, D>(
607 deserializer: D,
608) -> Result<Option<Decimal>, D::Error>
609where
610 D: Deserializer<'de>,
611{
612 let opt = Option::<String>::deserialize(deserializer)?;
613 match opt {
614 Some(s) if !s.is_empty() => Decimal::from_str(&s).map(Some).map_err(D::Error::custom),
615 _ => Ok(None),
616 }
617}
618
619pub fn deserialize_optional_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
627where
628 D: Deserializer<'de>,
629{
630 let opt: Option<String> = Deserialize::deserialize(deserializer)?;
631 match opt {
632 None => Ok(Decimal::ZERO),
633 Some(s) if s.is_empty() || s == "0" => Ok(Decimal::ZERO),
634 Some(s) => Decimal::from_str(&s).map_err(D::Error::custom),
635 }
636}
637
638pub fn deserialize_vec_decimal_from_str<'de, D>(deserializer: D) -> Result<Vec<Decimal>, D::Error>
644where
645 D: Deserializer<'de>,
646{
647 let strings = Vec::<String>::deserialize(deserializer)?;
648 strings
649 .into_iter()
650 .map(|s| Decimal::from_str(&s).map_err(D::Error::custom))
651 .collect()
652}
653
654pub fn serialize_decimal_as_str<S>(decimal: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
660where
661 S: Serializer,
662{
663 serializer.serialize_str(&decimal.to_string())
664}
665
666pub fn serialize_optional_decimal_as_str<S>(
672 decimal: &Option<Decimal>,
673 serializer: S,
674) -> Result<S::Ok, S::Error>
675where
676 S: Serializer,
677{
678 match decimal {
679 Some(d) => serializer.serialize_str(&d.to_string()),
680 None => serializer.serialize_none(),
681 }
682}
683
684pub fn serialize_vec_decimal_as_str<S>(
690 decimals: &Vec<Decimal>,
691 serializer: S,
692) -> Result<S::Ok, S::Error>
693where
694 S: Serializer,
695{
696 let mut seq = serializer.serialize_seq(Some(decimals.len()))?;
697 for decimal in decimals {
698 seq.serialize_element(&decimal.to_string())?;
699 }
700 seq.end()
701}
702
703pub fn parse_decimal(s: &str) -> anyhow::Result<Decimal> {
709 Decimal::from_str(s).map_err(|e| anyhow::anyhow!("Failed to parse decimal from '{s}': {e}"))
710}
711
712pub fn parse_optional_decimal(s: &Option<String>) -> anyhow::Result<Option<Decimal>> {
718 match s {
719 None => Ok(None),
720 Some(s) if s.is_empty() => Ok(None),
721 Some(s) => parse_decimal(s).map(Some),
722 }
723}
724
725pub fn deserialize_empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
737where
738 D: Deserializer<'de>,
739{
740 let opt = Option::<String>::deserialize(deserializer)?;
741 Ok(opt.filter(|s| !s.is_empty()))
742}
743
744pub fn deserialize_empty_ustr_as_none<'de, D>(deserializer: D) -> Result<Option<Ustr>, D::Error>
750where
751 D: Deserializer<'de>,
752{
753 let opt = Option::<Ustr>::deserialize(deserializer)?;
754 Ok(opt.filter(|s| !s.is_empty()))
755}
756
757pub fn deserialize_string_to_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
765where
766 D: Deserializer<'de>,
767{
768 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
769 if s.is_empty() {
770 return Ok(0);
771 }
772 s.as_ref().parse::<u8>().map_err(D::Error::custom)
773}
774
775pub fn deserialize_string_to_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
783where
784 D: Deserializer<'de>,
785{
786 let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
787 if s.is_empty() {
788 Ok(0)
789 } else {
790 s.as_ref().parse::<u64>().map_err(D::Error::custom)
791 }
792}
793
794pub fn deserialize_optional_string_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
802where
803 D: Deserializer<'de>,
804{
805 let s: Option<String> = Option::deserialize(deserializer)?;
806 match s {
807 Some(s) if s.is_empty() => Ok(None),
808 Some(s) => s.parse().map(Some).map_err(D::Error::custom),
809 None => Ok(None),
810 }
811}
812
813#[cfg(test)]
814mod tests {
815 use rstest::*;
816 use rust_decimal::Decimal;
817 use rust_decimal_macros::dec;
818 use serde::{Deserialize, Serialize};
819 use ustr::Ustr;
820
821 use super::{
822 Serializable, deserialize_decimal, deserialize_decimal_from_str,
823 deserialize_decimal_or_zero, deserialize_empty_string_as_none,
824 deserialize_empty_ustr_as_none, deserialize_optional_decimal,
825 deserialize_optional_decimal_or_zero, deserialize_optional_decimal_str,
826 deserialize_optional_string_to_u64, deserialize_string_to_u8, deserialize_string_to_u64,
827 deserialize_vec_decimal_from_str,
828 msgpack::{FromMsgPack, ToMsgPack},
829 parse_decimal, parse_optional_decimal, serialize_decimal, serialize_decimal_as_str,
830 serialize_optional_decimal, serialize_optional_decimal_as_str,
831 serialize_vec_decimal_as_str,
832 };
833
834 #[derive(Serialize, Deserialize, PartialEq, Debug)]
835 struct SerializableTestStruct {
836 id: u32,
837 name: String,
838 value: f64,
839 }
840
841 impl Serializable for SerializableTestStruct {}
842
843 #[rstest]
844 fn test_serializable_json_roundtrip() {
845 let original = SerializableTestStruct {
846 id: 42,
847 name: "test".to_string(),
848 value: std::f64::consts::PI,
849 };
850
851 let json_bytes = original.to_json_bytes().unwrap();
852 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
853
854 assert_eq!(original, deserialized);
855 }
856
857 #[rstest]
858 fn test_serializable_msgpack_roundtrip() {
859 let original = SerializableTestStruct {
860 id: 123,
861 name: "msgpack_test".to_string(),
862 value: std::f64::consts::E,
863 };
864
865 let msgpack_bytes = original.to_msgpack_bytes().unwrap();
866 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
867
868 assert_eq!(original, deserialized);
869 }
870
871 #[rstest]
872 fn test_serializable_json_invalid_data() {
873 let invalid_json = b"invalid json data";
874 let result = SerializableTestStruct::from_json_bytes(invalid_json);
875 assert!(result.is_err());
876 }
877
878 #[rstest]
879 fn test_serializable_msgpack_invalid_data() {
880 let invalid_msgpack = b"invalid msgpack data";
881 let result = SerializableTestStruct::from_msgpack_bytes(invalid_msgpack);
882 assert!(result.is_err());
883 }
884
885 #[rstest]
886 fn test_serializable_json_empty_values() {
887 let test_struct = SerializableTestStruct {
888 id: 0,
889 name: String::new(),
890 value: 0.0,
891 };
892
893 let json_bytes = test_struct.to_json_bytes().unwrap();
894 let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
895
896 assert_eq!(test_struct, deserialized);
897 }
898
899 #[rstest]
900 fn test_serializable_msgpack_empty_values() {
901 let test_struct = SerializableTestStruct {
902 id: 0,
903 name: String::new(),
904 value: 0.0,
905 };
906
907 let msgpack_bytes = test_struct.to_msgpack_bytes().unwrap();
908 let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
909
910 assert_eq!(test_struct, deserialized);
911 }
912
913 #[derive(Deserialize)]
914 struct TestOptionalDecimalStr {
915 #[serde(deserialize_with = "deserialize_optional_decimal_str")]
916 value: Option<Decimal>,
917 }
918
919 #[derive(Deserialize)]
920 struct TestDecimalOrZero {
921 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
922 value: Decimal,
923 }
924
925 #[derive(Deserialize)]
926 struct TestOptionalDecimalOrZero {
927 #[serde(deserialize_with = "deserialize_optional_decimal_or_zero")]
928 value: Decimal,
929 }
930
931 #[derive(Serialize, Deserialize, PartialEq, Debug)]
932 struct TestDecimalRoundtrip {
933 #[serde(
934 serialize_with = "serialize_decimal_as_str",
935 deserialize_with = "deserialize_decimal_from_str"
936 )]
937 value: Decimal,
938 #[serde(
939 serialize_with = "serialize_optional_decimal_as_str",
940 deserialize_with = "super::deserialize_optional_decimal_from_str"
941 )]
942 optional_value: Option<Decimal>,
943 }
944
945 #[rstest]
946 #[case(r#"{"value":"123.45"}"#, Some(dec!(123.45)))]
947 #[case(r#"{"value":"0"}"#, None)]
948 #[case(r#"{"value":""}"#, None)]
949 fn test_deserialize_optional_decimal_str(
950 #[case] json: &str,
951 #[case] expected: Option<Decimal>,
952 ) {
953 let result: TestOptionalDecimalStr = serde_json::from_str(json).unwrap();
954 assert_eq!(result.value, expected);
955 }
956
957 #[rstest]
958 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
959 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
960 #[case(r#"{"value":""}"#, Decimal::ZERO)]
961 fn test_deserialize_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
962 let result: TestDecimalOrZero = serde_json::from_str(json).unwrap();
963 assert_eq!(result.value, expected);
964 }
965
966 #[rstest]
967 #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
968 #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
969 #[case(r#"{"value":null}"#, Decimal::ZERO)]
970 fn test_deserialize_optional_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
971 let result: TestOptionalDecimalOrZero = serde_json::from_str(json).unwrap();
972 assert_eq!(result.value, expected);
973 }
974
975 #[rstest]
976 fn test_decimal_serialization_roundtrip() {
977 let original = TestDecimalRoundtrip {
978 value: dec!(123.456789012345678),
979 optional_value: Some(dec!(0.000000001)),
980 };
981
982 let json = serde_json::to_string(&original).unwrap();
983
984 assert!(json.contains("\"123.456789012345678\""));
986 assert!(json.contains("\"0.000000001\""));
987
988 let deserialized: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
989 assert_eq!(original.value, deserialized.value);
990 assert_eq!(original.optional_value, deserialized.optional_value);
991 }
992
993 #[rstest]
994 fn test_decimal_optional_none_handling() {
995 let test_struct = TestDecimalRoundtrip {
996 value: dec!(42.0),
997 optional_value: None,
998 };
999
1000 let json = serde_json::to_string(&test_struct).unwrap();
1001 assert!(json.contains("null"));
1002
1003 let parsed: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1004 assert_eq!(test_struct.value, parsed.value);
1005 assert_eq!(None, parsed.optional_value);
1006 }
1007
1008 #[derive(Deserialize)]
1009 struct TestEmptyStringAsNone {
1010 #[serde(deserialize_with = "deserialize_empty_string_as_none")]
1011 value: Option<String>,
1012 }
1013
1014 #[rstest]
1015 #[case(r#"{"value":"hello"}"#, Some("hello".to_string()))]
1016 #[case(r#"{"value":""}"#, None)]
1017 #[case(r#"{"value":null}"#, None)]
1018 fn test_deserialize_empty_string_as_none(#[case] json: &str, #[case] expected: Option<String>) {
1019 let result: TestEmptyStringAsNone = serde_json::from_str(json).unwrap();
1020 assert_eq!(result.value, expected);
1021 }
1022
1023 #[derive(Deserialize)]
1024 struct TestEmptyUstrAsNone {
1025 #[serde(deserialize_with = "deserialize_empty_ustr_as_none")]
1026 value: Option<Ustr>,
1027 }
1028
1029 #[rstest]
1030 #[case(r#"{"value":"hello"}"#, Some(Ustr::from("hello")))]
1031 #[case(r#"{"value":""}"#, None)]
1032 #[case(r#"{"value":null}"#, None)]
1033 fn test_deserialize_empty_ustr_as_none(#[case] json: &str, #[case] expected: Option<Ustr>) {
1034 let result: TestEmptyUstrAsNone = serde_json::from_str(json).unwrap();
1035 assert_eq!(result.value, expected);
1036 }
1037
1038 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1039 struct TestVecDecimal {
1040 #[serde(
1041 serialize_with = "serialize_vec_decimal_as_str",
1042 deserialize_with = "deserialize_vec_decimal_from_str"
1043 )]
1044 values: Vec<Decimal>,
1045 }
1046
1047 #[rstest]
1048 fn test_vec_decimal_roundtrip() {
1049 let original = TestVecDecimal {
1050 values: vec![dec!(1.5), dec!(2.25), dec!(100.001)],
1051 };
1052
1053 let json = serde_json::to_string(&original).unwrap();
1054 assert!(json.contains("[\"1.5\",\"2.25\",\"100.001\"]"));
1055
1056 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1057 assert_eq!(original.values, parsed.values);
1058 }
1059
1060 #[rstest]
1061 fn test_vec_decimal_empty() {
1062 let original = TestVecDecimal { values: vec![] };
1063
1064 let json = serde_json::to_string(&original).unwrap();
1065 let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1066 assert_eq!(original.values, parsed.values);
1067 }
1068
1069 #[derive(Deserialize)]
1070 struct TestStringToU8 {
1071 #[serde(deserialize_with = "deserialize_string_to_u8")]
1072 value: u8,
1073 }
1074
1075 #[rstest]
1076 #[case(r#"{"value":"42"}"#, 42)]
1077 #[case(r#"{"value":"0"}"#, 0)]
1078 #[case(r#"{"value":"255"}"#, 255)]
1079 #[case(r#"{"value":""}"#, 0)]
1080 fn test_deserialize_string_to_u8(#[case] json: &str, #[case] expected: u8) {
1081 let result: TestStringToU8 = serde_json::from_str(json).unwrap();
1082 assert_eq!(result.value, expected);
1083 }
1084
1085 #[rstest]
1086 #[case(r#"{"value":"256"}"#)]
1087 #[case(r#"{"value":"999"}"#)]
1088 #[case(r#"{"value":"abc"}"#)]
1089 fn test_deserialize_string_to_u8_invalid(#[case] json: &str) {
1090 let result: Result<TestStringToU8, _> = serde_json::from_str(json);
1091 assert!(result.is_err());
1092 }
1093
1094 #[derive(Deserialize)]
1095 struct TestStringToU64 {
1096 #[serde(deserialize_with = "deserialize_string_to_u64")]
1097 value: u64,
1098 }
1099
1100 #[rstest]
1101 #[case(r#"{"value":"12345678901234"}"#, 12_345_678_901_234)]
1102 #[case(r#"{"value":"0"}"#, 0)]
1103 #[case(r#"{"value":"18446744073709551615"}"#, u64::MAX)]
1104 #[case(r#"{"value":""}"#, 0)]
1105 fn test_deserialize_string_to_u64(#[case] json: &str, #[case] expected: u64) {
1106 let result: TestStringToU64 = serde_json::from_str(json).unwrap();
1107 assert_eq!(result.value, expected);
1108 }
1109
1110 #[rstest]
1111 #[case(r#"{"value":"18446744073709551616"}"#)]
1112 #[case(r#"{"value":"abc"}"#)]
1113 #[case(r#"{"value":"-1"}"#)]
1114 fn test_deserialize_string_to_u64_invalid(#[case] json: &str) {
1115 let result: Result<TestStringToU64, _> = serde_json::from_str(json);
1116 assert!(result.is_err());
1117 }
1118
1119 #[derive(Deserialize)]
1120 struct TestOptionalStringToU64 {
1121 #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
1122 value: Option<u64>,
1123 }
1124
1125 #[rstest]
1126 #[case(r#"{"value":"12345678901234"}"#, Some(12_345_678_901_234))]
1127 #[case(r#"{"value":"0"}"#, Some(0))]
1128 #[case(r#"{"value":""}"#, None)]
1129 #[case(r#"{"value":null}"#, None)]
1130 fn test_deserialize_optional_string_to_u64(#[case] json: &str, #[case] expected: Option<u64>) {
1131 let result: TestOptionalStringToU64 = serde_json::from_str(json).unwrap();
1132 assert_eq!(result.value, expected);
1133 }
1134
1135 #[rstest]
1136 #[case("123.45", dec!(123.45))]
1137 #[case("0", Decimal::ZERO)]
1138 #[case("0.0", Decimal::ZERO)]
1139 fn test_parse_decimal(#[case] input: &str, #[case] expected: Decimal) {
1140 let result = parse_decimal(input).unwrap();
1141 assert_eq!(result, expected);
1142 }
1143
1144 #[rstest]
1145 fn test_parse_decimal_invalid() {
1146 assert!(parse_decimal("invalid").is_err());
1147 assert!(parse_decimal("").is_err());
1148 }
1149
1150 #[rstest]
1151 #[case(&Some("123.45".to_string()), Some(dec!(123.45)))]
1152 #[case(&Some("0".to_string()), Some(Decimal::ZERO))]
1153 #[case(&Some(String::new()), None)]
1154 #[case(&None, None)]
1155 fn test_parse_optional_decimal(
1156 #[case] input: &Option<String>,
1157 #[case] expected: Option<Decimal>,
1158 ) {
1159 let result = parse_optional_decimal(input).unwrap();
1160 assert_eq!(result, expected);
1161 }
1162
1163 #[derive(Debug, Serialize, Deserialize, PartialEq)]
1166 struct TestFlexibleDecimal {
1167 #[serde(
1168 serialize_with = "serialize_decimal",
1169 deserialize_with = "deserialize_decimal"
1170 )]
1171 value: Decimal,
1172 #[serde(
1173 serialize_with = "serialize_optional_decimal",
1174 deserialize_with = "deserialize_optional_decimal"
1175 )]
1176 optional_value: Option<Decimal>,
1177 }
1178
1179 #[rstest]
1180 #[case(r#"{"value": 123.456, "optional_value": 789.012}"#, dec!(123.456), Some(dec!(789.012)))]
1181 #[case(r#"{"value": "123.456", "optional_value": "789.012"}"#, dec!(123.456), Some(dec!(789.012)))]
1182 #[case(r#"{"value": 100, "optional_value": null}"#, dec!(100), None)]
1183 #[case(r#"{"value": null, "optional_value": null}"#, Decimal::ZERO, None)]
1184 fn test_deserialize_flexible_decimal(
1185 #[case] json: &str,
1186 #[case] expected_value: Decimal,
1187 #[case] expected_optional: Option<Decimal>,
1188 ) {
1189 let result: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1190 assert_eq!(result.value, expected_value);
1191 assert_eq!(result.optional_value, expected_optional);
1192 }
1193
1194 #[rstest]
1195 fn test_flexible_decimal_roundtrip() {
1196 let original = TestFlexibleDecimal {
1197 value: dec!(123.456),
1198 optional_value: Some(dec!(789.012)),
1199 };
1200
1201 let json = serde_json::to_string(&original).unwrap();
1202 let deserialized: TestFlexibleDecimal = serde_json::from_str(&json).unwrap();
1203
1204 assert_eq!(original.value, deserialized.value);
1205 assert_eq!(original.optional_value, deserialized.optional_value);
1206 }
1207
1208 #[rstest]
1209 fn test_flexible_decimal_scientific_notation() {
1210 let json = r#"{"value": 0.00000001, "optional_value": 12345678.12345}"#;
1214 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1215 assert_eq!(parsed.value, dec!(0.00000001));
1216 assert_eq!(parsed.optional_value, Some(dec!(12345678.12345)));
1217 }
1218
1219 #[rstest]
1220 fn test_flexible_decimal_empty_string_optional() {
1221 let json = r#"{"value": 100, "optional_value": ""}"#;
1222 let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1223 assert_eq!(parsed.value, dec!(100));
1224 assert_eq!(parsed.optional_value, None);
1225 }
1226
1227 #[derive(Debug, Deserialize)]
1230 struct TestDecimalOnly {
1231 #[serde(deserialize_with = "deserialize_decimal")]
1232 value: Decimal,
1233 }
1234
1235 #[rstest]
1236 #[case(r#"{"value": "1.5e-8"}"#, dec!(0.000000015))]
1237 #[case(r#"{"value": "1E10"}"#, dec!(10000000000))]
1238 #[case(r#"{"value": "-1.23e5"}"#, dec!(-123000))]
1239 fn test_deserialize_decimal_scientific_string(#[case] json: &str, #[case] expected: Decimal) {
1240 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1241 assert_eq!(result.value, expected);
1242 }
1243
1244 #[rstest]
1245 #[case(r#"{"value": 9223372036854775807}"#, dec!(9223372036854775807))] #[case(r#"{"value": -9223372036854775808}"#, dec!(-9223372036854775808))] #[case(r#"{"value": 0}"#, Decimal::ZERO)]
1248 fn test_deserialize_decimal_large_integers(#[case] json: &str, #[case] expected: Decimal) {
1249 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1250 assert_eq!(result.value, expected);
1251 }
1252
1253 #[rstest]
1254 #[case(r#"{"value": "-123.456789"}"#, dec!(-123.456789))]
1255 #[case(r#"{"value": -999.99}"#, dec!(-999.99))]
1256 fn test_deserialize_decimal_negative(#[case] json: &str, #[case] expected: Decimal) {
1257 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1258 assert_eq!(result.value, expected);
1259 }
1260
1261 #[rstest]
1262 #[case(r#"{"value": "123456789.123456789012345678"}"#)] fn test_deserialize_decimal_high_precision(#[case] json: &str) {
1264 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1265 assert_eq!(result.value, dec!(123456789.123456789012345678));
1266 }
1267
1268 #[rstest]
1269 #[case(
1270 r#"{"value": "1.234567890123456789012345678912345e-1"}"#,
1271 "0.1234567890123456789012345679"
1272 )]
1273 #[case(
1274 r#"{"value": "0.1234567890123456789012345678912345"}"#,
1275 "0.1234567890123456789012345679"
1276 )]
1277 #[case(
1278 r#"{"value": "999999999999999999999999999995e-29"}"#,
1279 "10.000000000000000000000000000"
1280 )]
1281 #[case(r#"{"value": "0.5e29"}"#, "50000000000000000000000000000")]
1282 #[case(r#"{"value": "-4e-29"}"#, "0.0000000000000000000000000000")]
1283 #[case(r#"{"value": "1.5e-999999"}"#, "0.0000000000000000000000000000")]
1284 #[case(r#"{"value": "9e-999999"}"#, "0.0000000000000000000000000000")]
1285 #[case(r#"{"value": "0e2000000000"}"#, "0")]
1286 fn test_deserialize_decimal_rounds_high_scale_values(
1287 #[case] json: &str,
1288 #[case] expected: &str,
1289 ) {
1290 let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1292 assert_eq!(result.value.to_string(), expected);
1293 }
1294
1295 #[rstest]
1296 #[case(r#"{"value": "8e28"}"#)] #[case(r#"{"value": "1e1000000000"}"#)] #[case(r#"{"value": "not-a-number"}"#)]
1299 fn test_deserialize_decimal_rejects_unrepresentable_values(#[case] json: &str) {
1300 let result: Result<TestDecimalOnly, _> = serde_json::from_str(json);
1303 assert!(result.is_err());
1304 }
1305
1306 #[rstest]
1307 fn test_deserialize_optional_decimal_rounds_high_scale_values() {
1308 let json = r#"{"value": "51.234567890123456789012345678912345e-1"}"#;
1309 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1310 assert_eq!(
1311 result.value.map(|v| v.to_string()),
1312 Some("5.1234567890123456789012345679".into()),
1313 );
1314 }
1315
1316 #[derive(Debug, Deserialize)]
1317 struct TestOptionalDecimalOnly {
1318 #[serde(deserialize_with = "deserialize_optional_decimal")]
1319 value: Option<Decimal>,
1320 }
1321
1322 #[rstest]
1323 #[case(r#"{"value": "1.5e-8"}"#, Some(dec!(0.000000015)))]
1324 #[case(r#"{"value": null}"#, None)]
1325 #[case(r#"{"value": ""}"#, None)]
1326 #[case(r#"{"value": 42}"#, Some(dec!(42)))]
1327 #[case(r#"{"value": -100.5}"#, Some(dec!(-100.5)))]
1328 fn test_deserialize_optional_decimal_various(
1329 #[case] json: &str,
1330 #[case] expected: Option<Decimal>,
1331 ) {
1332 let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1333 assert_eq!(result.value, expected);
1334 }
1335
1336 use proptest::prelude::*;
1337 use rust_decimal::prelude::ToPrimitive;
1338
1339 fn representable_decimal_strategy() -> impl Strategy<Value = Decimal> {
1340 (
1343 -79_228_162_514_264_337_593_543_950_335i128
1344 ..=79_228_162_514_264_337_593_543_950_335i128,
1345 0u32..=28u32,
1346 )
1347 .prop_map(|(mantissa, scale)| Decimal::from_i128_with_scale(mantissa, scale))
1348 }
1349
1350 fn numeric_string_strategy() -> impl Strategy<Value = String> {
1351 (
1354 proptest::bool::ANY,
1355 "[0-9]{1,30}",
1356 proptest::option::of("[0-9]{1,40}"),
1357 proptest::option::of(-40i32..=40),
1358 )
1359 .prop_map(|(negative, integer, fraction, exponent)| {
1360 let mut value = String::new();
1361 if negative {
1362 value.push('-');
1363 }
1364 value.push_str(&integer);
1365 if let Some(fraction) = fraction {
1366 value.push('.');
1367 value.push_str(&fraction);
1368 }
1369
1370 if let Some(exponent) = exponent {
1371 value.push('e');
1372 value.push_str(&exponent.to_string());
1373 }
1374 value
1375 })
1376 }
1377
1378 proptest! {
1379 #[rstest]
1380 fn prop_deserialize_decimal_roundtrips_representable_values(
1381 expected in representable_decimal_strategy()
1382 ) {
1383 let json = format!(r#"{{"value": "{expected}"}}"#);
1386 let parsed: TestDecimalOnly = serde_json::from_str(&json).unwrap();
1387 prop_assert_eq!(parsed.value, expected);
1388 }
1389
1390 #[rstest]
1391 fn prop_deserialize_decimal_total_on_arbitrary_strings(value in "\\PC{0,64}") {
1392 let json = serde_json::to_string(&serde_json::json!({"value": value})).unwrap();
1394 let _ = serde_json::from_str::<TestDecimalOnly>(&json);
1395 }
1396
1397 #[rstest]
1398 fn prop_deserialize_decimal_tracks_f64_reference(value in numeric_string_strategy()) {
1399 let json = format!(r#"{{"value": "{value}"}}"#);
1402 if let Ok(parsed) = serde_json::from_str::<TestDecimalOnly>(&json) {
1403 let reference: f64 = value.parse().unwrap();
1404 let decoded = parsed.value.to_f64().unwrap();
1405 prop_assert!(
1406 (decoded - reference).abs() <= reference.abs() * 1e-9 + 1e-27,
1407 "decoded {decoded} diverges from reference {reference} for input {value}",
1408 );
1409 }
1410 }
1411
1412 #[rstest]
1413 fn prop_deserialize_optional_decimal_matches_required(
1414 value in numeric_string_strategy()
1415 ) {
1416 let json = format!(r#"{{"value": "{value}"}}"#);
1417 let required = serde_json::from_str::<TestDecimalOnly>(&json);
1418 let optional = serde_json::from_str::<TestOptionalDecimalOnly>(&json);
1419 match (required, optional) {
1420 (Ok(required), Ok(optional)) => {
1421 prop_assert_eq!(optional.value, Some(required.value));
1422 }
1423 (Err(_), Err(_)) => {}
1424 (required, optional) => prop_assert!(
1425 false,
1426 "required and optional decoding disagree for input {}: {:?} vs {:?}",
1427 value,
1428 required.map(|r| r.value),
1429 optional.map(|o| o.value),
1430 ),
1431 }
1432 }
1433 }
1434}