summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2016-09-02 19:10:46 +0200
committerYorhel <git@yorhel.nl>2016-09-02 19:10:46 +0200
commit39eb2ac55f5b8f4192064f1c05e6c77b8ca11eaa (patch)
tree04798e75286d6c6167f34f23f73728910d2ccd9b /src
Initial commit. Mostly learning Rust
Diffstat (limited to 'src')
-rw-r--r--src/config.rs131
-rw-r--r--src/main.rs68
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();
+}