Coverage Report

Created: 2026-09-01 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.5/src/cursor.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
//! Types for walking stepwise through a trie.
6
//!
7
//! For examples, see the `.cursor()` functions
8
//! and the `Cursor` types in this module.
9
10
use crate::reader;
11
use crate::ZeroAsciiIgnoreCaseTrie;
12
use crate::ZeroTrieSimpleAscii;
13
14
use core::fmt;
15
16
impl<Store> ZeroTrieSimpleAscii<Store>
17
where
18
    Store: AsRef<[u8]> + ?Sized,
19
{
20
    /// Gets a cursor into the current trie.
21
    ///
22
    /// Useful to query a trie with data that is not a slice.
23
    ///
24
    /// This is currently supported only on [`ZeroTrieSimpleAscii`]
25
    /// and [`ZeroAsciiIgnoreCaseTrie`].
26
    ///
27
    /// # Examples
28
    ///
29
    /// Get a value out of a trie by [writing](fmt::Write) it to the cursor:
30
    ///
31
    /// ```
32
    /// use core::fmt::Write;
33
    /// use zerotrie::ZeroTrieSimpleAscii;
34
    ///
35
    /// // A trie with two values: "abc" and "abcdef"
36
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
37
    ///
38
    /// // Get out the value for "abc"
39
    /// let mut cursor = trie.cursor();
40
    /// write!(&mut cursor, "abc");
41
    /// assert_eq!(cursor.take_value(), Some(0));
42
    /// ```
43
    ///
44
    /// Find the longest prefix match:
45
    ///
46
    /// ```
47
    /// use zerotrie::ZeroTrieSimpleAscii;
48
    ///
49
    /// // A trie with two values: "abc" and "abcdef"
50
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
51
    ///
52
    /// // Find the longest prefix of the string "abcdxy":
53
    /// let query = b"abcdxy";
54
    /// let mut longest_prefix = 0;
55
    /// let mut cursor = trie.cursor();
56
    /// for (i, b) in query.iter().enumerate() {
57
    ///     // Checking is_empty() is not required, but it is
58
    ///     // good for efficiency
59
    ///     if cursor.is_empty() {
60
    ///         break;
61
    ///     }
62
    ///     if cursor.take_value().is_some() {
63
    ///         longest_prefix = i;
64
    ///     }
65
    ///     cursor.step(*b);
66
    /// }
67
    ///
68
    /// // The longest prefix is "abc" which is length 3:
69
    /// assert_eq!(longest_prefix, 3);
70
    /// ```
71
    #[inline]
72
0
    pub fn cursor(&self) -> ZeroTrieSimpleAsciiCursor<'_> {
73
0
        ZeroTrieSimpleAsciiCursor {
74
0
            trie: self.as_borrowed_slice(),
75
0
        }
76
0
    }
Unexecuted instantiation: <zerotrie::zerotrie::ZeroTrieSimpleAscii<zerovec::zerovec::ZeroVec<u8>>>::cursor
Unexecuted instantiation: <zerotrie::zerotrie::ZeroTrieSimpleAscii<&[u8]>>::cursor
Unexecuted instantiation: <zerotrie::zerotrie::ZeroTrieSimpleAscii<_>>::cursor
77
78
    /// Queries the trie using a closure that writes to a cursor.
79
    ///
80
    /// Third-party string-like types can integrate with this API by returning a function
81
    /// with the required signature.
82
    ///
83
    /// # Examples
84
    ///
85
    /// Using the `writeable` crate:
86
    ///
87
    /// ```
88
    /// use writeable::Writeable;
89
    /// use zerotrie::ZeroTrieSimpleAscii;
90
    ///
91
    /// // A trie with two values: "abc" and "abcdef"
92
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
93
    ///
94
    /// // Get out the value for "abc"
95
    /// let needle = writeable::concat_writeable!("a", "bc");
96
    /// assert_eq!(
97
    ///     trie.get_with_write_fn(|sink| needle.write_to(sink)),
98
    ///     Some(0)
99
    /// );
100
    /// ```
101
    #[inline]
102
0
    pub fn get_with_write_fn<'a>(
103
0
        &'a self,
104
0
        write_fn: impl for<'b> FnOnce(&'b mut ZeroTrieSimpleAsciiCursor<'a>) -> fmt::Result,
105
0
    ) -> Option<usize> {
106
0
        let mut cursor = self.cursor();
107
0
        write_fn(&mut cursor).ok()?;
108
0
        cursor.take_value()
109
0
    }
