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