time_macros/format_description/
ast.rs1use 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 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}