kanit

Toy init system
Log | Files | Refs | README | LICENSE

config.rs (4715B)


      1 // generic config handling
      2 // # comment
      3 // [Header]
      4 // key = [a-zA-Z_][a-zA-Z_-0-9]*
      5 
      6 use std::collections::HashMap;
      7 
      8 use nom::IResult;
      9 use nom::branch::alt;
     10 use nom::bytes::complete::{is_a, tag};
     11 use nom::character::complete::{
     12     alpha1, alphanumeric1, char, multispace0, multispace1, not_line_ending,
     13 };
     14 use nom::combinator::{all_consuming, map, recognize, value};
     15 use nom::multi::many0;
     16 use nom::sequence::{delimited, pair, separated_pair};
     17 
     18 pub fn config_file(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> {
     19     all_consuming(map(pair(body, many0(category_body)), |(root, mut rest)| {
     20         rest.push((".root", root));
     21         rest.into_iter().collect()
     22     }))(input)
     23 }
     24 
     25 fn category_body(input: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> {
     26     pair(category, body)(input)
     27 }
     28 
     29 fn category(input: &str) -> IResult<&str, &str> {
     30     delimited(char('['), identifier, char(']'))(input)
     31 }
     32 
     33 fn body(input: &str) -> IResult<&str, HashMap<&str, &str>> {
     34     map(many0(delimited(ignored, key_value, ignored)), |map| {
     35         map.into_iter().collect()
     36     })(input)
     37 }
     38 
     39 fn ignored(input: &str) -> IResult<&str, ()> {
     40     value((), many0(alt((comment, value((), multispace1)))))(input)
     41 }
     42 
     43 fn comment(input: &str) -> IResult<&str, ()> {
     44     value((), pair(char('#'), not_line_ending))(input)
     45 }
     46 
     47 fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
     48     separated_pair(
     49         identifier,
     50         delimited(multispace0, char('='), multispace0),
     51         not_line_ending,
     52     )(input)
     53 }
     54 
     55 fn identifier(input: &str) -> IResult<&str, &str> {
     56     recognize(pair(
     57         alt((alpha1, tag("_"))),
     58         many0(alt((alphanumeric1, is_a("-_")))),
     59     ))(input)
     60 }
     61 
     62 #[cfg(test)]
     63 mod tests {
     64     use nom::error::Error;
     65 
     66     use crate::parser_test;
     67 
     68     use super::*;
     69 
     70     #[test]
     71     fn parse_identifier() -> Result<(), Error<&'static str>> {
     72         parser_test!(
     73             identifier,
     74             [
     75                 "foo" => "foo",
     76                 "foo2" => "foo2",
     77                 "foo_bar" => "foo_bar",
     78                 "foo-bar" => "foo-bar"
     79             ]
     80         );
     81 
     82         Ok(())
     83     }
     84 
     85     #[test]
     86     fn parse_key_value() -> Result<(), Error<&'static str>> {
     87         parser_test!(
     88             key_value,
     89             [
     90                 "foo=bar" => ("foo", "bar"),
     91                 "foo = bar" => ("foo", "bar")
     92             ]
     93         );
     94 
     95         Ok(())
     96     }
     97 
     98     #[test]
     99     fn parse_comment() -> Result<(), Error<&'static str>> {
    100         parser_test!(
    101             comment,
    102             [
    103                 "#foo" => (),
    104                 "# foo" => ()
    105             ]
    106         );
    107 
    108         Ok(())
    109     }
    110 
    111     #[test]
    112     fn parse_ignored() -> Result<(), Error<&'static str>> {
    113         parser_test!(
    114             ignored,
    115             [
    116                 "#foo" => (),
    117                 "# foo" => (),
    118                 " " => (),
    119                 " \t \n" => ()
    120             ]
    121         );
    122 
    123         Ok(())
    124     }
    125 
    126     #[test]
    127     fn parse_body() -> Result<(), Error<&'static str>> {
    128         parser_test!(
    129             body,
    130             [
    131                 "x = y\ny=z" => HashMap::from([
    132                     ("x", "y"),
    133                     ("y", "z")
    134                 ]),
    135                 "x = y\n# foo\n\ny=z" => HashMap::from([
    136                     ("x", "y"),
    137                     ("y", "z")
    138                 ])
    139             ]
    140         );
    141 
    142         Ok(())
    143     }
    144 
    145     #[test]
    146     fn parse_category() -> Result<(), Error<&'static str>> {
    147         parser_test!(
    148             category,
    149             [
    150                 "[foo]" => "foo",
    151                 "[foo-bar]" => "foo-bar"
    152             ]
    153         );
    154 
    155         Ok(())
    156     }
    157 
    158     #[test]
    159     fn parse_category_body() -> Result<(), Error<&'static str>> {
    160         parser_test!(
    161             category_body,
    162             [
    163                 "[foo]\nx = y\n# foo\ny=z" => (
    164                     "foo",
    165                     HashMap::from([
    166                         ("x", "y"),
    167                         ("y", "z")
    168                     ])
    169                 )
    170             ]
    171         );
    172 
    173         Ok(())
    174     }
    175 
    176     #[test]
    177     fn parse_config_file() -> Result<(), Error<&'static str>> {
    178         parser_test!(
    179             config_file,
    180             [
    181                 "# foo\n\nx = y\n[foo]\nx = y\n# foo\ny=z" => HashMap::from([
    182                     (
    183                         ".root",
    184                         HashMap::from([
    185                             ("x", "y")
    186                         ])
    187                     ),
    188                     (
    189                         "foo",
    190                         HashMap::from([
    191                             ("x", "y"),
    192                             ("y", "z")
    193                         ])
    194                     )
    195                 ])
    196             ]
    197         );
    198 
    199         Ok(())
    200     }
    201 }