spreadsheet_to_json/
euro_number_format.rs

1
2/// Detect if a numeric string uses the European format with , as the decimal separator and dots as thousand separators
3/// If only one comma is present, *enforce_euro_mode* will treat the comma as a decimal separator.
4/// Otherwise it will be assumed to be a thousand separator
5pub fn is_euro_number_format(txt: &str, enforce_euro_mode: bool) -> bool {
6    let chs = txt.char_indices();
7    let mut num_indices = 0;
8    let mut dot_pos: Option<usize> = None;
9    let mut num_dots = 0;
10    let mut num_commas = 0;
11    let mut comma_pos: Option<usize> = None;
12    for (index, ch) in chs {
13        match ch {
14            '.' => {
15                if dot_pos.is_none() {
16                    dot_pos = Some(index);
17                }
18                num_dots += 1;
19            }
20            ',' => {
21                if comma_pos.is_none() {
22                    comma_pos = Some(index);
23                }
24                num_commas += 1;
25            },
26            _ => ()
27        }
28        num_indices += 1; // count indices here to avoid cloning above
29    }
30    if let Some(d_pos) = dot_pos {
31        if let Some(c_pos) = comma_pos {
32            d_pos < c_pos
33        } else {
34            // if it only has one dot only interpreet as decimal separator if enforce_euro_mode is true
35            num_dots > 1 || enforce_euro_mode
36        }
37    } else {
38        // no dots
39        if let Some(c_pos) = comma_pos {
40            if num_commas > 1 {
41                false
42            } else {
43                // with only one comma, assume it's a decimal separator
44                // if it is not exactly 4 positions from the right or if enforce
45                num_indices - c_pos != 4 || enforce_euro_mode
46            }
47        } else {
48            false
49        }
50
51    }
52}
53
54
55
56#[cfg(test)]
57mod tests {
58    // Note this useful idiom: importing names from outer (for mod tests) scope.
59    use super::*;
60
61    #[test]
62    fn test_number_format_1() {
63        let sample = "1.256";
64        assert_eq!(is_euro_number_format(sample, false), false);
65    }
66
67    #[test]
68    fn test_number_format_2() {
69        let sample = "1,256";
70        assert_eq!(is_euro_number_format(sample, false), false);
71    }
72
73    #[test]
74    fn test_number_format_3() {
75
76        let sample = "12,56";
77        
78        assert_eq!(is_euro_number_format(sample, false), true);
79    }
80
81    #[test]
82    fn test_number_format_4() {
83
84        let sample = "1,256.67";
85        
86        assert_eq!(is_euro_number_format(sample, false), false);
87    }
88
89    #[test]
90    fn test_number_format_5() {
91
92        let sample = "1.256,67";
93        
94        assert_eq!(is_euro_number_format(sample, false), true);
95    }
96}