Coverage Report

Created: 2025-10-13 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_decimal-1.5.0/src/grouper.rs
Line
Count
Source
1
// This file is part of ICU4X. For terms of use, please see the file
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5
//! Algorithms to determine where to position grouping separators.
6
7
use crate::options::GroupingStrategy;
8
use crate::provider::GroupingSizesV1;
9
use core::cmp;
10
11
/// Returns whether to display a grouping separator at the given magnitude.
12
///
13
/// `upper_magnitude` is the magnitude of the highest-power digit, used for resolving minimum
14
/// grouping digits.
15
0
pub fn check(
16
0
    upper_magnitude: i16,
17
0
    magnitude: i16,
18
0
    strategy: GroupingStrategy,
19
0
    sizes: &GroupingSizesV1,
20
0
) -> bool {
21
0
    let primary = if sizes.primary == 0 {
22
0
        return false;
23
    } else {
24
0
        sizes.primary as i16
25
    };
26
0
    if magnitude < primary {
27
0
        return false;
28
0
    }
29
0
    let min_grouping = {
30
        use GroupingStrategy::*;
31
0
        match strategy {
32
0
            Never => return false,
33
            // Note: Auto and Always are the same for FixedDecimalFormatter.
34
            // When currencies are implemented, this will change.
35
0
            Auto | Always => cmp::max(1, sizes.min_grouping) as i16,
36
0
            Min2 => cmp::max(2, sizes.min_grouping) as i16,
37
        }
38
    };
39
0
    if upper_magnitude < primary + min_grouping - 1 {
40
0
        return false;
41
0
    }
42
0
    let secondary = if sizes.secondary == 0 {
43
0
        primary
44
    } else {
45
0
        sizes.secondary as i16
46
    };
47
0
    let magnitude_prime = magnitude - primary;
48
0
    if magnitude_prime % secondary == 0 {
49
0
        return true;
50
0
    }
51
0
    false
52
0
}
53
54
#[test]
55
fn test_grouper() {
56
    use crate::options;
57
    use crate::provider::*;
58
    use crate::FixedDecimalFormatter;
59
    use fixed_decimal::FixedDecimal;
60
    use icu_locid::LanguageIdentifier;
61
    use icu_provider::prelude::*;
62
    use icu_provider_adapters::any_payload::AnyPayloadProvider;
63
    use writeable::assert_writeable_eq;
64
65
    let western_sizes = GroupingSizesV1 {
66
        min_grouping: 1,
67
        primary: 3,
68
        secondary: 3,
69
    };
70
    let indic_sizes = GroupingSizesV1 {
71
        min_grouping: 1,
72
        primary: 3,
73
        secondary: 2,
74
    };
75
    let western_sizes_min3 = GroupingSizesV1 {
76
        min_grouping: 3,
77
        primary: 3,
78
        secondary: 3,
79
    };
80
81
    // primary=0 implies no grouping; the other fields are ignored
82
    let zero_test = GroupingSizesV1 {
83
        min_grouping: 0,
84
        primary: 0,
85
        secondary: 0,
86
    };
87
88
    // secondary=0 implies that it inherits from primary
89
    let blank_secondary = GroupingSizesV1 {
90
        min_grouping: 0,
91
        primary: 3,
92
        secondary: 0,
93
    };
94
95
    #[derive(Debug)]
96
    struct TestCase {
97
        strategy: GroupingStrategy,
98
        sizes: GroupingSizesV1,
99
        // Expected results for numbers with magnitude 3, 4, 5, and 6
100
        expected: [&'static str; 4],
101
    }
102
    #[allow(clippy::redundant_clone)]
103
    let cases = [
104
        TestCase {
105
            strategy: GroupingStrategy::Auto,
106
            sizes: western_sizes,
107
            expected: ["1,000", "10,000", "100,000", "1,000,000"],
108
        },
109
        TestCase {
110
            strategy: GroupingStrategy::Min2,
111
            sizes: western_sizes,
112
            expected: ["1000", "10,000", "100,000", "1,000,000"],
113
        },
114
        TestCase {
115
            strategy: GroupingStrategy::Auto,
116
            sizes: indic_sizes,
117
            expected: ["1,000", "10,000", "1,00,000", "10,00,000"],
118
        },
119
        TestCase {
120
            strategy: GroupingStrategy::Min2,
121
            sizes: indic_sizes,
122
            expected: ["1000", "10,000", "1,00,000", "10,00,000"],
123
        },
124
        TestCase {
125
            strategy: GroupingStrategy::Auto,
126
            sizes: western_sizes_min3,
127
            expected: ["1000", "10000", "100,000", "1,000,000"],
128
        },
129
        TestCase {
130
            strategy: GroupingStrategy::Min2,
131
            sizes: western_sizes_min3,
132
            expected: ["1000", "10000", "100,000", "1,000,000"],
133
        },
134
        TestCase {
135
            strategy: GroupingStrategy::Auto,
136
            sizes: zero_test,
137
            expected: ["1000", "10000", "100000", "1000000"],
138
        },
139
        TestCase {
140
            strategy: GroupingStrategy::Min2,
141
            sizes: zero_test,
142
            expected: ["1000", "10000", "100000", "1000000"],
143
        },
144
        TestCase {
145
            strategy: GroupingStrategy::Auto,
146
            sizes: blank_secondary,
147
            expected: ["1,000", "10,000", "100,000", "1,000,000"],
148
        },
149
        TestCase {
150
            strategy: GroupingStrategy::Min2,
151
            sizes: blank_secondary,
152
            expected: ["1000", "10,000", "100,000", "1,000,000"],
153
        },
154
    ];
155
    for cas in &cases {
156
        for i in 0..4 {
157
            let dec = FixedDecimal::from(1).multiplied_pow10((i as i16) + 3);
158
            let provider = AnyPayloadProvider::from_owned::<DecimalSymbolsV1Marker>(
159
                crate::provider::DecimalSymbolsV1 {
160
                    grouping_sizes: cas.sizes,
161
                    ..Default::default()
162
                },
163
            );
164
            let options = options::FixedDecimalFormatterOptions {
165
                grouping_strategy: cas.strategy,
166
                ..Default::default()
167
            };
168
            let fdf = FixedDecimalFormatter::try_new_unstable(
169
                &provider.as_downcasting(),
170
                &LanguageIdentifier::UND.into(),
171
                options,
172
            )
173
            .unwrap();
174
            let actual = fdf.format(&dec);
175
            assert_writeable_eq!(actual, cas.expected[i], "{:?}", cas);
176
        }
177
    }
178
}