Quantcast
Channel: Simple command line parser/dispatcher in Rust - Code Review Stack Exchange
Viewing all articles
Browse latest Browse all 2

Simple command line parser/dispatcher in Rust

$
0
0

I've written a very simple command line parser/dispatcher library. This is an extension of a previous review, which I've refactored, found here. Basically, a user can add functions to a Cmdr struct, then invoke them using a &str. I've made the library generic so that a user can define their own Result<T, E> to be returned from the functions added to Cmdr.

I appreciate any and all comments, especially those related to Rust best practices. One thing that slightly irks me is that I'm not sure that Cmdr should be living in the root lib.rs file. But, if I move it to say, cmdr.rs, my namespace would be something stupid like cmdr::cmdr::Cmdr, as my root directory is also named cmdr. Anyway, behold.

cmd.rs

pub struct Cmd<T, E> {    pub name: String,    pub invocation: Box<FnMut() -> Result<T, E>>,}impl<T, E> Cmd<T, E> {    pub fn new<F: 'static + FnMut() -> Result<T, E>>(invoke_str: &str, invocation: F) -> Cmd<T, E> {        Cmd {            name: String::from(invoke_str),            invocation: Box::new(invocation),        }    }    pub fn invoke(&mut self) -> Result<T, E> {        (self.invocation)()    }}

lib.rs

pub mod cmd;use cmd::Cmd;pub struct Cmdr<T, E> {    cmds: Vec<Cmd<T, E>>,}impl<T, E> Cmdr<T, E> {    pub fn new() -> Cmdr<T, E> {        Cmdr { cmds: Vec::new() }    }    pub fn add<F: 'static + FnMut() -> Result<T, E>>(&mut self, name: &str, cmd: F) {        self.cmds.push(Cmd::new(name, cmd));    }    pub fn invoke(&mut self, cmd_name: &str) -> Option<Result<T, E>> {        let cmd_to_invoke = self.cmds.iter_mut().find(|cmd| cmd.name == cmd_name);        if let Some(cmd) = cmd_to_invoke {            Some(cmd.invoke())        } else {            None        }    }}mod tests {    #[test]    fn cmdr() {        let mut cmdr: super::Cmdr<&str, &str> = super::Cmdr::new();        cmdr.add("test1", || Ok("test1 executed."));        cmdr.add("test2", || Ok("test2 executed."));        // Good commands.        let test1_msg = cmdr.invoke("test1").unwrap().expect("Error in test1.");        assert!(test1_msg == "test1 executed.", "Incorrect test1 message.");        let test2_msg = cmdr.invoke("test2").unwrap().expect("Error in test2.");        assert!(test2_msg == "test2 executed.", "Incorrect test2 message.");        // Bad command.        if let Some(_) = cmdr.invoke("ghost") {            assert!(false, "Non-existent command somehow returned Some().");        }    }}

Some plans for the future are to...

  • Enable the library to actually utilize FnMut closures. The framework is in place, but I currently don't have a strong enough grasp of lifetimes to implement this.

  • Add sub-commands and command flags. It will be an interesting exercise in using the ? operator to propagate errors.

  • Document the code in a Rusty style. I haven't delved in to code based doc tags, as of yet.

  • Eventually, this will be a front end for a DHCP client/server package I'd like to implement in Rust. I may also release it on crates.io, if it matures into something worthwhile.


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles



Latest Images