110
}
111
112
impl<Store> ZeroAsciiIgnoreCaseTrie<Store>
113
where
114
    Store: AsRef<[u8]> + ?Sized,
115
{
116
    /// Gets a cursor into the current trie.
117
    ///
118
    /// Useful to query a trie with data that is not a slice.
119
    ///
120
    /// This is currently supported only on [`ZeroTrieSimpleAscii`]
121
    /// and [`ZeroAsciiIgnoreCaseTrie`].
122
    ///
123
    /// # Examples
124
    ///
125
    /// Get a value out of a trie by [writing](fmt::Write) it to the cursor:
126
    ///
127
    /// ```
128
    /// use core::fmt::Write;
129
    /// use zerotrie::ZeroAsciiIgnoreCaseTrie;
130
    ///
131
    /// // A trie with two values: "aBc" and "aBcdEf"
132
    /// let trie = ZeroAsciiIgnoreCaseTrie::from_bytes(b"aBc\x80dEf\x81");
133
    ///
134
    /// // Get out the value for "abc" (case-insensitive!)
135
    /// let mut cursor = trie.cursor();
136
    /// write!(&mut cursor, "abc");
137
    /// assert_eq!(cursor.take_value(), Some(0));
138
    /// ```
139
    ///
140
    /// For more examples, see [`ZeroTrieSimpleAscii::cursor`].
141
    #[inline]
142
0
    pub fn cursor(&self) -> ZeroAsciiIgnoreCaseTrieCursor<'_> {
143
0
        ZeroAsciiIgnoreCaseTrieCursor {
144
0
            trie: self.as_borrowed_slice(),
145
0
        }
146
0
    }
147
148
    /// Queries the trie using a closure that writes to a cursor.
149
    ///
150
    /// Third-party string-like types can integrate with this API by returning a function
151
    /// with the required signature.
152
    ///
153
    /// # Examples
154
    ///
155
    /// Using the `writeable` crate:
156
    ///
157
    /// ```
158
    /// use writeable::Writeable;
159
    /// use zerotrie::ZeroAsciiIgnoreCaseTrie;
160
    ///
161
    /// // A trie with two values: "aBc" and "aBcdEf"
162
    /// let trie = ZeroAsciiIgnoreCaseTrie::from_bytes(b"aBc\x80dEf\x81");
163
    ///
164
    /// // Get out the value for "abc"
165
    /// let needle = writeable::concat_writeable!("a", "bc");
166
    /// assert_eq!(
167
    ///     trie.get_with_write_fn(|sink| needle.write_to(sink)),
168
    ///     Some(0)
169
    /// );
170
    /// ```
171
    #[inline]
172
0
    pub fn get_with_write_fn<'a>(
173
0
        &'a self,
174
0
        write_fn: impl for<'b> FnOnce(&'b mut ZeroAsciiIgnoreCaseTrieCursor<'a>) -> fmt::Result,
175
0
    ) -> Option<usize> {
176
0
        let mut cursor = self.cursor();
177
0
        write_fn(&mut cursor).ok()?;
178
0
        cursor.take_value()
179
0
    }
180
}
181
182
impl<'a> ZeroTrieSimpleAscii<&'a [u8]> {
183
    /// Same as [`ZeroTrieSimpleAscii::cursor()`] but moves self to avoid
184
    /// having to doubly anchor the trie to the stack.
185
    #[inline]
186
0
    pub fn into_cursor(self) -> ZeroTrieSimpleAsciiCursor<'a> {
187
0
        ZeroTrieSimpleAsciiCursor { trie: self }
188
0
    }
189
}
190
191
impl<'a> ZeroAsciiIgnoreCaseTrie<&'a [u8]> {
192
    /// Same as [`ZeroAsciiIgnoreCaseTrie::cursor()`] but moves self to avoid
193
    /// having to doubly anchor the trie to the stack.
194
    #[inline]
195
0
    pub fn into_cursor(self) -> ZeroAsciiIgnoreCaseTrieCursor<'a> {
196
0
        ZeroAsciiIgnoreCaseTrieCursor { trie: self }
197
0
    }
198
}
199
200
/// A cursor into a [`ZeroTrieSimpleAscii`], useful for stepwise lookup.
201
///
202
/// For examples, see [`ZeroTrieSimpleAscii::cursor()`].
203
// Clone but not Copy: <https://stackoverflow.com/q/32324251/1407170>
204
#[derive(Debug, Clone)]
205
pub struct ZeroTrieSimpleAsciiCursor<'a> {
206
    trie: ZeroTrieSimpleAscii<&'a [u8]>,
