pub(crate) struct Task { pub name: &'static str, pub exec: fn(&[&str]) -> Result, } pub(crate) const TASKS: &[Task] = &[ Task { name: "nop", exec: no_op, }, Task { name: "exec", exec: exec, }, ]; fn no_op(_: &[&str]) -> Result { Ok("".to_string()) } fn exec(cmds: &[&str]) -> Result { use std::process::Command; // TODO: fmt and add exit code, and add support for both stdout and stderr, and support for no output match Command::new(cmds[0]).args(&cmds[1..]).output() { Ok(o) => { if o.status.success() { Ok(String::from_utf8(o.stdout) .unwrap_or("Failed decoding vec -> String".to_string()) .to_string()) } else { Err(String::from_utf8(o.stderr) .unwrap_or("Failed decoding vec -> String".to_string()) .to_string()) } } Err(e) => Err(e.to_string()), } } pub(crate) enum IntoTaskErr { NotFound, } pub(crate) trait IntoTask { fn into_task<'a>(self) -> Result<&'a Task, IntoTaskErr>; } impl IntoTask for String { fn into_task<'a>(self) -> Result<&'a Task, IntoTaskErr> { for task in TASKS { if task.name == self { return Ok(task); } } Err(IntoTaskErr::NotFound) } } impl IntoTask for &str { fn into_task<'a>(self) -> Result<&'a Task, IntoTaskErr> { for task in TASKS { if task.name == self { return Ok(task); } } Err(IntoTaskErr::NotFound) } }