use std::collections::HashMap;
use std::slice::Iter;
use crate::command::{BaseCommand, Command};
pub struct CommandSet<'a, S> {
cmds: HashMap<String, Box<Command<'a, S>>>,
order: Vec<String>,
}
impl<'a, S> Default for CommandSet<'a, S> {
fn default() -> Self {
Self::new()
}
}
impl<'a, S> CommandSet<'a, S> {
pub fn new() -> Self {
CommandSet {
cmds: HashMap::new(),
order: Vec::new(),
}
}
pub fn new_from_vec(cmds: Vec<Command<'a, S>>) -> Self {
let mut cmd_set = CommandSet::new();
for cmd in cmds {
cmd_set.add(cmd);
}
cmd_set
}
}
impl<'a, S> CommandSet<'a, S> {
#[allow(clippy::borrowed_box)]
pub fn get(&self, name: &str) -> Option<&Box<Command<'a, S>>> {
self.cmds.get(name)
}
pub fn add(&mut self, cmd: Command<'a, S>) {
let cmd_name = cmd.name().to_string();
self.cmds.insert(cmd_name.clone(), Box::new(cmd));
self.order.push(cmd_name);
}
pub fn contains(&self, name: &str) -> bool {
self.cmds.contains_key(name)
}
pub fn len(&self) -> usize {
self.cmds.len()
}
pub fn names(&self) -> Vec<String> {
self.order.clone()
}
pub fn iter(&self) -> CommandSetIterator<S> {
CommandSetIterator {
iter: self.order.iter(),
cmds: self,
}
}
}
pub struct CommandSetIterator<'a, S> {
iter: Iter<'a, String>,
cmds: &'a CommandSet<'a, S>,
}
impl<'a, S: 'a> Iterator for CommandSetIterator<'a, S> {
type Item = &'a Box<Command<'a, S>>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|k| {
self.cmds
.get(k)
.expect("order vector and command map mismatch")
})
}
}
impl<'a, S: 'a> IntoIterator for &'a CommandSet<'a, S> {
type Item = &'a Box<Command<'a, S>>;
type IntoIter = CommandSetIterator<'a, S>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::command::{BaseCommand, Command};
use crate::Result;
struct EmptyCommand<'a> {
name: &'a str,
}
impl<'a> EmptyCommand<'a> {
fn new(name: &'a str) -> EmptyCommand<'a> {
EmptyCommand { name }
}
}
impl<'a> BaseCommand for EmptyCommand<'a> {
type State = ();
fn name(&self) -> &str {
self.name
}
#[cfg(not(tarpaulin_include))]
fn validate_args(&self, _: &[String]) -> Result<()> {
Ok(())
}
#[cfg(not(tarpaulin_include))]
fn execute(&self, _: &mut Self::State, _: &[String]) -> Result<String> {
Ok(String::from(""))
}
}
#[test]
fn get() {
let cmd_set = CommandSet::new_from_vec(vec![
Command::new_leaf(EmptyCommand::new("a")),
Command::new_leaf(EmptyCommand::new("b")),
Command::new_leaf(EmptyCommand::new("c")),
]);
assert!(cmd_set.get("b").is_some());
assert_eq!(cmd_set.get("b").unwrap().name(), "b");
}
#[test]
fn add() {
let mut cmd_set = CommandSet::new();
assert!(cmd_set.get("a").is_none());
cmd_set.add(Command::new_leaf(EmptyCommand::new("a")));
assert_eq!(cmd_set.get("a").unwrap().name(), "a");
}
#[test]
fn contains() {
let cmd_set = CommandSet::new_from_vec(vec![Command::new_leaf(EmptyCommand::new("b"))]);
assert!(cmd_set.contains("b"));
assert!(!cmd_set.contains("I DONT EXIST"));
}
#[test]
fn len() {
let mut cmd_set = CommandSet::new();
assert_eq!(cmd_set.len(), 0);
cmd_set.add(Command::new_leaf(EmptyCommand::new("a")));
assert_eq!(cmd_set.len(), 1);
cmd_set.add(Command::new_leaf(EmptyCommand::new("b")));
cmd_set.add(Command::new_leaf(EmptyCommand::new("c")));
assert_eq!(cmd_set.len(), 3);
}
#[test]
fn iter() {
let cmd_set = CommandSet::new_from_vec(vec![
Command::new_leaf(EmptyCommand::new("a")),
Command::new_leaf(EmptyCommand::new("b")),
Command::new_leaf(EmptyCommand::new("c")),
]);
let mut num_a = 0;
let mut num_b = 0;
let mut num_c = 0;
for cmd in &cmd_set {
match cmd.name() {
"a" => num_a += 1,
"b" => num_b += 1,
"c" => num_c += 1,
_ => panic!("unexpected command name from iteration"),
}
}
assert_eq!(num_a, 1);
assert_eq!(num_b, 1);
assert_eq!(num_c, 1);
}
#[test]
fn names() {
let cmd_set = CommandSet::new_from_vec(vec![
Command::new_leaf(EmptyCommand::new("a")),
Command::new_leaf(EmptyCommand::new("b")),
Command::new_leaf(EmptyCommand::new("c")),
]);
let names = cmd_set.names();
assert_eq!(vec!["a", "b", "c"], names);
}
}