Skip to content

Instantly share code, notes, and snippets.

@joshkunz
Created November 16, 2018 07:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshkunz/3d05fcc168a5fafba260acb394c4dcda to your computer and use it in GitHub Desktop.
Save joshkunz/3d05fcc168a5fafba260acb394c4dcda to your computer and use it in GitHub Desktop.
Example homebrew flag parser in rust.
use std::collections::HashMap;
use std::convert::From;
use std::env;
use std::error;
use std::boxed::Box;
use std::iter::FromIterator;
type Error = Box<error::Error>;
fn parse_arg(s: &str) -> Result<(&str, &str), Error> {
let items = s.splitn(2, '=').collect::<Vec<&str>>();
if items.len() < 2 || items.len() > 2 {
return Err(Error::from(format!("invalid flag {}, items:{:?}", s, items)))
};
Ok((items[0], items[1]))
}
type OptionValueLoader<T> = Box<dyn Fn(&mut T, &str) -> Option<Error>>;
struct OptionParser<'a, T> {
loaders: HashMap<&'a str, OptionValueLoader<T>>
}
impl<'a, T> FromIterator<(&'a str, OptionValueLoader<T>)> for OptionParser<'a, T> {
fn from_iter<U>(iter: U) -> OptionParser<'a, T>
where U: IntoIterator<Item=(&'a str, OptionValueLoader<T>)>
{
OptionParser{ loaders: iter.into_iter().collect() }
}
}
impl<'a, T> OptionParser<'a, T> {
fn load_into(&self, opt_obj: &mut T, args: &Vec<&str>) -> Option<Error> {
for arg in args {
let (arg_name, arg_val) = match parse_arg(arg) {
Ok(x) => x,
Err(e) => return Some(e),
};
match self.loaders.get(arg_name) {
Some(loader) => match loader(opt_obj, arg_val) {
out@Some(_) => return out,
None => (),
},
None => return Some(Error::from(format!("Unrecognized flag {}", arg_name))),
};
};
None
}
}
impl<'a, T: Default> OptionParser<'a, T> {
fn load(&self, args: &Vec<&str>) -> Result<T, Error> {
let mut result = T::default();
match self.load_into(&mut result, args) {
Some(err) => Err(err),
None => Ok(result),
}
}
}
#[derive(Debug, Default)]
struct Options {
dir_a: String,
dir_b: String,
}
fn options() -> Result<Options, Error> {
let loader: OptionParser<Options> = vec![
("dir_a",
Box::new(|o: &mut Options, val: &str| { o.dir_a = val.to_string(); None })
as OptionValueLoader<Options>),
("dir_b",
Box::new(|o: &mut Options, val: &str| { o.dir_b = val.to_string(); None })
as OptionValueLoader<Options>),
].into_iter().collect();
let clean_args: Vec<String> = env::args().skip(1).collect(); // Don't include the program name
let clean_args_ref = clean_args.iter().map(|x| x.as_str()).collect();
loader.load(&clean_args_ref)
}
fn main() {
let opts = options().unwrap();
print!("Got options: {:?}\n", opts)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment