parse_sap_atom_feed/deserializers/edm_decimal/
mod.rs

1use rust_decimal::Decimal;
2use serde::{de::Error, Deserialize, Deserializer};
3use std::str::FromStr;
4
5// i64::MAX is 19 digits long
6static MAX_DIGITS: usize = 19;
7// Value duplicated from rust_decimal::constants::MAX_PRECISION which is private...
8const MAX_PRECISION: usize = 28;
9// Maximum potential padding is 27 zeroes for 0.0000000000000000000000000001
10static ZEROES: &str = "000000000000000000000000000";
11
12// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
13/// Parse the digits in the decimal string and return an i64 suitable for creating a new rust_decimal::Decimal value
14///
15/// * Split integer and fractional parts
16/// * Trim trailing zeroes from fractional part before deciding if it's too long
17/// * Check the number of fractional digits <= scale
18/// * Concatenate integer with zero padded fraction (on the right) then strip leading zeroes
19/// * Check the number digits <= MAX_DIGITS
20/// * Convert digits to i64
21fn parse_decimal_digits(dec_str: &str, scale: usize) -> Result<i64, String> {
22    // Just in case it goes horribly wrong...
23    let data_loss_prefix = format!(
24        "Error: '{}' cannot be converted to rust_decimal::Decimal without loss of data:",
25        dec_str,
26    );
27    // Split string at decimal point (that might not be there)
28    let num_parts = dec_str
29        .split(".")
30        .map(|s| String::from(s))
31        .collect::<Vec<String>>();
32
33    let integers = num_parts[0].as_str();
34    let fraction = if num_parts.len() == 2 {
35        // Trailing zeroes in the fraction are not needed at this point in time
36        // However, some trailing zeroes might need to be reinstated before converting to i64
37        num_parts[1].trim_end_matches("0")
38    } else {
39        // Fill the missing fractional zeroes
40        &format!("{ZEROES:.*}", scale)
41    };
42
43    // Return an error if there are more fractional digits than are permitted by the scale
44    if fraction.len() > scale {
45        return Err(format!(
46            "{} {} fractional digit{} supplied, but scale only permits {}",
47            data_loss_prefix,
48            fraction.len(),
49            if fraction.len() > 1 { "s" } else { "" },
50            scale
51        ));
52    }
53
54    // Zero pad any missing fractional zeroes on the right, combine parts then strip leading zeroes
55    let digits = format!("{integers}{:0<scale$}", fraction);
56    let digits = digits.trim_start_matches("0");
57    let digit_count = digits.len();
58
59    // Can this number be converted to an i64?
60    if digit_count == 0 {
61        Ok(0)
62    } else if digit_count > MAX_DIGITS {
63        Err(format!(
64            "{} Too many digits ({}) to fit in an i64 ({})",
65            data_loss_prefix, digit_count, MAX_DIGITS
66        ))
67    } else {
68        Ok(i64::from_str(&format!("{digits}")).unwrap())
69    }
70}
71
72// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73/// String -> rust_decimal::Decimal parser
74fn dec_str_to_rust_decimal(dec_str: String, scale: usize) -> Result<Decimal, String> {
75    if scale > MAX_PRECISION {
76        return Err(format!(
77            "Scale exceeds the maximum precision allowed: {} > {}",
78            scale, MAX_PRECISION
79        ));
80    }
81
82    // Check for cases that always return zero
83    if dec_str.is_empty()
84        || dec_str.eq("0")
85        || dec_str.eq("0.0")
86        || dec_str.eq("0.")
87        || dec_str.eq(".0")
88    {
89        return Ok(Decimal::new(0, scale as u32));
90    }
91
92    // If parse_decimal_digits returns an error, then data loss has been detected - throw toys out of pram
93    let digits_i64 = match parse_decimal_digits(&dec_str, scale) {
94        Ok(digits) => digits,
95        Err(err) => panic!("{}", err),
96    };
97
98    Decimal::try_new(digits_i64, scale as u32).or_else(|err| Err(err.to_string()))
99}
100
101// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102fn to_rust_decimal_inner<'de, D>(deserializer: D, scale: u32) -> Result<Decimal, D::Error>
103where
104    D: Deserializer<'de>,
105{
106    match String::deserialize(deserializer) {
107        Ok(dec_str) => {
108            dec_str_to_rust_decimal(dec_str, scale as usize).or_else(|err| Err(Error::custom(err)))
109        }
110        Err(err) => Err(Error::custom(err.to_string())),
111    }
112}
113
114// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
115pub fn to_rust_decimal_0dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
116where
117    D: Deserializer<'de>,
118{
119    Ok(to_rust_decimal_0dp(deserializer).ok())
120}
121pub fn to_rust_decimal_1dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
122where
123    D: Deserializer<'de>,
124{
125    Ok(to_rust_decimal_1dp(deserializer).ok())
126}
127pub fn to_rust_decimal_2dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
128where
129    D: Deserializer<'de>,
130{
131    Ok(to_rust_decimal_2dp(deserializer).ok())
132}
133pub fn to_rust_decimal_3dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
134where
135    D: Deserializer<'de>,
136{
137    Ok(to_rust_decimal_3dp(deserializer).ok())
138}
139pub fn to_rust_decimal_4dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
140where
141    D: Deserializer<'de>,
142{
143    Ok(to_rust_decimal_4dp(deserializer).ok())
144}
145pub fn to_rust_decimal_5dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
146where
147    D: Deserializer<'de>,
148{
149    Ok(to_rust_decimal_5dp(deserializer).ok())
150}
151pub fn to_rust_decimal_6dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
152where
153    D: Deserializer<'de>,
154{
155    Ok(to_rust_decimal_6dp(deserializer).ok())
156}
157pub fn to_rust_decimal_7dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
158where
159    D: Deserializer<'de>,
160{
161    Ok(to_rust_decimal_7dp(deserializer).ok())
162}
163pub fn to_rust_decimal_8dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
164where
165    D: Deserializer<'de>,
166{
167    Ok(to_rust_decimal_8dp(deserializer).ok())
168}
169pub fn to_rust_decimal_9dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
170where
171    D: Deserializer<'de>,
172{
173    Ok(to_rust_decimal_9dp(deserializer).ok())
174}
175pub fn to_rust_decimal_10dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
176where
177    D: Deserializer<'de>,
178{
179    Ok(to_rust_decimal_10dp(deserializer).ok())
180}
181pub fn to_rust_decimal_11dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
182where
183    D: Deserializer<'de>,
184{
185    Ok(to_rust_decimal_11dp(deserializer).ok())
186}
187pub fn to_rust_decimal_12dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
188where
189    D: Deserializer<'de>,
190{
191    Ok(to_rust_decimal_12dp(deserializer).ok())
192}
193pub fn to_rust_decimal_13dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
194where
195    D: Deserializer<'de>,
196{
197    Ok(to_rust_decimal_13dp(deserializer).ok())
198}
199pub fn to_rust_decimal_14dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
200where
201    D: Deserializer<'de>,
202{
203    Ok(to_rust_decimal_14dp(deserializer).ok())
204}
205pub fn to_rust_decimal_15dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
206where
207    D: Deserializer<'de>,
208{
209    Ok(to_rust_decimal_15dp(deserializer).ok())
210}
211pub fn to_rust_decimal_16dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
212where
213    D: Deserializer<'de>,
214{
215    Ok(to_rust_decimal_16dp(deserializer).ok())
216}
217pub fn to_rust_decimal_17dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
218where
219    D: Deserializer<'de>,
220{
221    Ok(to_rust_decimal_17dp(deserializer).ok())
222}
223pub fn to_rust_decimal_18dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
224where
225    D: Deserializer<'de>,
226{
227    Ok(to_rust_decimal_18dp(deserializer).ok())
228}
229pub fn to_rust_decimal_19dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
230where
231    D: Deserializer<'de>,
232{
233    Ok(to_rust_decimal_19dp(deserializer).ok())
234}
235pub fn to_rust_decimal_20dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
236where
237    D: Deserializer<'de>,
238{
239    Ok(to_rust_decimal_20dp(deserializer).ok())
240}
241pub fn to_rust_decimal_21dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
242where
243    D: Deserializer<'de>,
244{
245    Ok(to_rust_decimal_21dp(deserializer).ok())
246}
247pub fn to_rust_decimal_22dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
248where
249    D: Deserializer<'de>,
250{
251    Ok(to_rust_decimal_22dp(deserializer).ok())
252}
253pub fn to_rust_decimal_23dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
254where
255    D: Deserializer<'de>,
256{
257    Ok(to_rust_decimal_23dp(deserializer).ok())
258}
259pub fn to_rust_decimal_24dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
260where
261    D: Deserializer<'de>,
262{
263    Ok(to_rust_decimal_24dp(deserializer).ok())
264}
265pub fn to_rust_decimal_25dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
266where
267    D: Deserializer<'de>,
268{
269    Ok(to_rust_decimal_25dp(deserializer).ok())
270}
271pub fn to_rust_decimal_26dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
272where
273    D: Deserializer<'de>,
274{
275    Ok(to_rust_decimal_26dp(deserializer).ok())
276}
277pub fn to_rust_decimal_27dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
278where
279    D: Deserializer<'de>,
280{
281    Ok(to_rust_decimal_27dp(deserializer).ok())
282}
283pub fn to_rust_decimal_28dp_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
284where
285    D: Deserializer<'de>,
286{
287    Ok(to_rust_decimal_28dp(deserializer).ok())
288}
289
290// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
291pub fn to_rust_decimal_0dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
292where
293    D: Deserializer<'de>,
294{
295    to_rust_decimal_inner(deserializer, 0)
296}
297pub fn to_rust_decimal_1dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
298where
299    D: Deserializer<'de>,
300{
301    to_rust_decimal_inner(deserializer, 1)
302}
303pub fn to_rust_decimal_2dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
304where
305    D: Deserializer<'de>,
306{
307    to_rust_decimal_inner(deserializer, 2)
308}
309pub fn to_rust_decimal_3dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
310where
311    D: Deserializer<'de>,
312{
313    to_rust_decimal_inner(deserializer, 3)
314}
315pub fn to_rust_decimal_4dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
316where
317    D: Deserializer<'de>,
318{
319    to_rust_decimal_inner(deserializer, 4)
320}
321pub fn to_rust_decimal_5dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
322where
323    D: Deserializer<'de>,
324{
325    to_rust_decimal_inner(deserializer, 5)
326}
327pub fn to_rust_decimal_6dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
328where
329    D: Deserializer<'de>,
330{
331    to_rust_decimal_inner(deserializer, 6)
332}
333pub fn to_rust_decimal_7dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
334where
335    D: Deserializer<'de>,
336{
337    to_rust_decimal_inner(deserializer, 7)
338}
339pub fn to_rust_decimal_8dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
340where
341    D: Deserializer<'de>,
342{
343    to_rust_decimal_inner(deserializer, 8)
344}
345pub fn to_rust_decimal_9dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
346where
347    D: Deserializer<'de>,
348{
349    to_rust_decimal_inner(deserializer, 9)
350}
351pub fn to_rust_decimal_10dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
352where
353    D: Deserializer<'de>,
354{
355    to_rust_decimal_inner(deserializer, 10)
356}
357pub fn to_rust_decimal_11dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
358where
359    D: Deserializer<'de>,
360{
361    to_rust_decimal_inner(deserializer, 11)
362}
363pub fn to_rust_decimal_12dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
364where
365    D: Deserializer<'de>,
366{
367    to_rust_decimal_inner(deserializer, 12)
368}
369pub fn to_rust_decimal_13dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
370where
371    D: Deserializer<'de>,
372{
373    to_rust_decimal_inner(deserializer, 13)
374}
375pub fn to_rust_decimal_14dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
376where
377    D: Deserializer<'de>,
378{
379    to_rust_decimal_inner(deserializer, 14)
380}
381pub fn to_rust_decimal_15dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
382where
383    D: Deserializer<'de>,
384{
385    to_rust_decimal_inner(deserializer, 15)
386}
387pub fn to_rust_decimal_16dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
388where
389    D: Deserializer<'de>,
390{
391    to_rust_decimal_inner(deserializer, 16)
392}
393pub fn to_rust_decimal_17dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
394where
395    D: Deserializer<'de>,
396{
397    to_rust_decimal_inner(deserializer, 17)
398}
399pub fn to_rust_decimal_18dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
400where
401    D: Deserializer<'de>,
402{
403    to_rust_decimal_inner(deserializer, 18)
404}
405pub fn to_rust_decimal_19dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
406where
407    D: Deserializer<'de>,
408{
409    to_rust_decimal_inner(deserializer, 19)
410}
411pub fn to_rust_decimal_20dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
412where
413    D: Deserializer<'de>,
414{
415    to_rust_decimal_inner(deserializer, 20)
416}
417pub fn to_rust_decimal_21dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
418where
419    D: Deserializer<'de>,
420{
421    to_rust_decimal_inner(deserializer, 21)
422}
423pub fn to_rust_decimal_22dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
424where
425    D: Deserializer<'de>,
426{
427    to_rust_decimal_inner(deserializer, 22)
428}
429pub fn to_rust_decimal_23dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
430where
431    D: Deserializer<'de>,
432{
433    to_rust_decimal_inner(deserializer, 23)
434}
435pub fn to_rust_decimal_24dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
436where
437    D: Deserializer<'de>,
438{
439    to_rust_decimal_inner(deserializer, 24)
440}
441pub fn to_rust_decimal_25dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
442where
443    D: Deserializer<'de>,
444{
445    to_rust_decimal_inner(deserializer, 25)
446}
447pub fn to_rust_decimal_26dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
448where
449    D: Deserializer<'de>,
450{
451    to_rust_decimal_inner(deserializer, 26)
452}
453pub fn to_rust_decimal_27dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
454where
455    D: Deserializer<'de>,
456{
457    to_rust_decimal_inner(deserializer, 27)
458}
459pub fn to_rust_decimal_28dp<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
460where
461    D: Deserializer<'de>,
462{
463    to_rust_decimal_inner(deserializer, 28)
464}
465
466// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467#[cfg(test)]
468mod parser_tests;
469#[cfg(test)]
470mod unit_tests;