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

zbraniecki / icu4x / 9014530096

08 May 2024 07:27PM UTC coverage: 76.402% (+0.2%) from 76.234%
9014530096

push

github

web-flow
Add missing std pointer-like impls for DataProvider, DynamicDataProvider (#4880)

0 of 3 new or added lines in 1 file covered. (0.0%)

3218 existing lines in 167 files now uncovered.

53328 of 69799 relevant lines covered (76.4%)

504343.42 hits per line

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

83.7
/utils/pattern/src/single.rs
1
// This file is part of ICU4X. For terms of use, please see the file
1✔
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
//! Code for the [`SinglePlaceholder`] pattern backend.
6

7
use core::convert::Infallible;
8
use core::{cmp::Ordering, str::FromStr};
9
use writeable::adapters::WriteableAsTryWriteableInfallible;
10
use writeable::Writeable;
11

12
use crate::common::*;
13
use crate::Error;
14

15
#[cfg(feature = "alloc")]
16
use alloc::string::String;
17

18
/// A singleton enum for the [`SinglePlaceholder`] pattern backend.
19
///
20
/// # Examples
21
///
22
/// ```
23
/// use core::cmp::Ordering;
24
/// use icu_pattern::PatternItem;
25
/// use icu_pattern::SinglePlaceholder;
26
/// use icu_pattern::SinglePlaceholderKey;
27
/// use icu_pattern::SinglePlaceholderPattern;
28
///
29
/// // Parse the string syntax and check the resulting data store:
30
/// let pattern =
31
///     SinglePlaceholderPattern::try_from_str("Hello, {0}!").unwrap();
32
///
33
/// assert_eq!(
34
///     pattern.iter().cmp(
35
///         [
36
///             PatternItem::Literal("Hello, "),
37
///             PatternItem::Placeholder(SinglePlaceholderKey::Singleton),
38
///             PatternItem::Literal("!")
39
///         ]
40
///         .into_iter()
41
///     ),
42
///     Ordering::Equal
43
/// );
44
/// ```
45
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
1✔
46
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
20✔
47
#[allow(clippy::exhaustive_enums)] // Singleton
48
pub enum SinglePlaceholderKey {
49
    Singleton,
50
}
51

52
impl FromStr for SinglePlaceholderKey {
53
    type Err = core::convert::Infallible;
54
    fn from_str(_: &str) -> Result<Self, Self::Err> {
72✔
55
        Ok(Self::Singleton)
56
    }
72✔
57
}
58

59
impl<W> PlaceholderValueProvider<SinglePlaceholderKey> for (W,)
60
where
61
    W: Writeable,
62
{
63
    type Error = Infallible;
64
    type W<'a> = WriteableAsTryWriteableInfallible<&'a W> where W: 'a;
65
    const LITERAL_PART: writeable::Part = crate::PATTERN_LITERAL_PART;
66
    fn value_for(&self, _key: SinglePlaceholderKey) -> (Self::W<'_>, writeable::Part) {
9✔
67
        (
9✔
68
            WriteableAsTryWriteableInfallible(&self.0),
9✔
69
            crate::PATTERN_PLACEHOLDER_PART,
70
        )
71
    }
9✔
72
}
73

74
impl<W> PlaceholderValueProvider<SinglePlaceholderKey> for [W; 1]
75
where
76
    W: Writeable,
77
{
78
    type Error = Infallible;
79
    type W<'a> = WriteableAsTryWriteableInfallible<&'a W> where W: 'a;
80
    const LITERAL_PART: writeable::Part = crate::PATTERN_LITERAL_PART;
81
    fn value_for(&self, _key: SinglePlaceholderKey) -> (Self::W<'_>, writeable::Part) {
82
        let [value] = self;
83
        (
84
            WriteableAsTryWriteableInfallible(value),
85
            crate::PATTERN_PLACEHOLDER_PART,
86
        )
87
    }
88
}
89

90
/// Backend for patterns containing zero or one placeholder.
91
///
92
/// This empty type is not constructible.
93
///
94
/// # Placeholder Keys
95
///
96
/// The placeholder is always [`SinglePlaceholderKey::Singleton`].
97
///
98
/// In [`Pattern::interpolate()`], pass a single-element array or tuple.
99
///
100
/// # Encoding Details
101
///
102
/// The first code point of the string is 1 plus the byte offset of the placeholder counting from
103
/// after that initial code point. If zero, there is no placeholder.
104
///
105
/// # Examples
106
///
107
/// Parsing a pattern into the encoding:
108
///
109
/// ```
110
/// use icu_pattern::Pattern;
111
/// use icu_pattern::SinglePlaceholder;
112
///
113
/// // Parse the string syntax and check the resulting data store:
114
/// let store = Pattern::<SinglePlaceholder, _>::try_from_str("Hello, {0}!")
115
///     .unwrap()
116
///     .take_store();
117
///
118
/// assert_eq!("\u{8}Hello, !", store);
119
/// ```
120
///
121
/// Example patterns supported by this backend:
122
///
123
/// ```
124
/// use icu_pattern::Pattern;
125
/// use icu_pattern::SinglePlaceholder;
126
///
127
/// // Single numeric placeholder:
128
/// assert_eq!(
129
///     Pattern::<SinglePlaceholder, _>::try_from_str("{0} days ago")
130
///         .unwrap()
131
///         .interpolate_to_string([5]),
132
///     "5 days ago",
133
/// );
134
///
135
/// // Single named placeholder:
136
/// assert_eq!(
137
///     Pattern::<SinglePlaceholder, _>::try_from_str("{name}")
138
///         .unwrap()
139
///         .interpolate_to_string(["Alice"]),
140
///     "Alice",
141
/// );
142
///
143
/// // No placeholder (note, the placeholder value is never accessed):
144
/// assert_eq!(
145
///     Pattern::<SinglePlaceholder, _>::try_from_str("yesterday")
146
///         .unwrap()
147
///         .interpolate_to_string(["hi"]),
148
///     "yesterday",
149
/// );
150
///
151
/// // Escaped placeholder and a real placeholder:
152
/// assert_eq!(
153
///     Pattern::<SinglePlaceholder, _>::try_from_str("'{0}' {1}")
154
///         .unwrap()
155
///         .interpolate_to_string(("hi",)),
156
///     "{0} hi",
157
/// );
158
/// ```
159
///
160
/// [`Pattern::interpolate()`]: crate::Pattern::interpolate
UNCOV
161
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
162
#[allow(clippy::exhaustive_enums)] // Empty Enum
163
pub enum SinglePlaceholder {}
164

165
impl crate::private::Sealed for SinglePlaceholder {}
166

167
impl PatternBackend for SinglePlaceholder {
168
    type PlaceholderKey<'a> = SinglePlaceholderKey;
169
    #[cfg(feature = "alloc")]
170
    type PlaceholderKeyCow<'a> = SinglePlaceholderKey;
171
    type Error<'a> = Infallible;
172
    type Store = str;
173
    type Iter<'a> = SinglePlaceholderPatternIterator<'a>;
174

175
    fn validate_store(store: &Self::Store) -> Result<(), Error> {
86✔
176
        let placeholder_offset_char = store.chars().next().ok_or(Error::InvalidPattern)?;
86✔
177
        let initial_offset = placeholder_offset_char.len_utf8();
86✔
178
        let placeholder_offset = placeholder_offset_char as usize;
86✔
179
        if placeholder_offset > store.len() - initial_offset + 1 {
86✔
180
            return Err(Error::InvalidPattern);
1✔
181
        }
182
        if placeholder_offset >= 0xD800 {
85✔
UNCOV
183
            return Err(Error::InvalidPattern);
×
184
        }
185
        Ok(())
85✔
186
    }
86✔
187

188
    fn iter_items(store: &Self::Store) -> Self::Iter<'_> {
43✔
189
        let placeholder_offset_char = match store.chars().next() {
43✔
190
            Some(i) => i,
43✔
191
            None => {
UNCOV
192
                debug_assert!(false);
×
193
                '\0'
194
            }
195
        };
196
        let initial_offset = placeholder_offset_char.len_utf8();
43✔
197
        SinglePlaceholderPatternIterator {
43✔
198
            store,
199
            placeholder_offset: placeholder_offset_char as usize + initial_offset - 1,
43✔
200
            current_offset: initial_offset,
201
        }
202
    }
43✔
203

204
    #[cfg(feature = "alloc")]
205
    fn try_from_items<
64✔
206
        'cow,
207
        'ph,
208
        I: Iterator<Item = Result<PatternItemCow<'cow, Self::PlaceholderKey<'ph>>, Error>>,
209
    >(
210
        items: I,
211
    ) -> Result<String, Error> {
212
        let mut result = String::new();
74✔
213
        let mut seen_placeholder = false;
69✔
214
        for item in items {
196✔
215
            match item? {
133✔
216
                PatternItemCow::Literal(s) => result.push_str(&s),
69✔
217
                PatternItemCow::Placeholder(_) if !seen_placeholder => {
64✔
218
                    seen_placeholder = true;
64✔
219
                    let placeholder_offset =
220
                        u32::try_from(result.len() + 1).map_err(|_| Error::InvalidPattern)?;
64✔
221
                    if placeholder_offset >= 0xD800 {
64✔
UNCOV
222
                        return Err(Error::InvalidPattern);
×
223
                    }
224
                    let placeholder_offset_char =
225
                        char::try_from(placeholder_offset).map_err(|_| Error::InvalidPattern)?;
64✔
226
                    result.insert(0, placeholder_offset_char);
64✔
227
                }
228
                PatternItemCow::Placeholder(_) => {
229
                    return Err(Error::InvalidPattern);
×
230
                }
231
            }
UNCOV
232
        }
×
233
        if !seen_placeholder {
63✔
UNCOV
234
            result.insert(0, '\0');
×
235
        }
236
        Ok(result)
63✔
237
    }
63✔
238
}
239

