• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 9457158389

10 Jun 2024 11:45PM UTC coverage: 75.174% (+0.05%) from 75.121%
9457158389

push

github

web-flow
Add constructing TinyAsciiStr from utf16 (#5025)

Introduces TinyAsciiStr constructors from utf16 and converges on the
consensus from #4931.

---------

Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>

65 of 82 new or added lines in 14 files covered. (79.27%)

3441 existing lines in 141 files now uncovered.

52850 of 70304 relevant lines covered (75.17%)

563298.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.02
/utils/fixed_decimal/src/compact.rs
1
// This file is part of ICU4X. For terms of use, please see the file
8✔
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
use core::convert::TryFrom;
6
use core::fmt;
7

8
use core::str::FromStr;
9

10
use crate::FixedDecimal;
11
use crate::ParseError;
12

13
/// A struct containing a [`FixedDecimal`] significand together with an exponent, representing a
14
/// number written in compact notation (such as 1.2M).
15
/// This represents a _source number_, as defined
16
/// [in UTS #35](https://www.unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax).
17
/// The value exponent=0 represents a number in non-compact
18
/// notation (such as 1 200 000).
19
///
20
/// This is distinct from [`crate::ScientificDecimal`] because it does not represent leading 0s
21
/// nor a sign in the exponent, and behaves differently in pluralization.
22
#[derive(Debug, Clone, PartialEq)]
×
23
pub struct CompactDecimal {
24
    significand: FixedDecimal,
×
25
    exponent: u8,
×
26
}
27

28
impl CompactDecimal {
29
    /// Constructs a [`CompactDecimal`] from its significand and exponent.
30
    pub fn from_significand_and_exponent(significand: FixedDecimal, exponent: u8) -> Self {
58✔
31
        Self {
58✔
32
            significand,
33
            exponent,
34
        }
35
    }
58✔
36

37
    /// Returns a reference to the significand of `self`.
38
    /// ```
39
    /// # use fixed_decimal::CompactDecimal;
40
    /// # use fixed_decimal::FixedDecimal;
41
    /// # use std::str::FromStr;
42
    /// #
43
    /// assert_eq!(
44
    ///     CompactDecimal::from_str("+1.20c6").unwrap().significand(),
45
    ///     &FixedDecimal::from_str("+1.20").unwrap()
46
    /// );
47
    /// ```
48
    pub fn significand(&self) -> &FixedDecimal {
1,031✔
49
        &self.significand
50
    }
1,031✔
51

52
    /// Returns the significand of `self`.
53
    /// ```
54
    /// # use fixed_decimal::CompactDecimal;
55
    /// # use fixed_decimal::FixedDecimal;
56
    /// # use std::str::FromStr;
57
    /// #
58
    /// assert_eq!(
59
    ///     CompactDecimal::from_str("+1.20c6")
60
    ///         .unwrap()
61
    ///         .into_significand(),
62
    ///     FixedDecimal::from_str("+1.20").unwrap()
63
    /// );
64
    /// ```
65
    pub fn into_significand(self) -> FixedDecimal {
1✔
66
        self.significand
1✔
67
    }
1✔
68

69
    /// Returns the exponent of `self`.
70
    /// ```
71
    /// # use fixed_decimal::CompactDecimal;
72
    /// # use std::str::FromStr;
73
    /// #
74
    /// assert_eq!(CompactDecimal::from_str("+1.20c6").unwrap().exponent(), 6);
75
    /// assert_eq!(CompactDecimal::from_str("1729").unwrap().exponent(), 0);
76
    /// ```
77
    pub fn exponent(&self) -> u8 {
406✔
78
        self.exponent
406✔
79
    }
406✔
80
}
81

82
/// Render the [`CompactDecimal`] in sampleValue syntax.
83
/// The letter c is used, rather than the deprecated e.
84
///
85
/// # Examples
86
///
87
/// ```
88
/// # use fixed_decimal::CompactDecimal;
89
/// # use std::str::FromStr;
90
/// # use writeable::assert_writeable_eq;
91
/// #
92
/// assert_writeable_eq!(
93
///     CompactDecimal::from_str("+1.20c6").unwrap(),
94
///     "+1.20c6"
95
/// );
96
/// assert_writeable_eq!(CompactDecimal::from_str("+1729").unwrap(), "+1729");
97
/// ```
98
impl writeable::Writeable for CompactDecimal {
99
    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
13✔
100
        self.significand.write_to(sink)?;
13✔
101
        if self.exponent != 0 {
13✔
102
            sink.write_char('c')?;
8✔
103
            self.exponent.write_to(sink)?;
21✔
104
        }
105
        Ok(())
13✔
106
    }
13✔
107

108
    fn writeable_length_hint(&self) -> writeable::LengthHint {
4✔
109
        let mut result = self.significand.writeable_length_hint();
4✔
110
        if self.exponent != 0 {
4✔
111
            result += self.exponent.writeable_length_hint() + 1;
2✔
112
        }
113
        result
4✔
114
    }
4✔
115
}
116

