use crate::common::check_directory_path;
use crate::error::KcfgError;
use clap::{ArgEnum, Clap, ValueHint};
use indoc::formatdoc;
use std::str::FromStr;
#[derive(Clap, Debug)]
pub struct InitOptions {
#[clap(arg_enum, default_value = "common")]
pub shell_type: InitShellType,
#[clap(short = 'p', long = "path", value_hint = ValueHint::DirPath)]
pub custom_path: Option<String>,
}
#[derive(Clap, Debug, ArgEnum)]
pub enum InitShellType {
Zsh,
Bash,
Common,
}
impl FromStr for InitShellType {
type Err = KcfgError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"zsh" => Ok(InitShellType::Zsh),
"bash" => Ok(InitShellType::Bash),
"common" => Ok(InitShellType::Common),
_ => Err(KcfgError::NotSupportedShellType(s.to_string())),
}
}
}
pub fn init(params: InitOptions) -> Result<String, KcfgError> {
let path = match params.custom_path {
None => {
let current_path = std::env::current_exe()?;
current_path
.to_str()
.ok_or_else(|| KcfgError::InvalidPath(current_path.clone()))?
.to_string()
}
Some(custom_path) => {
check_directory_path(&custom_path)?;
custom_path
}
};
Ok(match params.shell_type {
InitShellType::Common => init_common_full(&path),
InitShellType::Zsh => init_zsh(&path),
InitShellType::Bash => init_bash(&path),
})
}
fn init_common_full(current_path: &str) -> String {
return formatdoc! {"
function kcfg() {{
result=$({cmd} $@)
if [[ $result = 'export '* ]]
then
eval $result
else
echo $result
fi
}}
",
cmd = current_path
};
}
fn init_zsh(current_path: &str) -> String {
formatdoc!("source <({} init zsh --print-full-init)", current_path)
}
fn init_bash(current_path: &str) -> String {
formatdoc! {"
__main() {{
local major=\"${{BASH_VERSINFO[0]}}\"
local minor=\"${{BASH_VERSINFO[1]}}\"
if ((major > 4)) || {{ ((major == 4)) && ((minor >= 1)); }}; then
source <(\"{cmd}\" init bash --print-full-init)
else
source /dev/stdin <<<\"$(\"{cmd}\" init bash --print-full-init)\"
fi
}}
__main
unset -f __main
",
cmd = current_path
}
}
#[cfg(test)]
mod tests {
use super::*;
mod router {
use super::*;
#[test]
fn can_fail_with_wrong_path() {
let params = InitOptions {
shell_type: InitShellType::Bash,
custom_path: Some("toto".to_string()),
};
match init(params) {
Ok(_) => panic!("Test should have failed"),
Err(e) => {
if !matches!(e, KcfgError::PathDoesNotExist(_)) {
panic!("Test failed with wrong error");
}
}
}
}
#[test]
fn can_fail_with_wrong_directory() {
let params = InitOptions {
shell_type: InitShellType::Bash,
custom_path: Some("./Cargo.toml".to_string()),
};
match init(params) {
Ok(_) => panic!("Test should have failed"),
Err(e) => {
if !matches!(e, KcfgError::WrongDirectory(_)) {
panic!("Test failed with wrong error");
}
}
}
}
#[test]
fn can_succeed_with_bash() {
let params = InitOptions {
shell_type: InitShellType::Bash,
custom_path: Some("/tmp".to_string()),
};
let res = init(params).unwrap();
assert_eq!(
formatdoc! {"
__main() {{
local major=\"${{BASH_VERSINFO[0]}}\"
local minor=\"${{BASH_VERSINFO[1]}}\"
if ((major > 4)) || {{ ((major == 4)) && ((minor >= 1)); }}; then
source <(\"/tmp\" init bash --print-full-init)
else
source /dev/stdin <<<\"$(\"/tmp\" init bash --print-full-init)\"
fi
}}
__main
unset -f __main
"},
res,
)
}
#[test]
fn can_succeed_with_zsh() {
let params = InitOptions {
shell_type: InitShellType::Zsh,
custom_path: Some("/tmp".to_string()),
};
let res = init(params).unwrap();
assert_eq!("source <(/tmp init zsh --print-full-init)".to_string(), res,)
}
#[test]
fn can_succeed_with_common() {
let params = InitOptions {
shell_type: InitShellType::Common,
custom_path: Some("/tmp".to_string()),
};
let res = init(params).unwrap();
assert_eq!(
formatdoc! {"
function kcfg() {{
result=$(/tmp $@)
if [[ $result = 'export '* ]]
then
eval $result
else
echo $result
fi
}}
"},
res,
)
}
}
mod init {
use super::*;
#[test]
fn test_init_common_full() {
assert_eq!(
formatdoc! {"
function kcfg() {{
result=$(test $@)
if [[ $result = 'export '* ]]
then
eval $result
else
echo $result
fi
}}
"},
init_common_full("test")
);
}
#[test]
fn test_init_zsh() {
assert_eq!(
"source <(test init zsh --print-full-init)".to_string(),
init_zsh("test")
);
}
#[test]
fn test_init_bash() {
assert_eq!(
formatdoc! {"
__main() {{
local major=\"${{BASH_VERSINFO[0]}}\"
local minor=\"${{BASH_VERSINFO[1]}}\"
if ((major > 4)) || {{ ((major == 4)) && ((minor >= 1)); }}; then
source <(\"test\" init bash --print-full-init)
else
source /dev/stdin <<<\"$(\"test\" init bash --print-full-init)\"
fi
}}
__main
unset -f __main
"},
init_bash("test")
);
}
}
}