240
#[doc(hidden)] // TODO(#4467): Should be internal
UNCOV
241
#[derive(Debug)]
×
242
pub struct SinglePlaceholderPatternIterator<'a> {
UNCOV
243
    store: &'a str,
×
UNCOV
244
    placeholder_offset: usize,
×
UNCOV
245
    current_offset: usize,
×
246
}
247

248
// Note: This impl is not exported via public bounds, but it might be in the
249
// future, and the compiler might be able to find it. The code is also
250
// reachable from `Iterator::size_hint`.
251
impl ExactSizeIterator for SinglePlaceholderPatternIterator<'_> {
252
    fn len(&self) -> usize {
42✔
253
        let placeholder_offset_char = match self.store.chars().next() {
42✔
254
            Some(i) => i,
42✔
255
            None => {
UNCOV
256
                debug_assert!(false);
×
257
                '\0'
258
            }
259
        };
260
        let initial_offset = placeholder_offset_char.len_utf8();
42✔
261
        let placeholder_offset = placeholder_offset_char as usize + initial_offset - 1;
42✔
262
        let store_len = self.store.len();
42✔
263
        if placeholder_offset < initial_offset {
42✔
264
            // No placeholder
265
            if initial_offset < store_len {
1✔
266
                // No placeholder, non-empty literal
267
                1
1✔
268
            } else {
269
                // No placeholder, empty literal
UNCOV
270
                0
×
271
            }
272
        } else if placeholder_offset == initial_offset {
41✔
273
            // Has placeholder, empty prefix
274
            if initial_offset < store_len {
17✔
275
                // Has placeholder, empty prefix, non-empty suffix
276
                2
16✔
277
            } else {
278
                // Has placeholder, empty prefix, empty suffix
279
                1
1✔
280
            }
281
        } else if placeholder_offset < store_len {
24✔
282
            // Has placeholder, non-empty prefix, non-empty suffix
283
            3
23✔
284
        } else {
285
            // Has placeholder, non-empty prefix, empty suffix
286
            2
1✔
287
        }
288
    }