207
}
208
209
/// A cursor into a [`ZeroAsciiIgnoreCaseTrie`], useful for stepwise lookup.
210
///
211
/// For examples, see [`ZeroAsciiIgnoreCaseTrie::cursor()`].
212
// Clone but not Copy: <https://stackoverflow.com/q/32324251/1407170>
213
#[derive(Debug, Clone)]
214
pub struct ZeroAsciiIgnoreCaseTrieCursor<'a> {
215
    trie: ZeroAsciiIgnoreCaseTrie<&'a [u8]>,
216
}
217
218
/// Information about a probed edge.
219
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
220
#[non_exhaustive] // no need to destructure or construct this in userland
221
pub struct AsciiProbeResult {
222
    /// The character's byte value between this node and its parent.
223
    pub byte: u8,
224
    /// The number of siblings of this node, _including itself_.
225
    pub total_siblings: u8,
226
}
227
228
impl<'a> ZeroTrieSimpleAsciiCursor<'a> {
229
    /// Steps the cursor one character into the trie based on the character's byte value.
230
    ///
231
    /// # Examples
232
    ///
233
    /// Unrolled loop checking for string presence at every step:
234
    ///
235
    /// ```
236
    /// use zerotrie::ZeroTrieSimpleAscii;
237
    ///
238
    /// // A trie with two values: "abc" and "abcdef"
239
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
240
    ///
241
    /// // Search the trie for the string "abcdxy"
242
    /// let mut cursor = trie.cursor();
243
    /// assert_eq!(cursor.take_value(), None); // ""
244
    /// cursor.step(b'a');
245
    /// assert_eq!(cursor.take_value(), None); // "a"
246
    /// cursor.step(b'b');
247
    /// assert_eq!(cursor.take_value(), None); // "ab"
248
    /// cursor.step(b'c');
249
    /// assert_eq!(cursor.take_value(), Some(0)); // "abc"
250
    /// cursor.step(b'd');
251
    /// assert_eq!(cursor.take_value(), None); // "abcd"
252
    /// assert!(!cursor.is_empty());
253
    /// cursor.step(b'x'); // no strings have the prefix "abcdx"
254
    /// assert!(cursor.is_empty());
255
    /// assert_eq!(cursor.take_value(), None); // "abcdx"
256
    /// cursor.step(b'y');
257
    /// assert_eq!(cursor.take_value(), None); // "abcdxy"
258
    /// ```
259
    ///
260
    /// If the byte is not ASCII, the cursor will become empty:
261
    ///
262
    /// ```
263
    /// use zerotrie::ZeroTrieSimpleAscii;
264
    ///
265
    /// // A trie with two values: "abc" and "abcdef"
266
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
267
    ///
268
    /// let mut cursor = trie.cursor();
269
    /// assert_eq!(cursor.take_value(), None); // ""
270
    /// cursor.step(b'a');
271
    /// assert_eq!(cursor.take_value(), None); // "a"
272
    /// cursor.step(b'b');
273
    /// assert_eq!(cursor.take_value(), None); // "ab"
274
    /// cursor.step(b'\xFF');
275
    /// assert!(cursor.is_empty());
276
    /// assert_eq!(cursor.take_value(), None);
277
    /// ```
278
    #[inline]
279
0
    pub fn step(&mut self, byte: u8) {
280
0
        reader::step_parameterized::<ZeroTrieSimpleAscii<[u8]>>(&mut self.trie.store, byte);
281
0
    }
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::step
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::step
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::step
282
283
    /// Takes the value at the current position.
284
    ///
285
    /// Calling this function on a new cursor is equivalent to calling `.get()`
286
    /// with the empty string (except that it can only be called once).
287
    ///
288
    /// # Examples
289
    ///
290
    /// ```
291
    /// use zerotrie::ZeroTrieSimpleAscii;
292
    ///
293
    /// // A trie with two values: "" and "abc"
294
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"\x80abc\x81");
295
    ///
296
    /// assert_eq!(Some(0), trie.get(""));
297
    /// let mut cursor = trie.cursor();
298
    /// assert_eq!(Some(0), cursor.take_value());
299
    /// assert_eq!(None, cursor.take_value());
300
    /// ```
301
    #[inline]
