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
267/// Serialize/deserialize [`BigDecimal`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
268///
269// The following example is ignored as it requires derives which we don't import and aren't compatible
270// with our locked versions of rust due to proc_macro2.
271/// ```ignore
272/// # extern crate serde;
273/// # use serde::{Serialize, Deserialize};
274/// # use bigdecimal::BigDecimal;
275/// # use std::str::FromStr;
276///
277/// #[derive(Serialize, Deserialize)]
278/// pub struct ArbitraryExample {
279///     #[serde(with = "bigdecimal::serde::json_num")]
280///     value: BigDecimal,
281/// }
282///
283/// let value = ArbitraryExample { value: BigDecimal::from_str("123.400").unwrap() };
284/// assert_eq!(
285///     &serde_json::to_string(&value).unwrap(),
286///     r#"{"value":123.400}"#
287/// );
288/// ```
289#[cfg(feature = "serde_json")]
290pub mod arbitrary_precision {
291    use super::*;
292
293    pub fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
294    where
295        D: serde::de::Deserializer<'de>,
296    {
297        let n = BigDecimal::deserialize(deserializer)?;
298
299        if n.scale.abs() > SERDE_SCALE_LIMIT && SERDE_SCALE_LIMIT > 0 {
300            let msg = format!("Calculated exponent '{}' out of bounds", -n.scale);
301            Err(serde::de::Error::custom(msg))
302        } else {
303            Ok(n)
304        }
305    }
306
307    pub fn serialize<S>(value: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
308    where
309        S: serde::Serializer,
310    {
311        serde_json::Number::from_str(&value.to_string())
312                           .map_err(ser::Error::custom)?
313                           .serialize(serializer)
314    }
315}
316
317
318/// Serialize/deserialize [`Option<BigDecimal>`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
319///
320// The following example is ignored as it requires derives which we don't import and aren't compatible
321// with our locked versions of rust due to proc_macro2.
322/// ```ignore
323/// # extern crate serde;
324/// # use serde::{Serialize, Deserialize};
325/// # use bigdecimal::BigDecimal;
326/// # use std::str::FromStr;
327///
328/// #[derive(Serialize, Deserialize)]
329/// pub struct ArbitraryExample {
330///     #[serde(with = "bigdecimal::impl_serde::arbitrary_precision_option")]
331///     value: Option<BigDecimal>,
332/// }
333///
334/// let value = ArbitraryExample { value: Some(BigDecimal::from_str("123.400").unwrap()) };
335/// assert_eq!(
336///     &serde_json::to_string(&value).unwrap(),
337///     r#"{"value":123.400}"#
338/// );
339///
340/// let value = ArbitraryExample { value: None };
341/// assert_eq!(
342///     &serde_json::to_string(&value).unwrap(),
343///     r#"{"value":null}"#
344/// );
345/// ```
346#[cfg(feature = "serde_json")]
347pub mod arbitrary_precision_option {
348    use super::*;
349
350    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BigDecimal>, D::Error>
351    where
352        D: serde::de::Deserializer<'de>,
353    {
354        Option::<serde_json::Number>::deserialize(deserializer)?
355                                     .map(|num| num.as_str().parse().map_err(serde::de::Error::custom))
356                                     .transpose()
357    }
358
359    pub fn serialize<S>(value: &Option<BigDecimal>, serializer: S) -> Result<S::Ok, S::Error>
360    where
361        S: serde::Serializer,
362    {
363        match *value {
364            Some(ref decimal) => {
365                serde_json::Number::from_str(&decimal.to_string())
366                                   .map_err(serde::ser::Error::custom)?
367                                   .serialize(serializer)
368            }
369            None => serializer.serialize_none(),
370        }
371    }
372}
373
374
375#[cfg(all(test, feature = "serde_json"))]
376mod test_jsonification {
377    use super::*;
378    extern crate serde_json;
379
380    mod deserialize_f64 {
381        use super::*;
382
383        macro_rules! impl_case {
384            ($name:ident : $input:expr) => {
385                impl_case!($name: $input => $input);
386            };
387            ($name:ident : $input:expr => $expected:literal) => {
388                #[test]
389                fn $name() {
390                    let mut json_deserialize = serde_json::Deserializer::from_str($input);
391                    let value = arbitrary_precision::deserialize(&mut json_deserialize)
392                                                    .expect("should parse JSON");
393                    let expected: BigDecimal = $expected.parse().unwrap();
394
395                    assert_eq!(expected, value);
396                }
397            };
398        }
399
400        impl_case!(case_1d0: "1.0" => "1.0");
401        impl_case!(case_0d001: "0.001" => "0.001");
402        impl_case!(case_41009d2207etc: "41009.22075436852032769878903135430037010");
403    }
404
405    mod serde_struct_decimals {
406        use super::*;
407        use crate as bigdecimal;
408
409        #[derive(Serialize, Deserialize)]
410        pub struct TestExample {
411            #[serde(with = "bigdecimal::serde::json_num")]
412            value: BigDecimal,
413        }
414
415        macro_rules! impl_case {
416            ($name:ident : $input:expr => (error)) => {
417                #[test]
418                fn $name() {
419                    let res = serde_json::from_str::<TestExample>($input);
420                    assert!(res.is_err());
421                }
422            };
423            ($name:ident : $input:expr => $expected:expr) => {
424                #[test]
425                fn $name() {
426                    let obj: TestExample = serde_json::from_str($input).unwrap();
427                    let expected: BigDecimal = $expected.parse().unwrap();
428
429                    assert_eq!(expected, obj.value);
430
431                    // expect output to be input with all spaces removed
432                    let s = serde_json::to_string(&obj).unwrap();
433                    let input_stripped = $input.replace(" ", "");
434
435                    assert_eq!(&s, &input_stripped);
436                }
437            };
438        }
439
440        impl_case!(case_1d0: r#"{ "value": 1.0 }"# => "1.0" );
441        impl_case!(case_2d01: r#"{ "value": 2.01 }"# => "2.01" );
442        impl_case!(case_50: r#"{ "value": 50 }"# => "50" );
443        impl_case!(case_high_prec: r#"{ "value": 64771126779.35857825871133263810255301911 }"# => "64771126779.35857825871133263810255301911" );
444
445        impl_case!(case_nan: r#"{ "value": nan }"# => (error) );
446
447
448        #[test]
449        fn scale_out_of_bounds() {
450            use serde_crate::de::Error;
451
452            let src = r#"{ "value": 1e92233720392233 }"#;
453
454            let parse_err = serde_json::from_str::<TestExample>(&src).err().unwrap();
455            let err_str = parse_err.to_string();
456
457            assert!(err_str.starts_with("Calculated exponent '92233720392233' out of bounds"), "{}", err_str);
458        }
459    }
460}