use crate::common::check_file_path;
use crate::error::KcfgError;
use clap::{ArgEnum, Parser, ValueHint};
use indoc::formatdoc;
#[derive(Parser, Debug)]
pub struct InitOptions {
#[clap(arg_enum)]
pub shell_type: Option<InitShellType>,
#[clap(short = 'p', long = "path", value_hint = ValueHint::DirPath)]
pub custom_path: Option<String>,
}
#[derive(Parser, Debug, ArgEnum, Clone)]
pub enum InitShellType {
Zsh,
Bash,
}
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_file_path(&custom_path)?;
custom_path
}
};
let res = match params.shell_type {
Some(shell_type) => match shell_type {
InitShellType::Bash => init_bash(&path),
InitShellType::Zsh => init_zsh(&path),
},
None => init_common_full(&path),
};
Ok(res)
}
fn init_common_full(current_path: &str) -> String {
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 {
format!("source <({} 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)
else
source /dev/stdin <<<\"$(\"{cmd}\" 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: Some(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_filr() {
let params = InitOptions {
shell_type: Some(InitShellType::Bash),
custom_path: Some("src".to_string()),
};
match init(params) {
Ok(_) => panic!("Test should have failed"),
Err(e) => {
if !matches!(e, KcfgError::WrongFile(_)) {
panic!("Test failed with wrong error");
}
}
}
}
#[test]
fn can_succeed_with_bash() {
let params = InitOptions {
shell_type: Some(InitShellType::Bash),
custom_path: Some("src/main.rs".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 <(\"src/main.rs\" init)
else
source /dev/stdin <<<\"$(\"src/main.rs\" init)\"
fi
}}
__main
unset -f __main
"},
res,
)
}
#[test]
fn can_succeed_with_zsh() {
let params = InitOptions {
shell_type: Some(InitShellType::Zsh),
custom_path: Some("src/main.rs".to_string()),
};
let res = init(params).unwrap();
assert_eq!("source <(src/main.rs init)".to_string(), res,)
}
#[test]
fn can_succeed_with_common() {
let params = InitOptions {
shell_type: None,
custom_path: Some("src/main.rs".to_string()),
};
let res = init(params).unwrap();
assert_eq!(
formatdoc! {"
function kcfg() {{
result=$(src/main.rs $@)
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)".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)
else
source /dev/stdin <<<\"$(\"test\" init)\"
fi
}}
__main
unset -f __main
"},
init_bash("test")
);
}
}
}