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}") .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 .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}