serde_decimal/
nullable_arbitrary_precision.rs

1//! Serialization and deserialization of required but nullable decimals with arbitrary precision.
2
3use std::fmt;
4
5use rust_decimal::Decimal;
6use serde_core::{de, Deserialize, Deserializer, Serializer};
7
8struct OptionDecimalVisitor;
9
10impl<'de> de::Visitor<'de> for OptionDecimalVisitor {
11    type Value = Option<Decimal>;
12
13    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
14        formatter.write_str("a Decimal type representing a fixed-point number")
15    }
16
17    fn visit_none<E>(self) -> Result<Option<Decimal>, E>
18    where
19        E: de::Error,
20    {
21        Ok(None)
22    }
23
24    fn visit_some<D>(self, d: D) -> Result<Option<Decimal>, D::Error>
25    where
26        D: Deserializer<'de>,
27    {
28        <Decimal as Deserialize>::deserialize(d).map(Some)
29    }
30
31    fn visit_unit<E>(self) -> Result<Self::Value, E>
32    where
33        E: de::Error,
34    {
35        Ok(None)
36    }
37}
38
39/// Nullable arbitrary-precision decimal deserializer.
40///
41/// See [module docs](self) for more.
42pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<rust_decimal::Decimal>, D::Error>
43where
44    D: Deserializer<'de>,
45{
46    deserializer.deserialize_option(OptionDecimalVisitor)
47}
48
49/// Nullable arbitrary-precision decimal serializer.
50///
51/// See [module docs](self) for more.
52pub fn serialize<S>(value: &Option<rust_decimal::Decimal>, serializer: S) -> Result<S::Ok, S::Error>
53where
54    S: Serializer,
55{
56    rust_decimal::serde::arbitrary_precision_option::serialize(value, serializer)
57}
58
59#[cfg(test)]
60mod tests {
61    use rust_decimal_macros::dec;
62
63    #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
64    struct Foo {
65        #[serde(with = "crate::nullable_arbitrary_precision")]
66        foo: Option<rust_decimal::Decimal>,
67    }
68
69    #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
70    struct Bar {
71        #[serde(flatten)]
72        foo: Foo,
73    }
74
75    #[test]
76    fn foo_serialize_some() {
77        let serialized = serde_json::to_string(&Foo {
78            foo: Some(dec!(0.1)),
79        })
80        .unwrap();
81        assert_eq!(serialized, r#"{"foo":0.1}"#);
82    }
83
84    #[test]
85    fn foo_serialize_none() {
86        let serialized = serde_json::to_string(&Foo { foo: None }).unwrap();
87        assert_eq!(serialized, r#"{"foo":null}"#);
88    }
89
90    #[test]
91    fn foo_deserialize_some() {
92        let deserialized: Foo = serde_json::from_str(r#"{"foo":0.1}"#).unwrap();
93        assert!(matches!(deserialized, Foo { foo: Some(_) }));
94    }
95
96    #[test]
97    #[should_panic]
98    fn foo_deserialize_missing() {
99        serde_json::from_str::<Foo>(r#"{}"#).unwrap();
100    }
101
102    #[test]
103    fn foo_deserialize_null() {
104        let deserialized: Foo = serde_json::from_str(r#"{"foo":null}"#).unwrap();
105        assert!(matches!(deserialized, Foo { foo: None }));
106    }
107
108    #[test]
109    fn bar_serialize_some() {
110        let serialized = serde_json::to_string(&Bar {
111            foo: Foo {
112                foo: Some(dec!(0.1)),
113            },
114        })
115        .unwrap();
116        assert_eq!(serialized, r#"{"foo":0.1}"#);
117    }
118
119    #[test]
120    fn bar_serialize_none() {
121        let serialized = serde_json::to_string(&Bar {
122            foo: Foo { foo: None },
123        })
124        .unwrap();
125        assert_eq!(serialized, r#"{"foo":null}"#);
126    }
127
128    #[test]
129    fn bar_deserialize_some() {
130        let deserialized: Bar = serde_json::from_str(r#"{"foo":0.1}"#).unwrap();
131        assert!(matches!(
132            deserialized,
133            Bar {
134                foo: Foo { foo: Some(_) }
135            }
136        ));
137    }
138
139    #[test]
140    #[should_panic]
141    fn bar_deserialize_missing() {
142        serde_json::from_str::<Bar>(r#"{}"#).unwrap();
143    }
144
145    #[test]
146    fn bar_deserialize_null() {
147        let deserialized: Bar = serde_json::from_str(r#"{"foo":null}"#).unwrap();
148        assert!(matches!(
149            deserialized,
150            Bar {
151                foo: Foo { foo: None }
152            }
153        ));
154    }
155}