Skip to main content

ixdtf/
core.rs

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//! Core functionality for `ixdtf`'s parsers
6
7use crate::encoding::EncodingType;
8use crate::{ParseError, ParserResult};
9
10// ==== Mini cursor implementation for Iso8601 targets ====
11
12/// `Cursor` is a small cursor implementation for parsing Iso8601 grammar.
13#[derive(Debug)]
14pub(crate) struct Cursor<'a, T: EncodingType> {
15    pos: usize,
16    source: &'a [T::CodeUnit],
17}
18
19impl<'a, T: EncodingType> Cursor<'a, T> {
20    /// Create a new cursor from a source UTF8 string.
21    #[must_use]
22    pub fn new(source: &'a [T::CodeUnit]) -> Self {
23        Self { pos: 0, source }
24    }
25
26    /// Returns a string value from a slice of the cursor.
27    pub(crate) fn slice(&self, start: usize, end: usize) -> Option<&'a [T::CodeUnit]> {
28        T::slice(self.source, start, end)
29    }
30
31    /// Get current position
32    pub(crate) const fn pos(&self) -> usize {
33        self.pos
34    }
35
36    /// Get current position
37    pub(crate) fn set_position(&mut self, pos: usize) {
38        self.pos = pos;
39    }
40
41    /// Peek the value at next position (current + 1).
42    pub(crate) fn peek(&self) -> ParserResult<Option<u8>> {
43        self.peek_n(1)
44    }
45
46    /// Returns current position in source as `char`.
47    pub(crate) fn current(&self) -> ParserResult<Option<u8>> {
48        self.peek_n(0)
49    }
50
51    /// Peeks the value at `n` as a `char`.
52    pub(crate) fn peek_n(&self, n: usize) -> ParserResult<Option<u8>> {
53        T::get_ascii(self.source, self.pos + n)
54    }
55
56    /// Runs the provided check on the current position.
57    pub(crate) fn check<F>(&self, f: F) -> ParserResult<Option<bool>>
58    where
59        F: FnOnce(u8) -> bool,
60    {
61        Ok(self.current()?.map(f))
62    }
63
64    /// Runs the provided check on current position returns the default value if None.
65    pub(crate) fn check_or<F>(&self, default: bool, f: F) -> ParserResult<bool>
66    where
67        F: FnOnce(u8) -> bool,
68    {
69        Ok(self.current()?.map_or(default, f))
70    }
71
72    /// Returns `Cursor`'s current char and advances to the next position.
73    pub(crate) fn next(&mut self) -> ParserResult<Option<u8>> {
74        let result = self.current();
75        self.advance_n(1);
76        result
77    }
78
79    /// Returns the next value as a digit
80    ///
81    /// # Errors
82    ///   - Returns an [`ParseError::AbruptEnd`] error if cursor ends.
83    pub(crate) fn next_digit(&mut self) -> ParserResult<Option<u8>> {
84        let ascii_char = self.next_or(ParseError::AbruptEnd { location: "digit" })?;
85        if ascii_char.is_ascii_digit() {
86            Ok(Some(ascii_char - 48))
87        } else {
88            Ok(None)
89        }
90    }
91
92    /// A utility next method that returns an [`ParseError::AbruptEnd`] error if invalid.
93    pub(crate) fn next_or(&mut self, err: ParseError) -> ParserResult<u8> {
94        self.next()?.ok_or(err)
95    }
96
97    /// Advances the cursor's position by n code points.
98    pub(crate) fn advance_n(&mut self, n: usize) {
99        self.pos += n;
100    }
101
102    // Advances the cursor by 1 code point.
103    pub(crate) fn advance(&mut self) {
104        self.advance_n(1)
105    }
106
107    /// Utility function to advance when a condition is true
108    pub(crate) fn advance_if(&mut self, condition: bool) {
109        if condition {
110            self.advance();
111        }
112    }
113
114    /// Closes the current cursor by checking if all contents have been consumed. If not, returns an error for invalid syntax.
115    pub(crate) fn close(&mut self) -> ParserResult<()> {
116        if self.pos < self.source.len() {
117            return Err(ParseError::InvalidEnd);
118        }
119        Ok(())
120    }
121}