use crate::common::check_file_path;
use crate::error::KcfgError;
use clap::{Parser, ValueEnum, ValueHint};
use indoc::formatdoc;
#[derive(Clone, Parser, Debug)]
pub struct InitOptions {
#[clap(value_enum)]
pub shell_type: Option<InitShellType>,
#[clap(short = 'p', long = "path", value_hint = ValueHint::DirPath)]
pub custom_path: Option<String>,
#[clap(short, long)]
pub full: bool,
}
#[derive(Parser, Debug, ValueEnum, Clone)]
pub enum InitShellType {
Zsh,
Bash,
Fish,
}
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, params.full),
InitShellType::Zsh => init_zsh(&path, params.full),
InitShellType::Fish => init_fish(&path, params.full),
},
None => return Err(KcfgError::MissingShellType),
};
Ok(res)
}
fn init_full_zsh_or_bash(current_path: &str) -> String {
formatdoc! {"
function kcfg() {{
result=$(\"{cmd}\" $@)
if [[ $result = 'export '* ]] then
eval $result
else
echo $result
fi
}}
",
cmd = current_path
}
}
fn init_full_fish(current_path: &str) -> String {
formatdoc! {"
function kcfg -d \"interpret kcfg\"
set result $(\"{cmd}\" $argv)
if string match -q -- \"export *\" $result
eval $result
else
echo $result
end
end
",
cmd = current_path
}
}
fn init_simple_zsh(current_path: &str) -> String {
format!("source <({} init zsh --full)", current_path)
}
fn init_simple_bash(current_path: &str) -> String {
formatdoc! {"
__kcfg() {{
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 --full)\"
fi
}}
__kcfg
unset -f __kcfg
",
cmd = current_path
}
}
fn init_simple_fish(current_path: &str) -> String {
format!("source <({} init fish --full)", current_path)
}
fn init_zsh(current_path: &str, is_full: bool) -> String {
if is_full {
init_full_zsh_or_bash(current_path)
} else {
init_simple_zsh(current_path)
}
}
fn init_bash(current_path: &str, is_full: bool) -> String {
if is_full {
init_full_zsh_or_bash(current_path)
} else {
init_simple_bash(current_path)
}
}
fn init_fish(current_path: &str, is_full: bool) -> String {
if is_full {
init_full_fish(current_path)
} else {
init_simple_fish(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()),
full: false,
};
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()),
full: false,
};
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()),
full: false,
};
let res = init(params).unwrap();
assert_eq!(
formatdoc! {"
__kcfg() {{
local major=\"${{BASH_VERSINFO[0]}}\"
local minor=\"${{BASH_VERSINFO[1]}}\"
if ((major > 4)) || {{ ((major == 4)) && ((minor >= 1)); }}; then
source <(\"src/main.rs\" init bash --print-full-init)
else
source /dev/stdin <<<\"$(\"src/main.rs\" init bash --full)\"
fi
}}
__kcfg
unset -f __kcfg
"},
res,
)
}
#[test]
fn can_succeed_with_bash_full() {
let params = InitOptions {
shell_type: Some(InitShellType::Bash),
custom_path: Some("src/main.rs".to_string()),
full: true,
};
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,
)
}
#[test]
fn can_succeed_with_zsh() {
let params = InitOptions {
shell_type: Some(InitShellType::Zsh),
custom_path: Some("src/main.rs".to_string()),
full: false,
};
let res = init(params).unwrap();
assert_eq!("source <(src/main.rs init zsh --full)".to_string(), res,);
}
#[test]
fn can_succeed_with_zsh_full() {
let params = InitOptions {
shell_type: Some(InitShellType::Zsh),
custom_path: Some("src/main.rs".to_string()),
full: true,
};
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,
)
}
#[test]
fn can_succeed_with_fish() {
let params = InitOptions {
shell_type: Some(InitShellType::Fish),
custom_path: Some("src/main.rs".to_string()),
full: false,
};
let res = init(params).unwrap();
assert_eq!("source <(src/main.rs init fish --full)".to_string(), res,);
}
#[test]
fn can_succeed_with_fish_full() {
let params = InitOptions {
shell_type: Some(InitShellType::Fish),
custom_path: Some("src/main.rs".to_string()),
full: true,
};
let res = init(params).unwrap();
assert_eq!(
formatdoc! {"
function kcfg -d \"interpret kcfg\"
set result $(\"src/main.rs\" $argv)
if string match -q -- \"export *\" $result
eval $result
else
echo $result
end
end
"},
res,
)
}
#[test]
fn can_fail_without_shell_type() {
let params = InitOptions {
shell_type: None,
custom_path: Some("src/main.rs".to_string()),
full: false,
};
match init(params) {
Ok(_) => panic!("Test should have failed"),
Err(e) => {
if !matches!(e, KcfgError::MissingShellType) {
panic!("Test failed with wrong error");
}
}
}
}
}
mod init {
use super::*;
#[test]
fn test_init_zsh() {
assert_eq!(
"source <(test init zsh --full)".to_string(),
init_zsh("test", false)
);
}
#[test]
fn test_init_zsh_full() {
assert_eq!(
formatdoc! {"
function kcfg() {{
result=$(\"test\" $@)
if [[ $result = 'export '* ]] then
eval $result
else
echo $result
fi
}}
"},
init_zsh("test", true)
);
}
#[test]
fn test_init_bash() {
assert_eq!(
formatdoc! {"
__kcfg() {{
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 --full)\"
fi
}}
__kcfg
unset -f __kcfg
"},
init_bash("test", false)
);
}
#[test]
fn test_init_bash_full() {
assert_eq!(
formatdoc! {"
function kcfg() {{
result=$(\"test\" $@)
if [[ $result = 'export '* ]] then
eval $result
else
echo $result
fi
}}
"},
init_bash("test", true)
);
}
#[test]
fn test_init_fish() {
assert_eq!(
"source <(test init fish --full)".to_string(),
init_fish("test", false)
);
}
#[test]
fn test_init_fish_full() {
assert_eq!(
formatdoc! {"
function kcfg -d \"interpret kcfg\"
set result $(\"test\" $argv)
if string match -q -- \"export *\" $result
eval $result
else
echo $result
end
end
"},
init_fish("test", true)
);
}
}
}