bigdecimal/
impl_serde.rs

1//!
2//! Support for serde implementations
3//!
4use crate::*;
5use serde_crate::{self as serde, de, ser, Serialize, Deserialize};
6
7// const SERDE_SCALE_LIMIT: usize = = ${RUST_BIGDECIMAL_SERDE_SCALE_LIMIT} or  150_000;
8#[cfg(feature = "serde_json")]
9include!(concat!(env!("OUT_DIR"), "/serde_scale_limit.rs"));
10
11impl ser::Serialize for BigDecimal {
12    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
13    where
14        S: ser::Serializer,
15    {
16        serializer.collect_str(&self)
17    }
18}
19
20/// Used by SerDe to construct a BigDecimal
21struct BigDecimalVisitor;
22
23impl<'de> de::Visitor<'de> for BigDecimalVisitor {
24    type Value = BigDecimal;
25
26    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
27        write!(formatter, "a number or formatted decimal string")
28    }
29
30    fn visit_str<E>(self, value: &str) -> Result<BigDecimal, E>
31    where
32        E: de::Error,
33    {
34        BigDecimal::from_str(value).map_err(|err| E::custom(format!("{}", err)))
35    }
36
37    fn visit_u64<E>(self, value: u64) -> Result<BigDecimal, E>
38    where
39        E: de::Error,
40    {
41        Ok(BigDecimal::from(value))
42    }
43
44    fn visit_i64<E>(self, value: i64) -> Result<BigDecimal, E>
45    where
46        E: de::Error,
47    {
48        Ok(BigDecimal::from(value))
49    }
50
51    fn visit_u128<E>(self, value: u128) -> Result<BigDecimal, E>
52    where
53        E: de::Error,
54    {
55        Ok(BigDecimal::from(value))
56    }
57
58    fn visit_i128<E>(self, value: i128) -> Result<BigDecimal, E>
59    where
60        E: de::Error,
61    {
62        Ok(BigDecimal::from(value))
63    }
64
65    fn visit_f32<E>(self, value: f32) -> Result<BigDecimal, E>
66    where
67        E: de::Error,
68    {
69        BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err)))
70    }
71
72    fn visit_f64<E>(self, value: f64) -> Result<BigDecimal, E>
73    where
74        E: de::Error,
75    {
76        BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err)))
77    }
78
79    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
80    where
81        A: de::MapAccess<'de>,
82    {
83        match map.next_key::<&str>() {
84            Ok(Some("$serde_json::private::Number")) => {
85                map.next_value::<BigDecimal>()
86            }
87            _ => {
88                Err(de::Error::invalid_type(de::Unexpected::Map, &self))
89            }
90        }
91    }
92}
93
94#[cfg(not(feature = "string-only"))]
95impl<'de> de::Deserialize<'de> for BigDecimal {
96    fn deserialize<D>(d: D) -> Result<Self, D::Error>
97    where
98        D: de::Deserializer<'de>,
99    {
100        d.deserialize_any(BigDecimalVisitor)
101    }
102}
103
104#[cfg(feature = "string-only")]
105impl<'de> de::Deserialize<'de> for BigDecimal {
106    fn deserialize<D>(d: D) -> Result<Self, D::Error>
107    where
108        D: de::Deserializer<'de>,
109    {
110        d.deserialize_str(BigDecimalVisitor)
111    }
112}
113
114#[cfg(test)]
115mod test {
116    use super::*;
117    use paste::paste;
118
119    use serde_test::{
120        Token, assert_tokens, assert_de_tokens, assert_de_tokens_error
121    };
122
123    mod serde_serialize_deserialize_str {
124        use super::*;
125
126        macro_rules! impl_case {
127            ($name:ident : $input:literal => $output:literal) => {
128                #[test]
129                fn $name() {
130                    let expected = Token::Str($output);
131                    let decimal: BigDecimal = $input.parse().unwrap();
132                    assert_tokens(&decimal, &[expected]);
133                }
134            };
135        }
136
137        impl_case!(case_1d0: "1.0" => "1.0");
138        impl_case!(case_0d5: "0.5" => "0.5");
139        impl_case!(case_50: "50" => "50");
140        impl_case!(case_50000: "50000" => "50000");
141        impl_case!(case_1en3: "1e-3" => "0.001");
142        impl_case!(case_10e11: "10e11" => "1000000000000");
143        impl_case!(case_d25: ".25" => "0.25");
144        impl_case!(case_12d34e1: "12.34e1" => "123.4");
145        impl_case!(case_40d0010: "40.0010" => "40.0010");
146    }
147
148    #[cfg(not(feature = "string-only"))]
149    mod serde_deserialize_int {
150        use super::*;
151
152        macro_rules! impl_case {
153            ( $( $ttype:ident ),+ : -$input:literal ) => {
154                $( paste! { impl_case!([< case_n $input _ $ttype:lower >] : $ttype : -$input); } )*
155            };
156            ( $( $ttype:ident ),+ : $input:literal ) => {
157                $( paste! { impl_case!([< case_ $input _ $ttype:lower >] : $ttype : $input); } )*
158            };
159            ($name:ident : $type:ident : $input:literal) => {
160                #[test]
161                fn $name() {
162                    let expected = BigDecimal::from($input);
163                    let token = Token::$type($input);
164                    assert_de_tokens(&expected, &[token]);
165                }
166            };
167        }
168
169        impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 0);
170        impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 1);
171        impl_case!(I8, I16, I32, I64 : -1);
172        impl_case!(I64: -99999999999i64);
173        impl_case!(I64: -9_223_372_036_854_775_808i64);
174    }
175
176    #[cfg(not(feature = "string-only"))]
177    mod serde_deserialize_float {
178        use super::*;
179
180        macro_rules! impl_case {
181            ( $name:ident : $input:literal => $ttype:ident : $expected:literal ) => {
182                paste! {
183                    #[test]
184                    fn [< $name _ $ttype:lower >]() {
185                        let expected: BigDecimal = $expected.parse().unwrap();
186                        let token = Token::$ttype($input);
187                        assert_de_tokens(&expected, &[token]);
188                    }
189                }
190            };
191            ( $name:ident : $input:literal => $( $ttype:ident : $expected:literal )+ ) => {
192                $( impl_case!($name : $input => $ttype : $expected); )*
193            };
194            ( $name:ident : $input:literal => $( $ttype:ident ),+ : $expected:literal ) => {
195                $( impl_case!($name : $input => $ttype : $expected); )*
196            };
197        }
198
199        impl_case!(case_1d0 : 1.0 => F32, F64 : "1");
200        impl_case!(case_1d1 : 1.1 => F32 : "1.10000002384185791015625"
201                                     F64 : "1.100000000000000088817841970012523233890533447265625");
202
203        impl_case!(case_0d001834988943300:
204            0.001834988943300 => F32 : "0.001834988943301141262054443359375"
205                                 F64 : "0.00183498894330000003084768511740776375518180429935455322265625");
206
207        impl_case!(case_n869651d9131236838:
208            -869651.9131236838 => F32 : "-869651.9375"
209                                  F64 : "-869651.91312368377111852169036865234375");
210
211        impl_case!(case_n1en20:
212            -1e-20 => F32 : "-9.999999682655225388967887463487205224055287544615566730499267578125E-21"
213                      F64 : "-999999999999999945153271454209571651729503702787392447107715776066783064379706047475337982177734375e-119");
214    }
215
216    #[cfg(not(feature = "string-only"))]
217    mod serde_deserialize_nan {
218        use super::*;
219
220        #[test]
221        fn case_f32() {
222            let tokens = [ Token::F32(f32::NAN) ];
223            assert_de_tokens_error::<BigDecimal>(&tokens, "NAN");
224        }
225
226        #[test]
227        fn case_f64() {
228            let tokens = [ Token::F64(f64::NAN) ];
229            assert_de_tokens_error::<BigDecimal>(&tokens, "NAN");
230        }
231    }
232
233
234    #[cfg(feature = "serde_json")]
235    mod json_support {
236        use super::*;
237        use impl_serde::{Serialize, Deserialize};
238        use serde_json;
239
240        #[derive(Serialize, Deserialize)]
241        struct TestStruct {
242            name: String,
243            value: BigDecimal,
244            #[serde(with = "crate::serde::json_num")]
245            number: BigDecimal,
246        }
247
248
249        #[test]
250        fn test_struct_parsing() {
251            let json_src = r#"
252                { "name": "foo", "value": 0.0008741329382918, "number": "12.34" }
253            "#;
254            let my_struct: TestStruct = serde_json::from_str(&json_src).unwrap();
255            assert_eq!(&my_struct.name, "foo");
256            assert_eq!(&my_struct.value, &"0.0008741329382918".parse().unwrap());
257            assert_eq!(&my_struct.number, &"12.34".parse().unwrap());
258
259            let s = serde_json::to_string(&my_struct).unwrap();
260            assert_eq!(s, r#"{"name":"foo","value":"0.0008741329382918","number":12.34}"#);
261        }
262    }
263}
264
265
266/// Serialize/deserialize [`BigDecimal`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
267///
268// The following example is ignored as it requires derives which we don't import and aren't compatible
269// with our locked versions of rust due to proc_macro2.
270/// ```ignore
271/// # extern crate serde;
272/// # use serde::{Serialize, Deserialize};
273/// # use bigdecimal::BigDecimal;
274/// # use std::str::FromStr;
275///
276/// #[derive(Serialize, Deserialize)]
277/// pub struct ArbitraryExample {
278///     #[serde(with = "bigdecimal::serde::json_num")]
279///     value: BigDecimal,
280/// }
281///
282/// let value = ArbitraryExample { value: BigDecimal::from_str("123.400").unwrap() };
283/// assert_eq!(
284///     &serde_json::to_string(&value).unwrap(),
285///     r#"{"value":123.400}"#
286/// );
287/// ```
288#[cfg(feature = "serde_json")]
289pub mod arbitrary_precision {
290    use super::*;
291
292    pub fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
293    where
294        D: serde::de::Deserializer<'de>,
295    {
296        let n = BigDecimal::deserialize(deserializer)?;
297
298        if n.scale.abs() > SERDE_SCALE_LIMIT && SERDE_SCALE_LIMIT > 0 {
299            let msg = format!("Calculated exponent '{}' out of bounds", -n.scale);
300            Err(serde::de::Error::custom(msg))
301        } else {
302            Ok(n)
303        }
304    }
305
306    pub fn serialize<S>(value: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
307    where
308        S: serde::Serializer,
309    {
310        serde_json::Number::from_str(&value.to_string())
311                           .map_err(ser::Error::custom)?
312                           .serialize(serializer)
313    }
314}
315
316
317/// Serialize/deserialize [`Option<BigDecimal>`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
318///
319// The following example is ignored as it requires derives which we don't import and aren't compatible
320// with our locked versions of rust due to proc_macro2.
321/// ```ignore
322/// # extern crate serde;
323/// # use serde::{Serialize, Deserialize};
324/// # use bigdecimal::BigDecimal;
325/// # use std::str::FromStr;
326///
327/// #[derive(Serialize, Deserialize)]
328/// pub struct ArbitraryExample {
329///     #[serde(with = "bigdecimal::impl_serde::arbitrary_precision_option")]
330///     value: Option<BigDecimal>,
331/// }
332///
333/// let value = ArbitraryExample { value: Some(BigDecimal::from_str("123.400").unwrap()) };
334/// assert_eq!(
335///     &serde_json::to_string(&value).unwrap(),
336///     r#"{"value":123.400}"#
337/// );
338///
339/// let value = ArbitraryExample { value: None };
340/// assert_eq!(
341///     &serde_json::to_string(&value).unwrap(),
342///     r#"{"value":null}"#
343/// );
344/// ```
345#[cfg(feature = "serde_json")]
346pub mod arbitrary_precision_option {
347    use super::*;
348
349    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BigDecimal>, D::Error>
350    where
351        D: serde::de::Deserializer<'de>,
352    {
353        Option::<serde_json::Number>::deserialize(deserializer)?
354                                     .map(|num| num.as_str().parse().map_err(serde::de::Error::custom))
355                                     .transpose()
356    }
357
358    pub fn serialize<S>(value: &Option<BigDecimal>, serializer: S) -> Result<S::Ok, S::Error>
359    where
360        S: serde::Serializer,
361    {
362        match *value {
363            Some(ref decimal) => {
364                serde_json::Number::from_str(&decimal.to_string())
365                                   .map_err(serde::ser::Error::custom)?
366                                   .serialize(serializer)
367            }
368            None => serializer.serialize_none(),
369        }
370    }
371}
372
373
374#[cfg(all(test, feature = "serde_json"))]
375mod test_jsonification {
376    use super::*;
377    extern crate serde_json;
378
379    mod deserialize_f64 {
380        use super::*;
381
382        macro_rules! impl_case {
383            ($name:ident : $input:expr) => {
384                impl_case!($name: $input => $input);
385            };
386            ($name:ident : $input:expr => $expected:literal) => {
387                #[test]
388                fn $name() {
389                    let mut json_deserialize = serde_json::Deserializer::from_str($input);
390                    let value = arbitrary_precision::deserialize(&mut json_deserialize)
391                                                    .expect("should parse JSON");
392                    let expected: BigDecimal = $expected.parse().unwrap();
393
394                    assert_eq!(expected, value);
395                }
396            };
397        }
398
399        impl_case!(case_1d0: "1.0" => "1.0");
400        impl_case!(case_0d001: "0.001" => "0.001");
401        impl_case!(case_41009d2207etc: "41009.22075436852032769878903135430037010");
402    }
403
404    mod serde_struct_decimals {
405        use super::*;
406        use crate as bigdecimal;
407
408        #[derive(Serialize, Deserialize)]
409        pub struct TestExample {
410            #[serde(with = "bigdecimal::serde::json_num")]
411            value: BigDecimal,
412        }
413
414        macro_rules! impl_case {
415            ($name:ident : $input:expr => (error)) => {
416                #[test]
417                fn $name() {
418                    let res = serde_json::from_str::<TestExample>($input);
419                    assert!(res.is_err());
420                }
421            };
422            ($name:ident : $input:expr => $expected:expr) => {
423                #[test]
424                fn $name() {
425                    let obj: TestExample = serde_json::from_str($input).unwrap();
426                    let expected: BigDecimal = $expected.parse().unwrap();
427
428                    assert_eq!(expected, obj.value);
429
430                    // expect output to be input with all spaces removed
431                    let s = serde_json::to_string(&obj).unwrap();
432                    let input_stripped = $input.replace(" ", "");
433
434                    assert_eq!(&s, &input_stripped);
435                }
436            };
437        }
438
439        impl_case!(case_1d0: r#"{ "value": 1.0 }"# => "1.0" );
440        impl_case!(case_2d01: r#"{ "value": 2.01 }"# => "2.01" );
441        impl_case!(case_50: r#"{ "value": 50 }"# => "50" );
442        impl_case!(case_high_prec: r#"{ "value": 64771126779.35857825871133263810255301911 }"# => "64771126779.35857825871133263810255301911" );
443
444        impl_case!(case_nan: r#"{ "value": nan }"# => (error) );
445
446
447        #[test]
448        fn scale_out_of_bounds() {
449            use serde_crate::de::Error;
450
451            let src = r#"{ "value": 1e92233720392233 }"#;
452
453            let parse_err = serde_json::from_str::<TestExample>(&src).err().unwrap();
454            let err_str = parse_err.to_string();
455
456            assert!(err_str.starts_with("Calculated exponent '92233720392233' out of bounds"), "{}", err_str);
457        }
458    }
459}