302
0
    pub fn take_value(&mut self) -> Option<usize> {
303
0
        reader::take_value(&mut self.trie.store)
304
0
    }
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::take_value
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::take_value
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::take_value
305
306
    /// Steps the cursor one character into the trie based on an edge index,
307
    /// returning the corresponding character as a byte.
308
    ///
309
    /// This function is similar to [`Self::step()`], but it takes an index instead of a char.
310
    /// This enables stepwise iteration over the contents of the trie.
311
    ///
312
    /// If there are multiple possibilities for the next byte, the `index` argument allows
313
    /// visiting them in order. Since this function steps the cursor, the cursor must be
314
    /// cloned (a cheap operation) in order to visit multiple children.
315
    ///
316
    /// # Examples
317
    ///
318
    /// Continually query index 0 to extract the first item from a trie:
319
    ///
320
    /// ```
321
    /// use zerotrie::ZeroTrieSimpleAscii;
322
    ///
323
    /// let data: &[(String, usize)] = &[
324
    ///     ("ab".to_string(), 111),
325
    ///     ("abcxyz".to_string(), 22),
326
    ///     ("abde".to_string(), 333),
327
    ///     ("afg".to_string(), 44),
328
    /// ];
329
    ///
330
    /// let trie: ZeroTrieSimpleAscii<Vec<u8>> =
331
    ///     data.iter().map(|(s, v)| (s.as_str(), *v)).collect();
332
    ///
333
    /// let mut cursor = trie.cursor();
334
    /// let mut key = String::new();
335
    /// let value = loop {
336
    ///     if let Some(value) = cursor.take_value() {
337
    ///         break value;
338
    ///     }
339
    ///     let probe_result = cursor.probe(0).unwrap();
340
    ///     key.push(char::from(probe_result.byte));
341
    /// };
342
    ///
343
    /// assert_eq!(key, "ab");
344
    /// assert_eq!(value, 111);
345
    /// ```
346
    ///
347
    /// Stepwise iterate over all entries in the trie:
348
    ///
349
    /// ```
350
    /// # use zerotrie::ZeroTrieSimpleAscii;
351
    /// # let data: &[(String, usize)] = &[
352
    /// #     ("ab".to_string(), 111),
353
    /// #     ("abcxyz".to_string(), 22),
354
    /// #     ("abde".to_string(), 333),
355
    /// #     ("afg".to_string(), 44)
356
    /// # ];
357
    /// # let trie: ZeroTrieSimpleAscii<Vec<u8>> = data
358
    /// #     .iter()
359
    /// #     .map(|(s, v)| (s.as_str(), *v))
360
    /// #     .collect();
361
    /// // (trie built as in previous example)
362
    ///
363
    /// // Initialize the iteration at the first child of the trie.
364
    /// let mut stack = Vec::from([(trie.cursor(), 0, 0)]);
365
    /// let mut key = Vec::new();
366
    /// let mut results = Vec::new();
367
    /// loop {
368
    ///     let Some((mut cursor, index, suffix_len)) = stack.pop() else {
369
    ///         // Nothing left in the trie.
370
    ///         break;
371
    ///     };
372
    ///     // Check to see if there is a value at the current node.
373
    ///     if let Some(value) = cursor.take_value() {
374
    ///         results.push((String::from_utf8(key.clone()).unwrap(), value));
375
    ///     }
376
    ///     // Now check for children of the current node.
377
    ///     let mut sub_cursor = cursor.clone();
378
    ///     if let Some(probe_result) = sub_cursor.probe(index) {
379
    ///         // Found a child. Add the current byte edge to the key.
380
    ///         key.push(probe_result.byte);
381
    ///         // Add the child to the stack, and also add back the current
382
    ///         // node if there are more siblings to visit.
383
    ///         if index + 1 < probe_result.total_siblings as usize {
384
    ///             stack.push((cursor, index + 1, suffix_len));
385
    ///             stack.push((sub_cursor, 0, 1));
386
    ///         } else {
387
    ///             stack.push((sub_cursor, 0, suffix_len + 1));
388
    ///         }
389
    ///     } else {
390
    ///         // No more children. Pop this node's bytes from the key.
391
    ///         for _ in 0..suffix_len {
392
    ///             key.pop();
393
    ///         }
394
    ///     }
395
    /// }
396
    ///
397
    /// assert_eq!(&results, data);
398
    /// ```
399
0
    pub fn probe(&mut self, index: usize) -> Option<AsciiProbeResult> {
400
0
        reader::probe_parameterized::<ZeroTrieSimpleAscii<[u8]>>(&mut self.trie.store, index)
401
0
    }
