#![warn(missing_docs)]
use colored::*;
use linefeed::{Interface, ReadResult};
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
pub mod builder;
mod parse;
use self::parse::LineResult;
pub use builder::{Builder, BuilderChain};
pub struct Commander<'r> {
root: Rc<SubClass<'r>>,
current: Rc<SubClass<'r>>,
path: String,
}
impl<'r> Commander<'r> {
pub fn path(&self) -> &str {
&self.path
}
pub fn run(mut self) {
let interface = Interface::new("commander").expect("failed to start interface");
let mut exit = false;
while !exit {
interface
.set_prompt(&format!("{}=> ", self.path().bright_cyan()))
.expect("failed to set prompt");
match interface.read_line() {
Ok(ReadResult::Input(s)) => match self.parse_line(&s, true, &mut std::io::stdout())
{
LineResult::Exit => exit = true,
_ => (),
},
_ => (),
}
}
}
}
#[derive(Debug, PartialEq)]
struct SubClass<'a> {
name: String,
help: &'a str,
classes: Vec<Rc<SubClass<'a>>>,
actions: Vec<Action<'a>>,
}
impl<'a> SubClass<'a> {
fn with_name(name: &str, help_msg: &'a str) -> Self {
SubClass {
name: name.to_lowercase(),
help: help_msg,
classes: Vec::new(),
actions: Vec::new(),
}
}
}
struct Action<'a> {
name: String,
help: &'a str,
closure: RefCell<Box<FnMut(&[&str]) + 'a>>,
}
impl<'a> Action<'a> {
fn call(&self, arguments: &[&str]) {
let c = &mut *self.closure.borrow_mut();
c(arguments);
}
#[cfg(test)]
fn blank_fn(name: &str, help_msg: &'a str) -> Self {
Action {
name: name.to_lowercase(),
help: help_msg,
closure: RefCell::new(Box::new(|_| ())),
}
}
}
impl<'a> PartialEq for Action<'a> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.help == other.help
}
}
impl<'a> fmt::Debug for Action<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Action {{ name: {}, help: {} }}", self.name, self.help)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn subclass_with_name_test() {
let sc = SubClass::with_name("NAME", "Help Message");
assert_eq!(&sc.name, "name");
assert_eq!(sc.help, "Help Message");
}
#[test]
fn action_debug_test() {
let a = Action::blank_fn("action-name", "help me!");
assert_eq!(
&format!("{:?}", a),
"Action { name: action-name, help: help me! }"
);
}
#[test]
fn current_path_test() {
let mut cmder = Builder::default_config("base")
.begin_class("one", "")
.begin_class("two", "")
.into_commander()
.unwrap();
let w = &mut std::io::sink();
assert_eq!(cmder.path(), "base");
cmder.parse_line("one two", true, w);
assert_eq!(cmder.path(), "base.one.two");
cmder.parse_line("c", true, w);
assert_eq!(cmder.path(), "base");
cmder.parse_line("one", true, w);
assert_eq!(cmder.path(), "base.one");
}
}