117
writeable::impl_display_with_writeable!(CompactDecimal);
118

119
impl FromStr for CompactDecimal {
120
    type Err = ParseError;
121
    fn from_str(input_str: &str) -> Result<Self, Self::Err> {
21✔
122
        Self::try_from(input_str.as_bytes())
21✔
123
    }
21✔
124
}
125

126
/// The deprecated letter e is not accepted as a synonym for c.
127
impl TryFrom<&[u8]> for CompactDecimal {
128
    type Error = ParseError;
129
    fn try_from(input_str: &[u8]) -> Result<Self, Self::Error> {
21✔
130
        if input_str.iter().any(|&c| c == b'e' || c == b'E') {
127✔
131
            return Err(ParseError::Syntax);
5✔
132
        }
133
        let mut parts = input_str.split(|&c| c == b'c');
105✔
134
        let significand = FixedDecimal::try_from(parts.next().ok_or(ParseError::Syntax)?)?;
16✔
135
        match parts.next() {
16✔
136
            None => Ok(CompactDecimal {
2✔
137
                significand,
2✔
138
                exponent: 0,
139
            }),
2✔
140
            Some(exponent_str) => {
14✔
141
                let exponent_str =
142
                    core::str::from_utf8(exponent_str).map_err(|_| ParseError::Syntax)?;
14✔
143
                if parts.next().is_some() {
14✔
UNCOV
144
                    return Err(ParseError::Syntax);
×
145
                }
146
                if exponent_str.is_empty()
39✔
147
                    || exponent_str.bytes().next() == Some(b'0')
13✔
148
                    || !exponent_str.bytes().all(|c| c.is_ascii_digit())
25✔
149
                {
150
                    return Err(ParseError::Syntax);
3✔
151
                }
152
                let exponent = exponent_str.parse().map_err(|_| ParseError::Limit)?;
11✔
153
                Ok(CompactDecimal {
11✔
154
                    significand,
11✔
155
                    exponent,
156
                })
157
            }
11✔
158
        }
159
    }
21✔
160
}
161

162
#[test]
163
fn test_compact_syntax_error() {
2✔
UNCOV
164
    #[derive(Debug)]
×
165
    struct TestCase {
166
        pub input_str: &'static str,
UNCOV
167
        pub expected_err: Option<ParseError>,
×
168
    }
169
    let cases = [
1✔
170
        TestCase {
1✔
171
            input_str: "-123e4",
172
            expected_err: Some(ParseError::Syntax),
1✔
173
        },
174
        TestCase {
1✔
175
            input_str: "-123c",
176
            expected_err: Some(ParseError::Syntax),
1✔
177
        },
178
        TestCase {
1✔
179
            input_str: "1c10",
180
            expected_err: None,
1✔
181
        },
182
        TestCase {
1✔
183
            input_str: "1E1c1",
184
            expected_err: Some(ParseError::Syntax),
1✔
185
        },
186
        TestCase {
1✔
187
            input_str: "1e1c1",
188
            expected_err: Some(ParseError::Syntax),
1✔
189
        },
190
        TestCase {
1✔
191
            input_str: "1c1e1",
192
            expected_err: Some(ParseError::Syntax),
1✔
193
        },
194
        TestCase {
1✔
195
            input_str: "1c1E1",
196
            expected_err: Some(ParseError::Syntax),
1✔
197
        },
198
        TestCase {
1✔
199
            input_str: "-1c01",
200
            expected_err: Some(ParseError::Syntax),
1✔
201
        },
202
        TestCase {
1✔
203
            input_str: "-1c-1",
204
            expected_err: Some(ParseError::Syntax),
1✔
205
        },
206
        TestCase {
1✔
207
            input_str: "-1c1",
208
            expected_err: None,
1✔
209
        },
210
    ];
211
    for cas in &cases {
11✔
212
        match CompactDecimal::from_str(cas.input_str) {
10✔
213
            Ok(dec) => {
2✔
214
                assert_eq!(cas.expected_err, None, "{cas:?}");
2✔
215
                assert_eq!(cas.input_str, dec.to_string(), "{cas:?}");
2✔
216
            }
2✔
217
            Err(err) => {
8✔
218
                assert_eq!(cas.expected_err, Some(err), "{cas:?}");
8✔
219
            }
220
        }
221
    }
222
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc