use cmd::Cmd;
use env::{Env, ObjectSelection};
use kobj::ObjType;
use rustyline::completion::{Completer, Pair};
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::{Context, Helper, Result};
use std::rc::Rc;
pub struct ClickHelper {
commands: Vec<Box<dyn Cmd>>,
help_topics: Vec<&'static str>,
env: Option<Rc<Env>>,
}
impl Helper for ClickHelper {}
impl Highlighter for ClickHelper {}
impl Hinter for ClickHelper {
fn hint(&self, _line: &str, _pos: usize, _context: &Context) -> Option<String> {
None
}
}
impl ClickHelper {
pub fn new(commands: Vec<Box<dyn Cmd>>, help_topics: Vec<&'static str>) -> ClickHelper {
ClickHelper {
commands,
help_topics,
env: None,
}
}
pub fn set_env(&mut self, env: Option<Rc<Env>>) {
self.env = env;
}
}
impl ClickHelper {
#[allow(clippy::borrowed_box)]
fn get_exact_command(&self, line: &str) -> Option<&Box<dyn Cmd>> {
for cmd in self.commands.iter() {
if cmd.is(line) {
return Some(cmd);
}
}
None
}
fn get_command_completions(&self, line: &str, candidates: &mut Vec<Pair>) {
for cmd in self.commands.iter() {
if cmd.get_name().starts_with(line) {
candidates.push(Pair {
display: cmd.get_name().to_owned(),
replacement: cmd.get_name().to_owned(),
});
}
}
}
fn get_help_completions(&self, line: &str, candidates: &mut Vec<Pair>) {
for topic in self.help_topics.iter() {
if topic.starts_with(line) {
candidates.push(Pair {
display: (*topic).to_string(),
replacement: (*topic).to_string(),
});
}
}
}
}
pub fn long_matches(long: &Option<&str>, prefix: &str) -> bool {
match long {
Some(lstr) => lstr.starts_with(prefix),
None => false,
}
}
impl Completer for ClickHelper {
type Candidate = Pair;
fn complete(&self, line: &str, pos: usize, _ctx: &Context) -> Result<(usize, Vec<Pair>)> {
let mut v = Vec::new();
if pos == 0 {
for cmd in self.commands.iter() {
v.push(Pair {
display: cmd.get_name().to_owned(),
replacement: cmd.get_name().to_owned(),
});
}
Ok((0, v))
} else {
let mut split = line.split_whitespace();
if let Some(linecmd) = split.next() {
if let Some(cmd) = self.get_exact_command(linecmd) {
let (pos, prefix) = match split.next_back() {
Some(back) => {
if line.ends_with(' ') {
let mut count = split.filter(|s| !s.starts_with('-')).count();
if !back.starts_with('-') {
count += 1;
}
(count, "")
} else if back == "-" {
return Ok((
line.len(),
vec![Pair {
display: "-".to_owned(),
replacement: "-".to_owned(),
}],
));
} else if let Some(opt_str) = back.strip_prefix("--") {
let mut opts = cmd.complete_option(opt_str);
if "--help".starts_with(back) {
opts.push(Pair {
display: "--help".to_owned(),
replacement: "help"[(back.len() - 2)..].to_owned(),
});
}
return Ok((line.len(), opts));
} else {
(split.filter(|s| !s.starts_with('-')).count(), back)
}
}
None => (0, ""),
};
if let Some(ref env) = self.env {
let opts = cmd.try_complete(pos, prefix, &*env);
return Ok((line.len(), opts));
} else {
return Ok((0, v));
}
} else if linecmd == "help" {
let cmd_part = split.next().unwrap_or("");
if split.next().is_none() {
self.get_command_completions(cmd_part, &mut v);
self.get_help_completions(cmd_part, &mut v);
return Ok((5, v)); }
} else {
self.get_command_completions(linecmd, &mut v);
if "help".starts_with(linecmd) {
v.push(Pair {
display: "help".to_string(),
replacement: "help".to_string(),
});
}
}
}
Ok((0, v))
}
}
}
pub fn context_complete(prefix: &str, env: &Env) -> Vec<Pair> {
let mut v = Vec::new();
for context in env.config.contexts.keys() {
if let Some(rest) = context.strip_prefix(prefix) {
v.push(Pair {
display: context.to_string(),
replacement: rest.to_string(),
})
}
}
v
}
pub fn namespace_completer(prefix: &str, env: &Env) -> Vec<Pair> {
match env.run_on_kluster(|k| k.namespaces_for_context()) {
Some(v) => v
.iter()
.filter(|ns| ns.starts_with(prefix))
.map(|ns| Pair {
display: ns.clone(),
replacement: ns[prefix.len()..].to_string(),
})
.collect(),
None => vec![],
}
}
pub fn container_completer(prefix: &str, env: &Env) -> Vec<Pair> {
let mut v = vec![];
if let ObjectSelection::Single(obj) = env.current_selection() {
if let ObjType::Pod { ref containers } = obj.typ {
for cont in containers.iter() {
if let Some(rest) = cont.strip_prefix(prefix) {
v.push(Pair {
display: cont.clone(),
replacement: rest.to_string(),
});
}
}
}
}
v
}
macro_rules! possible_values_completer {
($name: ident, $values: expr) => {
pub fn $name(prefix: &str, _env: &Env) -> Vec<Pair> {
let mut v = vec![];
for val in $values.iter() {
if let Some(rest) = val.strip_prefix(prefix) {
v.push(Pair {
display: val.to_string(),
replacement: rest.to_string(),
});
}
}
v
}
};
}
possible_values_completer!(setoptions_values_completer, ::cmd::SET_OPTS);
possible_values_completer!(
portforwardaction_values_completer,
["list", "output", "stop"]
);