tmpl_resolver/
parsers.rs

1mod branch;
2pub(crate) mod context;
3mod process_tmpl;
4
5use alloc::{boxed::Box, vec::Vec};
6
7use compact_str::format_compact;
8use nom::{
9  IResult, Parser,
10  bytes::complete::{tag, take_while1},
11  character::complete::multispace0,
12  multi::many0,
13};
14use tap::Pipe;
15
16use crate::{
17  error::{ResolverError, ResolverResult},
18  selector, template,
19};
20
21pub(crate) fn parse_value_or_map_err<D: core::fmt::Display>(
22  key: D,
23  value: &str,
24) -> ResolverResult<template::Template> {
25  parse_value(value).map_err(|e| {
26    format_compact!("Failed to parse '{key}': {e}") //
27      .pipe(ResolverError::ParseError)
28  })
29}
30
31fn parse_value(input: &str) -> ResolverResult<template::Template> {
32  match parse_conditional(input.trim_ascii()) {
33    Ok((_, cond)) => cond.pipe(template::Template::Conditional),
34    _ => template::parse_template(input)?.pipe(template::Template::Parts),
35  }
36  .pipe(Ok)
37}
38
39fn parse_conditional(input: &str) -> IResult<&str, selector::Selector> {
40  let (input, _) = tag("$").parse(input)?;
41  let (input, param) =
42    take_while1(|c: char| c.is_alphanumeric() || c == '-' || c == '_')
43      .parse(input)?;
44
45  let (input, _t) = (multispace0, tag("->"), multispace0).parse(input)?;
46
47  let (input, branches) = many0(branch::parse_branch).parse(input)?;
48
49  let (cases, default) = branches //
50    .into_iter()
51    .fold(
52      (Vec::with_capacity(8), None),
53      |(mut cases, mut default), branch| {
54        match branch.is_default {
55          true => {
56            default = branch
57              .template
58              .pipe(Box::new)
59              .pipe(Some)
60          }
61          _ => cases.push((branch.value, branch.template)),
62        }
63        (cases, default)
64      },
65    );
66
67  (
68    input,
69    selector::Selector {
70      param: param.into(),
71      cases: cases.into(),
72      default,
73    },
74  )
75    .pipe(Ok)
76}