use crate::split::split;
pub struct ArbitraryArgument {
pub name: String,
pub description: String,
}
pub struct Flag {
pub name: String,
pub arguments: Vec<ArbitraryArgument>,
}
impl Flag {
pub fn new(flag: &str) -> Flag {
Flag {
name: String::from(flag),
arguments: vec![],
}
}
}
pub enum Argument {
Flag(Flag),
ArbitraryArgument(ArbitraryArgument),
}
impl From<&str> for Argument {
fn from(flag: &str) -> Argument {
Argument::Flag(Flag::new(flag))
}
}
pub struct Command {
pub name: String,
pub args: Vec<Argument>,
pub subcommands: Vec<Command>,
}
impl Command {
pub fn new(name: &str) -> Command {
Command {
name: String::from(name),
args: vec![],
subcommands: vec![],
}
}
pub fn subcommand(mut self, cmd: Command) -> Command {
self.subcommands.push(cmd);
self
}
pub fn arg<T: Into<Argument>>(mut self, arg: T) -> Command {
self.args.push(arg.into());
self
}
}
pub enum CompletionResult {
None,
Description(String),
PossibilityList(Vec<String>),
}
fn command_names(commands: &[Command]) -> Vec<String> {
let mut result = vec![];
for cmd in commands {
result.push(cmd.name.clone());
}
result
}
fn active_command<'a>(cmdline: &[String], commands: &'a [Command]) -> Option<&'a Command> {
let mut result = None;
for component in cmdline {
for command in commands {
if *component == command.name {
result = Some(command)
}
}
}
result
}
fn get_possible_completions(cmd: &Command) -> CompletionResult {
let mut list = vec![];
for arg in &cmd.args {
match arg {
Argument::ArbitraryArgument(_) => {
return CompletionResult::Description(String::from("Various artists"))
}
Argument::Flag(flag) => list.push(flag.name.clone()),
}
}
for cmd in &cmd.subcommands {
list.push(cmd.name.clone())
}
CompletionResult::PossibilityList(list)
}
pub fn complete(previous: &str, commands: &[Command]) -> CompletionResult {
if previous.is_empty() {
let possible_commands = command_names(commands);
if possible_commands.is_empty() {
CompletionResult::None
} else {
CompletionResult::PossibilityList(possible_commands)
}
} else {
let mut components = split(previous);
let to_complete = if previous.chars().last().unwrap().is_whitespace() {
String::new()
} else {
components.pop().unwrap()
};
let mut possibilities = if let Some(cmd) = active_command(&components, commands) {
if let CompletionResult::PossibilityList(possibilities) = get_possible_completions(cmd)
{
possibilities
} else {
return CompletionResult::Description(String::from("Various possible"));
}
} else if components.is_empty() {
command_names(commands)
} else {
vec![]
};
possibilities.retain(|possibility| possibility.starts_with(&to_complete));
CompletionResult::PossibilityList(possibilities)
}
}