1//! Parser for format descriptions.
23use alloc::vec::Vec;
45use self::lexer_ast::parse_generic;
6use self::sealed::{Version, VersionedParser};
7pub use self::strftime::{parse_strftime_borrowed, parse_strftime_owned};
8use crate::error;
9use crate::format_description::{BorrowedFormatItem, FormatDescriptionV3, OwnedFormatItem};
1011macro_rules! version {
12 ($pat:pat) => {
13const { matches!(VERSION, $pat) }
14 };
15}
1617macro_rules! assert_version {
18 () => {
19const {
20assert!(matches!(VERSION, 1..=3), "invalid version provided");
21 }
22 };
23}
2425mod format_item;
26mod lexer_ast;
27mod strftime;
2829mod sealed {
30use super::*;
3132/// The version of the parser, represented in the type system.
33#[expect(
34 missing_debug_implementations,
35 reason = "only used at the type level; not public API"
36)]
37pub struct Version<const N: usize>;
3839/// A trait for parsing format descriptions, with different output types depending on the
40 /// version.
41pub trait VersionedParser {
42/// The output type of the borrowed parser. This type avoids allocating where possible.
43type BorrowedOutput<'input>;
4445/// The output type of the owned parser. This type may allocate but is valid for `'static`.
46type OwnedOutput;
4748/// Parse a format description into a type that avoids allocating where possible.
49fn parse_borrowed(
50 s: &str,
51 ) -> Result<Self::BorrowedOutput<'_>, error::InvalidFormatDescription>;
5253/// Parse a format description into an owned type, which may allocate but is valid for
54 /// `'static`.
55fn parse_owned(s: &str) -> Result<Self::OwnedOutput, error::InvalidFormatDescription>;
56 }
57}
5859impl VersionedParserfor Version<1> {
60type BorrowedOutput<'input> = Vec<BorrowedFormatItem<'input>>;
61type OwnedOutput = OwnedFormatItem;
6263#[inline]
64fn parse_borrowed(
65 s: &str,
66 ) -> Result<Self::BorrowedOutput<'_>, error::InvalidFormatDescription> {
67Ok(parse_generic::<1, false>(s)?)
68 }
6970#[inline]
71fn parse_owned(s: &str) -> Result<Self::OwnedOutput, error::InvalidFormatDescription> {
72Ok(parse_generic::<1, true>(s)?)
73 }
74}
7576impl VersionedParserfor Version<2> {
77type BorrowedOutput<'input> = Vec<BorrowedFormatItem<'input>>;
78type OwnedOutput = OwnedFormatItem;
7980#[inline]
81fn parse_borrowed(
82 s: &str,
83 ) -> Result<Self::BorrowedOutput<'_>, error::InvalidFormatDescription> {
84Ok(parse_generic::<2, false>(s)?)
85 }
8687#[inline]
88fn parse_owned(s: &str) -> Result<Self::OwnedOutput, error::InvalidFormatDescription> {
89Ok(parse_generic::<2, true>(s)?)
90 }
91}
9293impl VersionedParserfor Version<3> {
94type BorrowedOutput<'input> = FormatDescriptionV3<'input>;
95type OwnedOutput = FormatDescriptionV3<'static>;
9697#[inline]
98fn parse_borrowed(
99 s: &str,
100 ) -> Result<Self::BorrowedOutput<'_>, error::InvalidFormatDescription> {
101Ok(parse_generic::<3, false>(s)?)
102 }
103104#[inline]
105fn parse_owned(s: &str) -> Result<Self::OwnedOutput, error::InvalidFormatDescription> {
106Ok(parse_generic::<3, true>(s)?)
107 }
108}
109110/// Parse a sequence of items from the format description.
111///
112/// The syntax for the format description can be found in [the
113/// book](https://time-rs.github.io/book/api/format-description.html).
114///
115/// This function exists for backward compatibility reasons. It is equivalent to calling
116/// `parse_borrowed::<1>(s)`. **It is recommended to use version 3, not version 1.**
117#[deprecated(
118 since = "0.3.48",
119 note = "use `parse_borrowed` with the appropriate version for clarity"
120)]
121#[inline]
122pub fn parse(s: &str) -> Result<Vec<BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
123parse_borrowed::<1>(s)
124}
125126/// Parse a sequence of items from the format description.
127///
128/// The syntax for the format description can be found in [the
129/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
130/// description is provided as the const parameter. **It is recommended to use version 3.**
131///
132/// # Return type
133///
134/// The return type of this function depends on the version provided.
135///
136/// - For versions 1 and 2, the function returns `Result<Vec<BorrowedFormatItem<'_>>,
137/// InvalidFormatDescription>`.
138/// - For version 3, the function returns `Result<FormatDescriptionV3<'_>,
139/// InvalidFormatDescription>`.
140#[inline]
141pub fn parse_borrowed<const VERSION: usize>(
142 s: &str,
143) -> Result<
144 <Version<VERSION> as VersionedParser>::BorrowedOutput<'_>,
145 error::InvalidFormatDescription,
146>
147where
148Version<VERSION>: VersionedParser,
149{
150Version::<VERSION>::parse_borrowed(s)
151}
152153/// Parse a sequence of items from the format description.
154///
155/// The syntax for the format description can be found in [the
156/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
157/// description is provided as the const parameter.
158///
159/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
160/// that there is no lifetime that needs to be handled. **It is recommended to use version 3.**
161///
162/// # Return type
163///
164/// The return type of this function depends on the version provided.
165///
166/// - For versions 1 and 2, the function returns `Result<OwnedFormatItem,
167/// InvalidFormatDescription>`.
168/// - For version 3, the function returns `Result<FormatDescriptionV3<'static>,
169/// InvalidFormatDescription>`.
170///
171/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
172#[inline]
173pub fn parse_owned<const VERSION: usize>(
174 s: &str,
175) -> Result<<Version<VERSION> as VersionedParser>::OwnedOutput, error::InvalidFormatDescription>
176where
177Version<VERSION>: VersionedParser,
178{
179Version::<VERSION>::parse_owned(s)
180}
181182/// A location within a string.
183#[derive(#[automatically_derived]
impl ::core::clone::Clone for Location {
#[inline]
fn clone(&self) -> Location {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Location { }Copy)]
184struct Location {
185/// The zero-indexed byte of the string.
186byte: u32,
187}
188189impl Location {
190const DUMMY: Self = Self { byte: u32::MAX };
191192/// Create a new [`Span`] from `self` to `other`.
193#[inline]
194const fn to(self, end: Self) -> Span {
195Span { start: self, end }
196 }
197198/// Create a new [`Span`] consisting entirely of `self`.
199#[inline]
200const fn to_self(self) -> Span {
201Span {
202 start: self,
203 end: self,
204 }
205 }
206207#[inline]
208const fn with_length(self, length: usize) -> Span {
209Span {
210 start: self,
211 end: Self {
212 byte: self.byte + lengthas u32 - 1,
213 },
214 }
215 }
216217/// Offset the location by the provided amount.
218 ///
219 /// Note that this assumes the resulting location is on the same line as the original location.
220#[must_use = "this does not modify the original value"]
221 #[inline]
222const fn offset(&self, offset: u32) -> Self {
223Self {
224 byte: self.byte + offset,
225 }
226 }
227228/// Create an error with the provided message at this location.
229#[inline]
230const fn error(self, message: &'static str) -> ErrorInner {
231ErrorInner {
232 _message: message,
233 _span: Span {
234 start: self,
235 end: self,
236 },
237 }
238 }
239}
240241/// A value with an associated [`Location`].
242#[derive(#[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for WithLocation<T> {
#[inline]
fn clone(&self) -> WithLocation<T> {
WithLocation {
value: ::core::clone::Clone::clone(&self.value),
location: ::core::clone::Clone::clone(&self.location),
}
}
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for WithLocation<T> { }Copy)]
243struct WithLocation<T> {
244/// The value.
245value: T,
246/// Where the value was in the format string.
247location: Location,
248}
249250impl<T> core::ops::Dereffor WithLocation<T> {
251type Target = T;
252253#[inline]
254fn deref(&self) -> &Self::Target {
255&self.value
256 }
257}
258259/// Helper trait to attach a [`Location`] to a value.
260trait WithLocationValue: Sized {
261/// Attach a [`Location`] to a value.
262fn with_location(self, location: Location) -> WithLocation<Self>;
263}
264265impl<T> WithLocationValuefor T {
266#[inline]
267fn with_location(self, location: Location) -> WithLocation<Self> {
268WithLocation {
269 value: self,
270location,
271 }
272 }
273}
274275/// A start and end point within a string.
276#[derive(#[automatically_derived]
impl ::core::clone::Clone for Span {
#[inline]
fn clone(&self) -> Span {
let _: ::core::clone::AssertParamIsClone<Location>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Span { }Copy)]
277struct Span {
278 start: Location,
279 end: Location,
280}
281282impl Span {
283const DUMMY: Self = Self {
284 start: Location { byte: u32::MAX },
285 end: Location { byte: u32::MAX },
286 };
287288/// Obtain a `Span` pointing at the start of the pre-existing span.
289#[must_use = "this does not modify the original value"]
290 #[inline]
291const fn shrink_to_start(&self) -> Self {
292Self {
293 start: self.start,
294 end: self.start,
295 }
296 }
297298/// Obtain a `Span` pointing at the end of the pre-existing span.
299#[must_use = "this does not modify the original value"]
300const fn shrink_to_end(&self) -> Self {
301Self {
302 start: self.end,
303 end: self.end,
304 }
305 }
306307/// Create an error with the provided message at this span.
308#[inline]
309const fn error(self, message: &'static str) -> ErrorInner {
310ErrorInner {
311 _message: message,
312 _span: self,
313 }
314 }
315}
316317/// A value with an associated [`Span`].
318#[derive(#[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for Spanned<T> {
#[inline]
fn clone(&self) -> Spanned<T> {
Spanned {
value: ::core::clone::Clone::clone(&self.value),
span: ::core::clone::Clone::clone(&self.span),
}
}
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for Spanned<T> { }Copy)]
319struct Spanned<T> {
320/// The value.
321value: T,
322/// Where the value was in the format string.
323span: Span,
324}
325326impl<T> core::ops::Dereffor Spanned<T> {
327type Target = T;
328329#[inline]
330fn deref(&self) -> &Self::Target {
331&self.value
332 }
333}
334335impl<T> Spanned<T> {
336#[inline]
337fn map<F, U>(self, f: F) -> Spanned<U>
338where
339F: FnOnce(T) -> U,
340 {
341Spanned {
342 value: f(self.value),
343 span: self.span,
344 }
345 }
346}
347348trait OptionExt<T> {
349fn transpose(self) -> Spanned<Option<T>>;
350}
351352impl<T> OptionExt<T> for Option<Spanned<T>> {
353#[inline]
354fn transpose(self) -> Spanned<Option<T>> {
355match self {
356Some(spanned) => Spanned {
357 value: Some(spanned.value),
358 span: spanned.span,
359 },
360None => Spanned {
361 value: None,
362 span: Span::DUMMY,
363 },
364 }
365 }
366}
367368/// Helper trait to attach a [`Span`] to a value.
369trait SpannedValue: Sized {
370/// Attach a [`Span`] to a value.
371fn spanned(self, span: Span) -> Spanned<Self>;
372}
373374impl<T> SpannedValuefor T {
375#[inline]
376fn spanned(self, span: Span) -> Spanned<Self> {
377Spanned { value: self, span }
378 }
379}
380381/// The internal error type.
382struct ErrorInner {
383/// The message displayed to the user.
384_message: &'static str,
385/// Where the error originated.
386_span: Span,
387}
388389/// A complete error description.
390struct Error {
391/// The internal error.
392_inner: Unused<ErrorInner>,
393/// The error needed for interoperability with the rest of `time`.
394public: error::InvalidFormatDescription,
395}
396397impl From<Error> for error::InvalidFormatDescription {
398#[inline]
399fn from(error: Error) -> Self {
400error.public
401 }
402}
403404impl From<core::convert::Infallible> for Error {
405#[inline]
406fn from(v: core::convert::Infallible) -> Self {
407match v {}
408 }
409}
410411/// A value that may be used in the future, but currently is not.
412///
413/// This struct exists so that data can semantically be passed around without _actually_ passing it
414/// around. This way the data still exists if it is needed in the future.
415// `PhantomData` is not used directly because we don't want to introduce any trait implementations.
416struct Unused<T>(core::marker::PhantomData<T>);
417418/// Indicate that a value is currently unused.
419#[inline]
420fn unused<T>(_: T) -> Unused<T> {
421Unused(core::marker::PhantomData)
422}