402
403
    /// Checks whether the cursor points to an empty trie.
404
    ///
405
    /// Use this to determine when to stop iterating.
406
    #[inline]
407
0
    pub fn is_empty(&self) -> bool {
408
0
        self.trie.is_empty()
409
0
    }
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::is_empty
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::is_empty
Unexecuted instantiation: <zerotrie::cursor::ZeroTrieSimpleAsciiCursor>::is_empty
410
411
    /// Returns a trie for all suffixes that begin with the previously stepped
412
    /// bytes.
413
    ///
414
    /// # Examples
415
    ///
416
    /// ```
417
    /// use zerotrie::ZeroTrieSimpleAscii;
418
    ///
419
    /// // A trie with two values: "abc" and "abcdef"
420
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
421
    ///
422
    /// // Consume the prefix "ab"
423
    /// let mut cursor = trie.cursor();
424
    /// cursor.step(b'a');
425
    /// cursor.step(b'b');
426
    /// let suffix_trie = cursor.into_suffix_trie();
427
    ///
428
    /// // The suffix trie contains the strings "c" and "cdef"
429
    /// assert_eq!(suffix_trie.get("c"), Some(0));
430
    /// assert_eq!(suffix_trie.get("cdef"), Some(1));
431
    /// ```
432
0
    pub fn into_suffix_trie(self) -> ZeroTrieSimpleAscii<&'a [u8]> {
433
0
        self.trie
434
0
    }
435
}
436
437
impl<'a> ZeroAsciiIgnoreCaseTrieCursor<'a> {
438
    /// Steps the cursor one byte into the trie.
439
    ///
440
    /// Returns the byte if matched, which may be a different case than the input byte.
441
    /// If this function returns `None`, any lookup loops can be terminated.
442
    ///
443
    /// # Examples
444
    ///
445
    /// Normalize the case of a value by stepping through an ignore-case trie:
446
    ///
447
    /// ```
448
    /// use std::borrow::Cow;
449
    /// use zerotrie::ZeroAsciiIgnoreCaseTrie;
450
    ///
451
    /// // A trie with two values: "aBc" and "aBcdEf"
452
    /// let trie = ZeroAsciiIgnoreCaseTrie::from_bytes(b"aBc\x80dEf\x81");
453
    ///
454
    /// // Get out the value for "abc" and normalize the key string
455
    /// let mut cursor = trie.cursor();
456
    /// let mut key_str = Cow::Borrowed("abc".as_bytes());
457
    /// let mut i = 0;
458
    /// let value = loop {
459
    ///     let Some(&input_byte) = key_str.get(i) else {
460
    ///         break cursor.take_value();
461
    ///     };
462
    ///     let Some(matched_byte) = cursor.step(input_byte) else {
463
    ///         break None;
464
    ///     };
465
    ///     if matched_byte != input_byte {
466
    ///         key_str.to_mut()[i] = matched_byte;
467
    ///     }
468
    ///     i += 1;
469
    /// };
470
    ///
471
    /// assert_eq!(value, Some(0));
472
    /// assert_eq!(&*key_str, "aBc".as_bytes());
473
    /// ```
474
    ///
475
    /// For more examples, see [`ZeroTrieSimpleAsciiCursor::step`].
476
    #[inline]
477
0
    pub fn step(&mut self, byte: u8) -> Option<u8> {
478
0
        reader::step_parameterized::<ZeroAsciiIgnoreCaseTrie<[u8]>>(&mut self.trie.store, byte)
479
0
    }
480
481
    /// Takes the value at the current position.
482
    ///
483
    /// For more details, see [`ZeroTrieSimpleAsciiCursor::take_value`].
484
    #[inline]
485
0
    pub fn take_value(&mut self) -> Option<usize> {
486
0
        reader::take_value(&mut self.trie.store)
487
0
    }
488
489
    /// Probes the next byte in the cursor.
490
    ///
491
    /// For more details, see [`ZeroTrieSimpleAsciiCursor::probe`].
492
0
    pub fn probe(&mut self, index: usize) -> Option<AsciiProbeResult> {
493
0
        reader::probe_parameterized::<ZeroAsciiIgnoreCaseTrie<[u8]>>(&mut self.trie.store, index)
494
0
    }
495
496
    /// Checks whether the cursor points to an empty trie.
497
    ///
498
    /// For more details, see [`ZeroTrieSimpleAsciiCursor::is_empty`].
499
    #[inline]
500
0
    pub fn is_empty(&self) -> bool {
501
0
        self.trie.is_empty()
502
0
    }
503
504
    /// Returns a trie for all suffixes that begin with the previously stepped
505
    /// bytes.
506
    ///
507
    /// # Examples
508
    ///
509
    /// ```
510
    /// use zerotrie::ZeroAsciiIgnoreCaseTrie;
511
    ///
512
    /// // A trie with two values: "aBc" and "aBcdEf"
513
    /// let trie = ZeroAsciiIgnoreCaseTrie::from_bytes(b"aBc\x80dEf\x81");
514
    ///
515
    /// // Consume the prefix "ab"
516
    /// let mut cursor = trie.cursor();
517
    /// cursor.step(b'a');
518
    /// cursor.step(b'b');
519
    /// let suffix_trie = cursor.into_suffix_trie();
520
    ///
521
    /// // The suffix trie contains the strings "c" and "cdef" (case-insensitive!)
522
    /// assert_eq!(suffix_trie.get("c"), Some(0));
523
    /// assert_eq!(suffix_trie.get("CDEF"), Some(1));
524
    /// ```
525
0
    pub fn into_suffix_trie(self) -> ZeroAsciiIgnoreCaseTrie<&'a [u8]> {
526
0
        self.trie
527
0
    }
