diff options
author | Yorhel <git@yorhel.nl> | 2016-09-02 19:10:46 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2016-09-02 19:10:46 +0200 |
commit | 39eb2ac55f5b8f4192064f1c05e6c77b8ca11eaa (patch) | |
tree | 04798e75286d6c6167f34f23f73728910d2ccd9b /src |
Initial commit. Mostly learning Rust
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 131 | ||||
-rw-r--r-- | src/main.rs | 68 |
2 files changed, 199 insertions, 0 deletions
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..7d01c77 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,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"[..]))); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..215670d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,68 @@ +// 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; +extern crate env_logger; +extern crate getopts; + + +use std::process::exit; +use std::io::prelude::*; + +mod config; + + +struct CliOpts { + config: String, +} + +impl CliOpts { + fn new() -> CliOpts { + CliOpts { + config: "/etc/webs.conf".to_string() + } + } + + fn parse(mut self) -> CliOpts { + let mut args = std::env::args(); + let prog = args.next().unwrap(); + + let mut opts = getopts::Options::new(); + opts.optflag("h", "help", "print this help menu"); + opts.optflag("V", "version", "show program version"); + opts.optopt("c", "config", "config file", "FILE"); + + let m = match opts.parse(args) { + Ok(m) => { m } + Err(e) => { + let _ = writeln!(std::io::stderr(), "{}", e); + exit(1); + } + }; + if m.opt_present("h") { + let _ = println!("{}", opts.usage(&prog)); + exit(0); + } + if m.opt_present("V") { + let _ = println!("{} {}", prog, option_env!("CARGO_PKG_VERSION").unwrap_or("")); + exit(0); + } + if let Some(f) = m.opt_str("c") { + self.config = f; + } + + self + } +} + + +fn main() { + let opts = CliOpts::new().parse(); + let conf = config::Config::parse(&opts.config); + println!("{:?}", conf); + env_logger::init().unwrap(); +} |