Lines
90.57 %
Functions
56.25 %
Branches
100 %
use std::env;
use std::str::FromStr;
use crate::error::Error;
pub mod cd;
pub mod error;
pub mod hint;
pub mod matcher;
pub mod shell;
use matcher::Matcher;
/// Number or String input
#[derive(Clone, Debug)]
pub enum NumberOrString {
Number(u8),
Str(String),
}
/// Create instance from string argument
impl FromStr for NumberOrString {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(s.parse::<u8>()
.map(NumberOrString::Number)
.unwrap_or_else(|_| NumberOrString::Str(s.to_string())))
/// Run cd command
pub fn run_cd(target: &NumberOrString) -> Result<String, Error> {
let cwd = env::current_dir().map_err(Error::InvalidCwd)?;
let matcher = Matcher::default();
let new_path = match target {
NumberOrString::Number(level) => cd::up_by_count(&cwd, *level as usize),
NumberOrString::Str(component) => cd::up_to_target(&matcher, &cwd, component)?,
};
Ok(format!("{}", new_path.display()))
/// Run hint command
pub fn run_hint(term: Option<&str>) -> Result<String, Error> {
Ok(hint::completion_for(&matcher, &cwd, term))
/// Run init command
pub fn run_init(alias: &String, hint: &Option<String>) -> Result<String, Error> {
let shell = shell::initialize(alias.to_string(), hint)?;
Ok(shell.compile_script())
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_cd_by_count() {
// GIVEN a numeric target
let target = NumberOrString::Number(1);
// WHEN running cd
let actual = run_cd(&target);
// THEN we should get a valid path
assert!(actual.is_ok());
fn run_cd_by_name_no_match() {
// GIVEN a non-matching target name
let target = NumberOrString::Str("zzz_nonexistent_zzz".to_string());
// THEN we should get a NoMatch error
assert!(matches!(actual, Err(Error::NoMatch)));
fn run_hint_without_term() {
// GIVEN no search term
// WHEN running hint
let actual = run_hint(None);
// THEN we should get a non-empty list of components
assert!(!actual.unwrap().is_empty());
fn run_hint_with_term() {
// GIVEN a search term
let term = Some("nonexistent");
let actual = run_hint(term);
// THEN we should get a result (empty string for no match)
fn run_init_bash() {
// GIVEN a bash shell hint and alias
let alias = String::from("up");
let hint = Some("bash".to_string());
// WHEN running init
let actual = run_init(&alias, &hint);
// THEN we should get a script containing the alias
assert!(actual.unwrap().contains("up"));
fn run_init_no_shell_no_env() {
// GIVEN no shell hint and no SHELL environment variable
env::remove_var("SHELL");
let actual = run_init(&alias, &None);
// THEN we should get a ShellNotDetected error
assert!(matches!(actual, Err(Error::ShellNotDetected)));