528
}
529
530
impl fmt::Write for ZeroTrieSimpleAsciiCursor<'_> {
531
    /// Steps the cursor through each ASCII byte of the string.
532
    ///
533
    /// If the string contains non-ASCII chars, an error is returned.
534
    ///
535
    /// # Examples
536
    ///
537
    /// ```
538
    /// use core::fmt::Write;
539
    /// use zerotrie::ZeroTrieSimpleAscii;
540
    ///
541
    /// // A trie with two values: "abc" and "abcdef"
542
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
543
    ///
544
    /// let mut cursor = trie.cursor();
545
    /// cursor.write_str("abcdxy").expect("all ASCII");
546
    /// cursor.write_str("🚂").expect_err("non-ASCII");
547
    /// ```
548
0
    fn write_str(&mut self, s: &str) -> fmt::Result {
549
0
        for b in s.bytes() {
550
0
            if !b.is_ascii() {
551
0
                return Err(fmt::Error);
552
0
            }
553
0
            self.step(b);
554
        }
555
0
        Ok(())
556
0
    }
557
558
    /// Equivalent to [`ZeroTrieSimpleAsciiCursor::step()`], except returns
559
    /// an error if the char is non-ASCII.
560
    ///
561
    /// # Examples
562
    ///
563
    /// ```
564
    /// use core::fmt::Write;
565
    /// use zerotrie::ZeroTrieSimpleAscii;
566
    ///
567
    /// // A trie with two values: "abc" and "abcdef"
568
    /// let trie = ZeroTrieSimpleAscii::from_bytes(b"abc\x80def\x81");
569
    ///
570
    /// let mut cursor = trie.cursor();
571
    /// cursor.write_char('a').expect("ASCII");
572
    /// cursor.write_char('x').expect("ASCII");
573
    /// cursor.write_char('🚂').expect_err("non-ASCII");
574
    /// ```
575
0
    fn write_char(&mut self, c: char) -> fmt::Result {
576
0
        if !c.is_ascii() {
577
0
            return Err(fmt::Error);
578
0
        }
579
0
        self.step(c as u8);
580
0
        Ok(())
581
0
    }
582
}
583
584
impl fmt::Write for ZeroAsciiIgnoreCaseTrieCursor<'_> {
585
    /// Steps the cursor through each ASCII byte of the string.
586
    ///
587
    /// If the string contains non-ASCII chars, an error is returned.
588
0
    fn write_str(&mut self, s: &str) -> fmt::Result {
589
0
        for b in s.bytes() {
590
0
            if !b.is_ascii() {
591
0
                return Err(fmt::Error);
592
0
            }
593
0
            self.step(b);
594
        }
595
0
        Ok(())
596
0
    }
597
598
    /// Equivalent to [`ZeroAsciiIgnoreCaseTrieCursor::step()`], except returns
599
    /// an error if the char is non-ASCII.
600
0
    fn write_char(&mut self, c: char) -> fmt::Result {
601
0
        if !c.is_ascii() {
602
0
            return Err(fmt::Error);
603
0
        }
604
0
        self.step(c as u8);
605
0
        Ok(())
606
0
    }
607
}