1use std::{any::TypeId, str::FromStr};
2
3use nom::{
4 IResult, Parser as _,
5 branch::alt,
6 bytes::complete::{escaped_transform, tag, take_while, take_while1},
7 character::complete::{anychar, char},
8 combinator::{cut, eof, map, not, opt},
9 multi::{many0, separated_list0},
10 sequence::{preceded, separated_pair, terminated},
11};
12
13use crate::errors::*;
14
15#[derive(Debug, PartialEq, Eq)]
16pub struct Arg<'a> {
17 pub key: &'a str,
18 pub val: Option<&'a str>,
19}
20
21impl Arg<'_> {
22 pub fn parse_value<T>(&self) -> Result<T>
23 where
24 T: FromStr + 'static,
25 T::Err: StdError + Send + Sync + 'static,
26 {
27 if TypeId::of::<T>() == TypeId::of::<bool>() && self.val.is_none() {
28 Ok("true".parse().expect("'true' is valid bool"))
29 } else {
30 self.val
31 .or_error(|| format!("missing value for argument '{}'", self.key))?
32 .parse()
33 .or_error(|| format!("invalid value for argument '{}'", self.key))
34 }
35 }
36}
37
38#[derive(Debug, PartialEq, Eq)]
39pub struct Formatter<'a> {
40 pub name: &'a str,
41 pub args: Vec<Arg<'a>>,
42}
43
44#[derive(Debug, PartialEq, Eq)]
45pub struct Placeholder<'a> {
46 pub name: &'a str,
47 pub formatter: Option<Formatter<'a>>,
48}
49
50#[derive(Debug, PartialEq, Eq)]
51pub enum Token<'a> {
52 Text(String),
53 Placeholder(Placeholder<'a>),
54 Icon(&'a str),
55 Recursive(FormatTemplate<'a>),
56}
57
58#[derive(Debug, PartialEq, Eq)]
59pub struct TokenList<'a>(pub Vec<Token<'a>>);
60
61#[derive(Debug, PartialEq, Eq)]
62pub struct FormatTemplate<'a>(pub Vec<TokenList<'a>>);
63
64#[derive(Debug, PartialEq, Eq)]
65enum PError<'a> {
66 Expected {
67 expected: char,
68 actual: Option<char>,
69 },
70 Other {
71 input: &'a str,
72 kind: nom::error::ErrorKind,
73 },
74}
75
76impl<'a> nom::error::ParseError<&'a str> for PError<'a> {
77 fn from_error_kind(input: &'a str, kind: nom::error::ErrorKind) -> Self {
78 Self::Other { input, kind }
79 }
80
81 fn append(_: &'a str, _: nom::error::ErrorKind, other: Self) -> Self {
82 other
83 }
84
85 fn from_char(input: &'a str, expected: char) -> Self {
86 let actual = input.chars().next();
87 Self::Expected { expected, actual }
88 }
89
90 fn or(self, other: Self) -> Self {
91 other
92 }
93}
94
95fn spaces(i: &str) -> IResult<&str, &str, PError<'_>> {
96 take_while(|x: char| x.is_ascii_whitespace())(i)
97}
98
99fn alphanum1(i: &str) -> IResult<&str, &str, PError<'_>> {
100 take_while1(|x: char| x.is_alphanumeric() || x == '_' || x == '-')(i)
101}
102
103fn arg1(i: &str) -> IResult<&str, &str, PError<'_>> {
106 alt((
107 take_while1(|x: char| x.is_alphanumeric() || x == '_' || x == '-' || x == '.' || x == '%'),
108 preceded(
109 char('\''),
110 cut(terminated(take_while(|x: char| x != '\''), char('\''))),
111 ),
112 ))
113 .parse(i)
114}
115
116fn parse_arg(i: &str) -> IResult<&str, Arg<'_>, PError<'_>> {
119 alt((
120 map(
121 separated_pair(alphanum1, char(':'), cut(arg1)),
122 |(key, val)| Arg {
123 key,
124 val: Some(val),
125 },
126 ),
127 map(alphanum1, |key| Arg { key, val: None }),
128 ))
129 .parse(i)
130}
131
132fn parse_args(i: &str) -> IResult<&str, Vec<Arg<'_>>, PError<'_>> {
135 let inner = separated_list0(preceded(spaces, char(',')), preceded(spaces, parse_arg));
136 preceded(
137 char('('),
138 cut(terminated(inner, preceded(spaces, char(')')))),
139 )
140 .parse(i)
141}
142
143fn parse_formatter(i: &str) -> IResult<&str, Formatter<'_>, PError<'_>> {
146 preceded(char('.'), cut((alphanum1, opt(parse_args))))
147 .map(|(name, args)| Formatter {
148 name,
149 args: args.unwrap_or_default(),
150 })
151 .parse(i)
152}
153
154fn parse_placeholder(i: &str) -> IResult<&str, Placeholder<'_>, PError<'_>> {
157 preceded(char('$'), cut((alphanum1, opt(parse_formatter))))
158 .map(|(name, formatter)| Placeholder { name, formatter })
159 .parse(i)
160}
161
162fn parse_string(i: &str) -> IResult<&str, String, PError<'_>> {
164 preceded(
165 not(eof),
166 escaped_transform(
167 take_while1(|x| x != '$' && x != '^' && x != '{' && x != '}' && x != '|' && x != '\\'),
168 '\\',
169 anychar,
170 ),
171 )
172 .parse(i)
173}
174
175fn parse_icon(i: &str) -> IResult<&str, &str, PError<'_>> {
177 preceded(char('^'), cut(preceded(tag("icon_"), alphanum1))).parse(i)
178}
179
180fn parse_recursive_template(i: &str) -> IResult<&str, FormatTemplate<'_>, PError<'_>> {
182 preceded(char('{'), cut(terminated(parse_format_template, char('}')))).parse(i)
183}
184
185fn parse_token_list(i: &str) -> IResult<&str, TokenList<'_>, PError<'_>> {
186 map(
187 many0(alt((
188 map(parse_string, Token::Text),
189 map(parse_placeholder, Token::Placeholder),
190 map(parse_icon, Token::Icon),
191 map(parse_recursive_template, Token::Recursive),
192 ))),
193 TokenList,
194 )
195 .parse(i)
196}
197
198fn parse_format_template(i: &str) -> IResult<&str, FormatTemplate<'_>, PError<'_>> {
199 map(separated_list0(char('|'), parse_token_list), FormatTemplate).parse(i)
200}
201
202pub fn parse_full(i: &str) -> Result<FormatTemplate<'_>> {
203 match parse_format_template(i) {
204 Ok((rest, template)) => {
205 if rest.is_empty() {
206 Ok(template)
207 } else {
208 Err(Error::new(format!(
209 "unexpected '{}'",
210 rest.chars().next().unwrap()
211 )))
212 }
213 }
214 Err(err) => Err(match err {
215 nom::Err::Incomplete(_) => unreachable!(),
216 nom::Err::Error(err) | nom::Err::Failure(err) => match err {
217 PError::Expected { expected, actual } => {
218 if let Some(actual) = actual {
219 Error::new(format!("expected '{expected}', got '{actual}'"))
220 } else {
221 Error::new(format!("expected '{expected}', got EOF"))
222 }
223 }
224 PError::Other { input, kind } => {
225 Error::new(format!("{kind:?} error near '{input}'"))
227 }
228 },
229 }),
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn arg() {
239 assert_eq!(
240 parse_arg("key:val,"),
241 Ok((
242 ",",
243 Arg {
244 key: "key",
245 val: Some("val")
246 }
247 ))
248 );
249 assert_eq!(
250 parse_arg("key:'val ue',"),
251 Ok((
252 ",",
253 Arg {
254 key: "key",
255 val: Some("val ue")
256 }
257 ))
258 );
259 assert_eq!(
260 parse_arg("key:'',"),
261 Ok((
262 ",",
263 Arg {
264 key: "key",
265 val: Some("")
266 }
267 ))
268 );
269 assert_eq!(
270 parse_arg("key,"),
271 Ok((
272 ",",
273 Arg {
274 key: "key",
275 val: None
276 }
277 ))
278 );
279 assert_eq!(
280 parse_arg("key:,"),
281 Err(nom::Err::Failure(PError::Expected {
282 expected: '\'',
283 actual: Some(',')
284 }))
285 );
286 }
287
288 #[test]
289 fn args() {
290 assert_eq!(
291 parse_args("(key:val)"),
292 Ok((
293 "",
294 vec![Arg {
295 key: "key",
296 val: Some("val")
297 }]
298 ))
299 );
300 assert_eq!(
301 parse_args("( abc:d , key:val )"),
302 Ok((
303 "",
304 vec![
305 Arg {
306 key: "abc",
307 val: Some("d"),
308 },
309 Arg {
310 key: "key",
311 val: Some("val")
312 }
313 ]
314 ))
315 );
316 assert_eq!(
317 parse_args("(abc)"),
318 Ok((
319 "",
320 vec![Arg {
321 key: "abc",
322 val: None
323 }]
324 ))
325 );
326 assert_eq!(
327 parse_args("( key:, )"),
328 Err(nom::Err::Failure(PError::Expected {
329 expected: '\'',
330 actual: Some(',')
331 }))
332 );
333 }
334
335 #[test]
336 fn formatter() {
337 assert_eq!(
338 parse_formatter(".str(key:val)"),
339 Ok((
340 "",
341 Formatter {
342 name: "str",
343 args: vec![Arg {
344 key: "key",
345 val: Some("val")
346 }]
347 }
348 ))
349 );
350 assert_eq!(
351 parse_formatter(".eng(w:3 , show:true )"),
352 Ok((
353 "",
354 Formatter {
355 name: "eng",
356 args: vec![
357 Arg {
358 key: "w",
359 val: Some("3")
360 },
361 Arg {
362 key: "show",
363 val: Some("true")
364 }
365 ]
366 }
367 ))
368 );
369 assert_eq!(
370 parse_formatter(".eng(w:3 , show)"),
371 Ok((
372 "",
373 Formatter {
374 name: "eng",
375 args: vec![
376 Arg {
377 key: "w",
378 val: Some("3")
379 },
380 Arg {
381 key: "show",
382 val: None
383 }
384 ]
385 }
386 ))
387 );
388 }
389
390 #[test]
391 fn placeholder() {
392 assert_eq!(
393 parse_placeholder("$key"),
394 Ok((
395 "",
396 Placeholder {
397 name: "key",
398 formatter: None,
399 }
400 ))
401 );
402 assert_eq!(
403 parse_placeholder("$var.str()"),
404 Ok((
405 "",
406 Placeholder {
407 name: "var",
408 formatter: Some(Formatter {
409 name: "str",
410 args: vec![]
411 }),
412 }
413 ))
414 );
415 assert_eq!(
416 parse_placeholder("$var.str(a:b, c:d)"),
417 Ok((
418 "",
419 Placeholder {
420 name: "var",
421 formatter: Some(Formatter {
422 name: "str",
423 args: vec![
424 Arg {
425 key: "a",
426 val: Some("b")
427 },
428 Arg {
429 key: "c",
430 val: Some("d")
431 }
432 ]
433 }),
434 }
435 ))
436 );
437 assert!(parse_placeholder("$key.").is_err());
438 }
439
440 #[test]
441 fn icon() {
442 assert_eq!(parse_icon("^icon_my_icon"), Ok(("", "my_icon")));
443 assert_eq!(parse_icon("^icon_m"), Ok(("", "m")));
444 assert!(parse_icon("^icon_").is_err());
445 assert!(parse_icon("^2").is_err());
446 }
447
448 #[test]
449 fn token_list() {
450 assert_eq!(
451 parse_token_list(" abc \\$ $var.str(a:b)$x "),
452 Ok((
453 "",
454 TokenList(vec![
455 Token::Text(" abc $ ".into()),
456 Token::Placeholder(Placeholder {
457 name: "var",
458 formatter: Some(Formatter {
459 name: "str",
460 args: vec![Arg {
461 key: "a",
462 val: Some("b")
463 }]
464 })
465 }),
466 Token::Placeholder(Placeholder {
467 name: "x",
468 formatter: None,
469 }),
470 Token::Text(" ".into())
471 ])
472 ))
473 );
474 }
475
476 #[test]
477 fn format_template() {
478 assert_eq!(
479 parse_format_template("simple"),
480 Ok((
481 "",
482 FormatTemplate(vec![TokenList(vec![Token::Text("simple".into())]),])
483 ))
484 );
485 assert_eq!(
486 parse_format_template(" $x.str() | N/A "),
487 Ok((
488 "",
489 FormatTemplate(vec![
490 TokenList(vec![
491 Token::Text(" ".into()),
492 Token::Placeholder(Placeholder {
493 name: "x",
494 formatter: Some(Formatter {
495 name: "str",
496 args: vec![]
497 })
498 }),
499 Token::Text(" ".into()),
500 ]),
501 TokenList(vec![Token::Text(" N/A ".into())]),
502 ])
503 ))
504 );
505 }
506
507 #[test]
508 fn full() {
509 assert_eq!(
510 parse_format_template(" ^icon_my_icon {$x.str()|N/A} "),
511 Ok((
512 "",
513 FormatTemplate(vec![TokenList(vec![
514 Token::Text(" ".into()),
515 Token::Icon("my_icon"),
516 Token::Text(" ".into()),
517 Token::Recursive(FormatTemplate(vec![
518 TokenList(vec![Token::Placeholder(Placeholder {
519 name: "x",
520 formatter: Some(Formatter {
521 name: "str",
522 args: vec![]
523 })
524 })]),
525 TokenList(vec![Token::Text("N/A".into())]),
526 ])),
527 Token::Text(" ".into()),
528 ]),])
529 ))
530 );
531 }
532}