diff options
-rw-r--r-- | src/config.rs | 163 | ||||
-rw-r--r-- | src/main.rs | 3 |
2 files changed, 108 insertions, 58 deletions
diff --git a/src/config.rs b/src/config.rs index 7d01c77..ea3a14a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,51 @@ use std; +use std::str::FromStr; 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}; +use nom::{alpha,alphanumeric,space,multispace,line_ending,not_line_ending}; -#[derive(Debug,Clone)] + +#[derive(Debug,PartialEq,Eq)] +enum Expr<'a> { + Keyval(&'a str,&'a str), + Interface(&'a str), + Close, + Comment +} + +named!(parse_itf<&[u8],Expr>, chain!( + tag!("interface") ~ + space ~ + val: map_res!(alpha, std::str::from_utf8) ~ + opt!(space) ~ + tag!("{"), + ||{ Expr::Interface(val) } +)); + +named!(parse_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!(parse_expr<&[u8],Expr>, alt!( + value!(Expr::Comment, multispace) + | value!(Expr::Comment, delimited!(tag!("#"), not_line_ending, line_ending)) + | value!(Expr::Close, delimited!(tag!("}"), opt!(space), line_ending)) + | parse_itf + | parse_keyval // Must be last, matches a lot +)); + + +#[derive(Debug,Clone,Default)] pub struct Interface { addr: Vec<SocketAddr> } -#[derive(Debug,Clone)] +#[derive(Debug,Clone,Default)] pub struct Config { itf: Vec<Interface> } @@ -22,42 +57,11 @@ pub enum Err { 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, @@ -65,16 +69,55 @@ struct ConfigConsumer { } +impl ConfigConsumer { + fn handle_root(&mut self, e: Expr) { + match e { + Expr::Interface(_) => { + self.cfg.itf.push(Default::default()); + self.state = ParseState::Interface + }, + _ => { panic!("Not implemented yet.") } + } + } + + fn handle_itf(&mut self, e: Expr) { + let ref mut itf = self.cfg.itf.last_mut().unwrap(); + match e { + Expr::Keyval("listen", a) => { + // TODO: More flexible input format (e.g. ":80" or dns "localhost:8080") + itf.addr.push(SocketAddr::from_str(a).expect("I crash on invalid strings")); + }, + Expr::Close => { + if itf.addr.len() < 1 { + panic!("No interface address configured"); + } + self.state = ParseState::Root + }, + _ => { panic!("Not implemented yet in interface: {:?}.", e) } + } + } + + fn token(&mut self, e: Expr) { + if e == Expr::Comment { + return; + } + match self.state { + ParseState::Root => self.handle_root(e), + ParseState::Interface => self.handle_itf(e) + } + } +} + + 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) { + match parse_expr(x) { IResult::Done(i, e) => { - println!("{:?}", e); - // TODO: Pass to state handler (if not comment) + self.token(e); self.c_state = ConsumerState::Continue(Move::Consume(x.offset(i))); }, IResult::Error(_) => { self.c_state = ConsumerState::Error(()); }, @@ -92,22 +135,16 @@ impl<'a> Consumer<&'a[u8], (), (), nom::Move> for ConfigConsumer { 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 p = try!(nom::FileProducer::new(file, 1024).map_err(Err::Io)); let mut c = ConfigConsumer{ state: ParseState::Root, c_state: ConsumerState::Continue(Move::Consume(0)), - cfg: Config::new() + cfg: Default::default() }; loop { match *p.apply(&mut c) { - ConsumerState::Done(_, x) => { return Ok(c.cfg) } + ConsumerState::Done(_, _) => { return Ok(c.cfg) } ConsumerState::Error(e) => { return Result::Err(Err::Parse(e)) } ConsumerState::Continue(_) => {} } @@ -117,15 +154,27 @@ impl Config { #[test] -fn parse_expr() { +fn test_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"[..]))); + let e = parse_expr; + assert_eq!(e(b" \r\n\tx"), x(Expr::Comment)); + assert_eq!(e(b"\nx"), x(Expr::Comment)); + assert_eq!(e(b"#\nx"), x(Expr::Comment)); + assert_eq!(e(b"##/!@$% \nx"), x(Expr::Comment)); + assert_eq!(e(b"}\nx"), x(Expr::Close)); + assert_eq!(e(b"key val\nx"), x(Expr::Keyval("key", "val"))); + assert_eq!(e(b"ke\t v a \nx"), x(Expr::Keyval("ke", "v a"))); + assert_eq!(e(b"interface http {x"), x(Expr::Interface("http"))); + assert_eq!(e(b"interface \thttp{x"), x(Expr::Interface("http"))); + + let l: &[&'static str] = &[ + "", + "something", + "#something", + ][..]; + for s in l { + if let IResult::Done(_,_) = e(s.as_bytes()) { + panic!("'{}' did not error", s); + } + } } diff --git a/src/main.rs b/src/main.rs index 215670d..504b7f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ -// I'm playing around. Let me. +/* I'm playing around. Let me. #![allow(dead_code)] #![allow(unused_variables)] #![allow(unused_mut)] #![allow(unused_imports)] +// */ #[macro_use] extern crate nom; #[macro_use] extern crate log; |