config_rs/conf/
parser.rs

1use std::collections::BTreeMap;
2
3use nom::{
4    branch::alt,
5    bytes::complete::tag,
6    character::complete::{line_ending, multispace0, not_line_ending},
7    combinator::opt,
8    multi::{many0, many1, separated_list0},
9    sequence::{delimited, pair, preceded, separated_pair, terminated},
10    IResult,
11};
12
13use crate::parser::{without_chars, without_chars_and_line_ending};
14
15use super::{Attribute, AttributeOrNote, Conf, Section};
16
17pub fn custom_conf<'a: 'b, 'b: 'c, 'c>(
18    note_starting: &'a str,
19) -> impl FnMut(&'b str) -> IResult<&'b str, Conf> + 'c {
20    move |input: &'b str| {
21        let (input, mut sections) = many0(custom_section(note_starting))(input)?;
22        Ok((input, {
23            let mut map = BTreeMap::new();
24            sections.drain(..).for_each(|(name, section)| {
25                map.insert(name, section);
26            });
27            map
28        }))
29    }
30}
31
32pub fn conf(input: &str) -> IResult<&str, Conf> {
33    custom_conf("#")(input)
34}
35
36pub fn custom_section<'a: 'b, 'b: 'c, 'c>(
37    note_starting: &'a str,
38) -> impl FnMut(&'b str) -> IResult<&'b str, Section> + 'c {
39    move |input: &'b str| {
40        let (input, name) = preceded(
41            multispace0,
42            terminated(
43                delimited(
44                    tag("["),
45                    without_chars_and_line_ending(("]".to_owned() + note_starting).as_str()),
46                    tag("]"),
47                ),
48                opt(pair(tag(note_starting), not_line_ending)),
49            ),
50        )(input)?;
51        let (input, mut attributes) = preceded(
52            multispace0,
53            separated_list0(many1(line_ending), custom_attribute_or_note(note_starting)),
54        )(input)?;
55        Ok((input, {
56            let mut map = BTreeMap::new();
57            attributes
58                .drain(..)
59                .filter_map(|x| if let Some(x) = x { Some(x) } else { None })
60                .for_each(|(k, v)| {
61                    map.insert(k, v);
62                });
63            (name.to_owned(), map)
64        }))
65    }
66}
67
68pub fn section(input: &str) -> IResult<&str, Section> {
69    custom_section(";")(input)
70}
71
72pub fn attribute(input: &str) -> IResult<&str, AttributeOrNote> {
73    let (input, (s1, s2)) = separated_pair(
74        without_chars("[]:"),
75        tag(":"),
76        without_chars_and_line_ending("[]"),
77    )(input)?;
78    Ok((
79        input,
80        AttributeOrNote::Attribute((s1.to_owned(), s2.to_owned())),
81    ))
82}
83
84pub fn custom_attribute_with_note<'a: 'b, 'b: 'c, 'c>(
85    note_starting: &'a str,
86) -> impl FnMut(&'b str) -> IResult<&'b str, AttributeOrNote> + 'c {
87    move |input: &'b str| {
88        let (input, ((k, v), note)) = separated_pair(
89            separated_pair(
90                without_chars_and_line_ending(("[]:".to_owned() + note_starting).as_str()),
91                tag("="),
92                without_chars_and_line_ending(("[]".to_owned() + note_starting).as_str()),
93            ),
94            tag(note_starting),
95            not_line_ending,
96        )(input)?;
97        Ok((
98            input,
99            AttributeOrNote::AttributeWithNote {
100                attribute: (k.to_owned(), v.to_owned()),
101                note: note.to_owned(),
102            },
103        ))
104    }
105}
106
107pub fn attribute_with_note(input: &str) -> IResult<&str, AttributeOrNote> {
108    custom_attribute_with_note("#")(input)
109}
110
111pub fn custom_note<'a: 'b, 'b: 'c, 'c>(
112    note_starting: &'a str,
113) -> impl FnMut(&'b str) -> IResult<&'b str, AttributeOrNote> + 'c {
114    move |input: &'b str| {
115        let (input, s) =
116            preceded(multispace0, preceded(tag(note_starting), not_line_ending))(input)?;
117        Ok((input, AttributeOrNote::Note(s.to_owned())))
118    }
119}
120
121pub fn note(input: &str) -> IResult<&str, AttributeOrNote> {
122    custom_note("#")(input)
123}
124
125pub fn custom_attribute_or_note<'a: 'b, 'b: 'c, 'c>(
126    note_starting: &'a str,
127) -> impl FnMut(&'b str) -> IResult<&str, Option<Attribute>> + 'c {
128    move |input: &'b str| {
129        let (input, may_be_attribute) = alt((
130            custom_note(note_starting),
131            custom_attribute_with_note(note_starting),
132            attribute,
133        ))(input)?;
134        Ok((
135            input,
136            match may_be_attribute {
137                AttributeOrNote::AttributeWithNote { attribute, .. } => Some(attribute),
138                AttributeOrNote::Attribute(attribute) => Some(attribute),
139                AttributeOrNote::Note(_) => None,
140            },
141        ))
142    }
143}
144
145pub fn attribute_or_note(input: &str) -> IResult<&str, Option<Attribute>> {
146    custom_attribute_or_note("#")(input)
147}