time/format_description/parse/
mod.rs

1//! Parser for format descriptions.
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5
6pub use self::strftime::{parse_strftime_borrowed, parse_strftime_owned};
7use crate::{error, format_description};
8
9/// A helper macro to make version restrictions simpler to read and write.
10macro_rules! version {
11    ($range:expr) => {
12        $range.contains(&VERSION)
13    };
14}
15
16/// A helper macro to statically validate the version (when used as a const parameter).
17macro_rules! validate_version {
18    ($version:ident) => {
19        const {
20            assert!($version >= 1 && $version <= 2);
21        }
22    };
23}
24
25mod ast;
26mod format_item;
27mod lexer;
28mod strftime;
29
30/// Parse a sequence of items from the format description.
31///
32/// The syntax for the format description can be found in [the
33/// book](https://time-rs.github.io/book/api/format-description.html).
34///
35/// This function exists for backward compatibility reasons. It is equivalent to calling
36/// `parse_borrowed::<1>(s)`. In the future, this function will be deprecated in favor of
37/// `parse_borrowed`.
38#[inline]
39pub fn parse(
40    s: &str,
41) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
42    parse_borrowed::<1>(s)
43}
44
45/// Parse a sequence of items from the format description.
46///
47/// The syntax for the format description can be found in [the
48/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
49/// description is provided as the const parameter. **It is recommended to use version 2.**
50#[inline]
51pub fn parse_borrowed<const VERSION: usize>(
52    s: &str,
53) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
54    validate_version!(VERSION);
55    let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
56    let ast = ast::parse::<_, VERSION>(&mut lexed);
57    let format_items = format_item::parse(ast);
58    Ok(format_items
59        .map(|res| res.and_then(TryInto::try_into))
60        .collect::<Result<_, _>>()?)
61}
62
63/// Parse a sequence of items from the format description.
64///
65/// The syntax for the format description can be found in [the
66/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
67/// description is provided as the const parameter.
68///
69/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
70/// that there is no lifetime that needs to be handled. **It is recommended to use version 2.**
71///
72/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
73#[inline]
74pub fn parse_owned<const VERSION: usize>(
75    s: &str,
76) -> Result<format_description::OwnedFormatItem, error::InvalidFormatDescription> {
77    validate_version!(VERSION);
78    let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
79    let ast = ast::parse::<_, VERSION>(&mut lexed);
80    let format_items = format_item::parse(ast);
81    let items = format_items.collect::<Result<Box<_>, _>>()?;
82    Ok(items.into())
83}
84
85/// Attach [`Location`] information to each byte in the iterator.
86#[inline]
87fn attach_location<'item>(
88    iter: impl Iterator<Item = &'item u8>,
89) -> impl Iterator<Item = (&'item u8, Location)> {
90    let mut byte_pos = 0;
91
92    iter.map(move |byte| {
93        let location = Location { byte: byte_pos };
94        byte_pos += 1;
95        (byte, location)
96    })
97}
98
99/// A location within a string.
100#[derive(Clone, Copy)]
101struct Location {
102    /// The zero-indexed byte of the string.
103    byte: u32,
104}
105
106impl Location {
107    /// Create a new [`Span`] from `self` to `other`.
108    #[inline]
109    const fn to(self, end: Self) -> Span {
110        Span { start: self, end }
111    }
112
113    /// Create a new [`Span`] consisting entirely of `self`.
114    #[inline]
115    const fn to_self(self) -> Span {
116        Span {
117            start: self,
118            end: self,
119        }
120    }
121
122    /// Offset the location by the provided amount.
123    ///
124    /// Note that this assumes the resulting location is on the same line as the original location.
125    #[must_use = "this does not modify the original value"]
126    #[inline]
127    const fn offset(&self, offset: u32) -> Self {
128        Self {
129            byte: self.byte + offset,
130        }
131    }
132
133    /// Create an error with the provided message at this location.
134    #[inline]
135    const fn error(self, message: &'static str) -> ErrorInner {
136        ErrorInner {
137            _message: message,
138            _span: Span {
139                start: self,
140                end: self,
141            },
142        }
143    }
144}
145
146/// A start and end point within a string.
147#[derive(Clone, Copy)]
148struct Span {
149    start: Location,
150    end: Location,
151}
152
153impl Span {
154    /// Obtain a `Span` pointing at the start of the pre-existing span.
155    #[must_use = "this does not modify the original value"]
156    #[inline]
157    const fn shrink_to_start(&self) -> Self {
158        Self {
159            start: self.start,
160            end: self.start,
161        }
162    }
163
164    /// Obtain a `Span` pointing at the end of the pre-existing span.
165    #[must_use = "this does not modify the original value"]
166    const fn shrink_to_end(&self) -> Self {
167        Self {
168            start: self.end,
169            end: self.end,
170        }
171    }
172
173    /// Obtain a `Span` that ends before the provided position of the pre-existing span.
174    #[must_use = "this does not modify the original value"]
175    #[inline]
176    const fn shrink_to_before(&self, pos: u32) -> Self {
177        Self {
178            start: self.start,
179            end: Location {
180                byte: self.start.byte + pos - 1,
181            },
182        }
183    }
184
185    /// Obtain a `Span` that starts after provided position to the end of the pre-existing span.
186    #[must_use = "this does not modify the original value"]
187    #[inline]
188    const fn shrink_to_after(&self, pos: u32) -> Self {
189        Self {
190            start: Location {
191                byte: self.start.byte + pos + 1,
192            },
193            end: self.end,
194        }
195    }
196
197    /// Create an error with the provided message at this span.
198    #[inline]
199    const fn error(self, message: &'static str) -> ErrorInner {
200        ErrorInner {
201            _message: message,
202            _span: self,
203        }
204    }
205}
206
207/// A value with an associated [`Span`].
208#[derive(Clone, Copy)]
209struct Spanned<T> {
210    /// The value.
211    value: T,
212    /// Where the value was in the format string.
213    span: Span,
214}
215
216impl<T> core::ops::Deref for Spanned<T> {
217    type Target = T;
218
219    #[inline]
220    fn deref(&self) -> &Self::Target {
221        &self.value
222    }
223}
224
225/// Helper trait to attach a [`Span`] to a value.
226trait SpannedValue: Sized {
227    /// Attach a [`Span`] to a value.
228    fn spanned(self, span: Span) -> Spanned<Self>;
229}
230
231impl<T> SpannedValue for T {
232    #[inline]
233    fn spanned(self, span: Span) -> Spanned<Self> {
234        Spanned { value: self, span }
235    }
236}
237
238/// The internal error type.
239struct ErrorInner {
240    /// The message displayed to the user.
241    _message: &'static str,
242    /// Where the error originated.
243    _span: Span,
244}
245
246/// A complete error description.
247struct Error {
248    /// The internal error.
249    _inner: Unused<ErrorInner>,
250    /// The error needed for interoperability with the rest of `time`.
251    public: error::InvalidFormatDescription,
252}
253
254impl From<Error> for error::InvalidFormatDescription {
255    #[inline]
256    fn from(error: Error) -> Self {
257        error.public
258    }
259}
260
261/// A value that may be used in the future, but currently is not.
262///
263/// This struct exists so that data can semantically be passed around without _actually_ passing it
264/// around. This way the data still exists if it is needed in the future.
265// `PhantomData` is not used directly because we don't want to introduce any trait implementations.
266struct Unused<T>(core::marker::PhantomData<T>);
267
268/// Indicate that a value is currently unused.
269#[inline]
270fn unused<T>(_: T) -> Unused<T> {
271    Unused(core::marker::PhantomData)
272}