summaryrefslogtreecommitdiff
path: root/src/config.rs
blob: 7d01c777a7520efe15995d23ddec223bb0093ddc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std;
use nom;
use std::str;
use std::net::SocketAddr;
use nom::{IResult,Input,Consumer,ConsumerState,Move,Producer,HexDisplay};
use nom::{alphanumeric,space,multispace,line_ending,not_line_ending};

#[derive(Debug,Clone)]
pub struct Interface {
    addr: Vec<SocketAddr>
}

#[derive(Debug,Clone)]
pub struct Config {
    itf: Vec<Interface>
}


#[derive(Debug)]
pub enum Err {
    Io(std::io::Error),
    Parse(())  // TODO: error info
}


#[derive(Debug,PartialEq,Eq)]
enum Expr<'a> {
    Keyval(&'a str,&'a str),
    Interface(&'a str),
    Close,
    Comment
}

enum ParseState {
    Root,
    Interface
}

named!(one_multispace, value!(b" ", one_of!(" \t\r\n")));

named!(keyval<&[u8],Expr>, chain!(
    key: map_res!(alphanumeric, std::str::from_utf8) ~
         space ~
    val: map_res!(not_line_ending, std::str::from_utf8) ~
         line_ending,
    ||{ Expr::Keyval(key, val.trim()) }
));

named!(expr<&[u8],Expr>,
    // The switch!() macro doesn't support wildcard, so put keyval in an alt!()
    alt!(
        switch!(peek!(alt!(tag!("#") | tag!("}") | one_multispace)),
            b" "  => value!(Expr::Comment, multispace) |
            b"#"  => value!(Expr::Comment, delimited!(tag!("#"), not_line_ending, line_ending)) |
            b"}"  => value!(Expr::Close, take!(1))
        ) | keyval
    )
);


struct ConfigConsumer {
    c_state: ConsumerState<(), (), nom::Move>,
    state: ParseState,
    cfg: Config
}


impl<'a> Consumer<&'a[u8], (), (), nom::Move> for ConfigConsumer {
    fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), nom::Move> {
        match input {
            Input::Eof(None) => { self.c_state = ConsumerState::Done(Move::Consume(0), ()); },
            Input::Empty     => { self.c_state = ConsumerState::Continue(Move::Consume(0)); },
            Input::Element(x) | Input::Eof(Some(x)) => {
                match expr(x) {
                    IResult::Done(i, e)    => {
                        println!("{:?}", e);
                        // TODO: Pass to state handler (if not comment)
                        self.c_state = ConsumerState::Continue(Move::Consume(x.offset(i)));
                    },
                    IResult::Error(_)      => { self.c_state = ConsumerState::Error(()); },
                    IResult::Incomplete(i) => { self.c_state = ConsumerState::Continue(Move::Await(i)); }
                }
            }
        }
        &self.c_state
    }

    fn state(&self) -> &ConsumerState<(), (), nom::Move> {
        &self.c_state
    }
}


impl Config {
    fn new() -> Config {
        Config {
            itf: Vec::new()
        }
    }

    pub fn parse(file: &str) -> Result<Config, Err> {
        let mut p = try!(nom::FileProducer::new(file, 4096).map_err(Err::Io));
        let mut c = ConfigConsumer{
            state: ParseState::Root,
            c_state: ConsumerState::Continue(Move::Consume(0)),
            cfg: Config::new()
        };
        loop {
            match *p.apply(&mut c) {
                ConsumerState::Done(_, x)  => { return Ok(c.cfg) }
                ConsumerState::Error(e)    => { return Result::Err(Err::Parse(e)) }
                ConsumerState::Continue(_) => {}
            }
        }
    }
}


#[test]
fn parse_expr() {
    let x = |e| { IResult::Done(&b"x"[..], e) };
    assert_eq!(expr(b" \r\n\tx"),     x(Expr::Comment));
    assert_eq!(expr(b"\nx"),          x(Expr::Comment));
    assert_eq!(expr(b"#\nx"),         x(Expr::Comment));
    assert_eq!(expr(b"##/!@$% \nx"),  x(Expr::Comment));
    assert_eq!(expr(b"}x"),           x(Expr::Close));
    assert_eq!(expr(b"key val\nx"),   x(Expr::Keyval("key", "val")));
    assert_eq!(expr(b"ke\t v a \nx"), x(Expr::Keyval("ke", "v a")));
    assert_eq!(expr(b""),             IResult::Incomplete(nom::Needed::Size(1)));
    assert_eq!(expr(b"something"),    IResult::Error(nom::Err::Position(nom::ErrorKind::Alt, &b"something"[..])));
}