Skip to main content

time_macros/format_description/
ast.rs

1use std::iter;
2
3use super::{Error, Location, Spanned, SpannedValue, Unused, lexer, unused};
4use crate::FormatDescriptionVersion;
5use crate::format_description::Span;
6
7pub(super) enum Item<'a> {
8    Literal {
9        version: FormatDescriptionVersion,
10        value: Spanned<&'a [u8]>,
11    },
12    Component {
13        version: FormatDescriptionVersion,
14        opening_bracket: Location,
15        _leading_whitespace: Unused<Option<Spanned<&'a str>>>,
16        name: Spanned<&'a str>,
17        modifiers: Box<[Modifier<'a>]>,
18        nested_format_descriptions: Box<[NestedFormatDescription<'a>]>,
19        _trailing_whitespace: Unused<Option<Spanned<&'a str>>>,
20        closing_bracket: Location,
21    },
22}
23
24pub(super) struct NestedFormatDescription<'a> {
25    pub(super) leading_whitespace: Option<Spanned<&'a str>>,
26    pub(super) opening_bracket: Location,
27    pub(super) items: Box<[Item<'a>]>,
28    pub(super) closing_bracket: Location,
29}
30
31#[derive(Debug)]
32pub(super) struct Modifier<'a> {
33    pub(super) _leading_whitespace: Unused<Spanned<&'a str>>,
34    pub(super) key: Spanned<&'a str>,
35    pub(super) _colon: Unused<Location>,
36    pub(super) value: Spanned<&'a str>,
37}
38
39impl<'a> Modifier<'a> {
40    fn from_leading_whitespace_and_token(
41        leading_whitespace: Spanned<&'a str>,
42        token: Spanned<&'a str>,
43    ) -> Result<Self, Error> {
44        let Some(colon_index) = token.bytes().position(|b| b == b':') else {
45            return Err(token.span.error("modifier must be of the form `key:value`"));
46        };
47        let key = &token[..colon_index];
48        let value = &token[colon_index + 1..];
49
50        if key.is_empty() {
51            return Err(token.span.shrink_to_start().error("expected modifier key"));
52        }
53        if value.is_empty() {
54            return Err(token.span.shrink_to_end().error("expected modifier value"));
55        }
56
57        Ok(Self {
58            _leading_whitespace: unused(leading_whitespace),
59            key: key.spanned(token.span),
60            _colon: unused(token.span.start.offset(colon_index as u32)),
61            value: value.spanned(token.span),
62        })
63    }
64
65    pub(super) fn key_value_span(&self) -> Span {
66        self.key.span.start.to(self.value.span.end)
67    }
68}
69
70pub(super) fn parse<'item: 'iter, 'iter, I: Iterator<Item = Result<lexer::Token<'item>, Error>>>(
71    version: FormatDescriptionVersion,
72    tokens: &'iter mut lexer::Lexed<I>,
73) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'item, 'iter, I> {
74    parse_inner(version, false, tokens)
75}
76
77fn parse_inner<'item, I: Iterator<Item = Result<lexer::Token<'item>, Error>>>(
78    version: FormatDescriptionVersion,
79    nested: bool,
80    tokens: &mut lexer::Lexed<I>,
81) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'_, 'item, I> {
82    iter::from_fn(move || {
83        if nested && tokens.peek_closing_bracket().is_some() {
84            return None;
85        }
86
87        let next = match tokens.next()? {
88            Ok(token) => token,
89            Err(err) => return Some(Err(err)),
90        };
91
92        Some(match next {
93            lexer::Token::Literal(Spanned { value: _, span: _ }) if nested => {
94                bug!("literal should not be present in nested description")
95            }
96            lexer::Token::Literal(value) => Ok(Item::Literal { version, value }),
97            lexer::Token::Bracket {
98                kind: lexer::BracketKind::Opening,
99                location,
100            } => {
101                if version.is_v1()
102                    && let Some(second_location) = tokens.next_if_opening_bracket()
103                {
104                    Ok(Item::Literal {
105                        version,
106                        value: b"[".as_slice().spanned(location.to(second_location)),
107                    })
108                } else {
109                    parse_component(version, location, tokens)
110                }
111            }
112            lexer::Token::Bracket {
113                kind: lexer::BracketKind::Closing,
114                location: _,
115            } if nested => {
116                bug!("closing bracket should be caught by the `if` statement")
117            }
118            lexer::Token::Bracket {
119                kind: lexer::BracketKind::Closing,
120                location: _,
121            } => {
122                bug!("closing bracket should have been consumed by `parse_component`")
123            }
124            lexer::Token::ComponentPart { kind: _, value } if nested => Ok(Item::Literal {
125                version,
126                value: value.map(str::as_bytes),
127            }),
128            lexer::Token::ComponentPart { kind: _, value: _ } => {
129                bug!("component part should have been consumed by `parse_component`")
130            }
131        })
132    })
133}
134
135struct Modifiers<'a> {
136    modifiers: Box<[Modifier<'a>]>,
137    trailing_whitespace: Option<Spanned<&'a str>>,
138}
139
140impl<'a> Modifiers<'a> {
141    /// Parse modifiers until there are none left. Returns any trailing whitespace after the last
142    /// modifier.
143    fn parse<I>(tokens: &mut lexer::Lexed<I>) -> Result<Self, Error>
144    where
145        I: Iterator<Item = Result<lexer::Token<'a>, Error>>,
146    {
147        let mut modifiers = Vec::new();
148        loop {
149            let Some(whitespace) = tokens.next_if_whitespace() else {
150                return Ok(Self {
151                    modifiers: modifiers.into_boxed_slice(),
152                    trailing_whitespace: None,
153                });
154            };
155
156            let Some(token) = tokens.next_if_not_whitespace() else {
157                return Ok(Self {
158                    modifiers: modifiers.into_boxed_slice(),
159                    trailing_whitespace: Some(whitespace),
160                });
161            };
162
163            let modifier = Modifier::from_leading_whitespace_and_token(whitespace, token)?;
164            modifiers.push(modifier);
165        }
166    }
167
168    fn span(&self) -> Span {
169        match &*self.modifiers {
170            [] => Span::dummy(),
171            [modifier] => modifier.key.span.start.to(modifier.value.span.end),
172            [first, .., last] => first.key.span.start.to(last.value.span.end),
173        }
174    }
175}
176
177fn parse_component<'a, I: Iterator<Item = Result<lexer::Token<'a>, Error>>>(
178    version: FormatDescriptionVersion,
179    opening_bracket: Location,
180    tokens: &mut lexer::Lexed<I>,
181) -> Result<Item<'a>, Error> {
182    let leading_whitespace = tokens.next_if_whitespace();
183
184    let Some(name) = tokens.next_if_not_whitespace() else {
185        let span = match leading_whitespace {
186            Some(Spanned { value: _, span }) => span,
187            None => opening_bracket.to(opening_bracket),
188        };
189        return Err(span.error("expected component name"));
190    };
191
192    let modifiers = Modifiers::parse(tokens)?;
193
194    let mut nested_format_descriptions = Vec::new();
195    while let Ok(description) = parse_nested(version, modifiers.span().end, tokens) {
196        nested_format_descriptions.push(description);
197    }
198
199    if modifiers.trailing_whitespace.is_some()
200        && let Some(first_nested) = nested_format_descriptions.first_mut()
201    {
202        first_nested.leading_whitespace = modifiers.trailing_whitespace;
203    }
204
205    let nested_fds_trailing_whitespace =
206        if modifiers.trailing_whitespace.is_some() && nested_format_descriptions.is_empty() {
207            modifiers.trailing_whitespace
208        } else {
209            tokens.next_if_whitespace()
210        };
211
212    let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
213        return Err(opening_bracket.error("unclosed bracket"));
214    };
215
216    Ok(Item::Component {
217        version,
218        opening_bracket,
219        _leading_whitespace: unused(leading_whitespace),
220        name,
221        modifiers: modifiers.modifiers,
222        nested_format_descriptions: nested_format_descriptions.into_boxed_slice(),
223        _trailing_whitespace: unused(nested_fds_trailing_whitespace),
224        closing_bracket,
225    })
226}
227
228fn parse_nested<'a, I: Iterator<Item = Result<lexer::Token<'a>, Error>>>(
229    version: FormatDescriptionVersion,
230    last_location: Location,
231    tokens: &mut lexer::Lexed<I>,
232) -> Result<NestedFormatDescription<'a>, Error> {
233    let leading_whitespace = tokens.next_if_whitespace();
234    let Some(opening_bracket) = tokens.next_if_opening_bracket() else {
235        return Err(last_location.error("expected opening bracket"));
236    };
237    let items = parse_inner(version, true, tokens).collect::<Result<_, _>>()?;
238    let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
239        return Err(opening_bracket.error("unclosed bracket"));
240    };
241
242    Ok(NestedFormatDescription {
243        leading_whitespace,
244        opening_bracket,
245        items,
246        closing_bracket,
247    })
248}