use std::env;
use crate::CompletionInput;
pub struct BashCompletionInput {
line: String,
cursor_position: usize,
}
#[derive(Debug)]
pub enum BashCompletionInputParsingError {
MissingEnvVar,
CursorPositionNotNumber,
}
impl BashCompletionInput {
pub fn from_env() -> Result<Self, BashCompletionInputParsingError> {
Ok(BashCompletionInput {
line: env::var("COMP_LINE").map_err(|_| BashCompletionInputParsingError::MissingEnvVar)?,
cursor_position: env::var("COMP_POINT")
.map_err(|_| BashCompletionInputParsingError::MissingEnvVar)?
.parse::<usize>()
.map_err(|_| BashCompletionInputParsingError::CursorPositionNotNumber)?,
})
}
}
impl<T> From<T> for BashCompletionInput
where
T: Into<String>,
{
fn from(s: T) -> Self {
let line = s.into();
let cursor_position = line.len();
BashCompletionInput {
line,
cursor_position,
}
}
}
impl CompletionInput for BashCompletionInput {
fn args(&self) -> Vec<&str> {
self.line.split(" ").collect()
}
fn arg_index(&self) -> usize {
self.line.split_at(self.cursor_position).0
.chars()
.filter(|c| *c == ' ')
.count()
}
fn char_index(&self) -> usize {
let start = self.line.split_at(self.cursor_position).0;
let current_word_fraction = start.rsplitn(2, ' ').next();
match current_word_fraction {
Some(word) => word.len(),
None => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trait_impl_two_parts() {
let input = BashCompletionInput::from("democli src/li");
assert_eq!(vec!["democli", "src/li"], input.args());
assert_eq!(1, input.arg_index());
assert_eq!(6, input.char_index());
}
#[test]
fn test_trait_impl_one_part() {
let input = BashCompletionInput::from("democli ");
assert_eq!(vec!["democli", ""], input.args());
assert_eq!(1, input.arg_index());
assert_eq!(0, input.char_index());
}
}