42✔
289
}
290

291
impl<'a> Iterator for SinglePlaceholderPatternIterator<'a> {
292
    type Item = PatternItem<'a, SinglePlaceholderKey>;
293
    fn next(&mut self) -> Option<Self::Item> {
151✔
294
        match self.current_offset.cmp(&self.placeholder_offset) {
151✔
295
            Ordering::Less => {
296
                // Prefix
297
                let literal_str = match self.store.get(self.current_offset..self.placeholder_offset)
25✔
298
                {
299
                    Some(s) => s,
25✔
300
                    None => {
UNCOV
301
                        debug_assert!(false, "offsets are in range");
×
302
                        ""
303
                    }
304
                };
305
                self.current_offset = self.placeholder_offset;
25✔
306
                Some(PatternItem::Literal(literal_str))
25✔
307
            }
25✔
308
            Ordering::Equal => {
309
                // Placeholder
310
                self.placeholder_offset = 0;
42✔
311
                Some(PatternItem::Placeholder(SinglePlaceholderKey::Singleton))
42✔
312
            }
313
            Ordering::Greater => {
314
                // Suffix or end of string
315
                let literal_str = match self.store.get(self.current_offset..) {
84✔
316
                    Some(s) => s,
84✔
317
                    None => {
UNCOV
318
                        debug_assert!(false, "offsets are in range");
×
319
                        ""
320
                    }
321
                };
322
                if literal_str.is_empty() {
84✔
323
                    // End of string
324
                    None
43✔
325
                } else {
326
                    // Suffix
327
                    self.current_offset = self.store.len();
41✔
328
                    Some(PatternItem::Literal(literal_str))
41✔
329
                }
330
            }
331
        }
332
    }
151✔
333

334
    fn size_hint(&self) -> (usize, Option<usize>) {
42✔
335
        let len = self.len();
42✔
336
        (len, Some(len))
42✔
337
    }
42✔
338
}
339

340
// TODO(#1668):  Add more tests
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