use crate::ported::params::{getaparam, getsparam};
fn split_lower_prefix(rest: &str) -> (&str, &str) {
let end = rest
.find(|c: char| !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'))
.unwrap_or(rest.len());
(&rest[..end], &rest[end..])
}
fn probe_names(segment: &str, state: Option<&str>, param: &str) -> [String; 3] {
let seg = segment.replace('-', "_");
let mut name = format!("prompt_{seg}");
if let Some(st) = state {
name.push('_');
name.push_str(st);
}
let rest = &name["prompt_".len()..];
let (m1, m2) = split_lower_prefix(rest);
let p1 = format!("POWERLEVEL9K_{}{}_{}", m1.to_ascii_uppercase(), m2, param);
let m1_trim = m1.strip_suffix('_').unwrap_or(m1);
let p2 = format!("POWERLEVEL9K_{}_{}", m1_trim.to_ascii_uppercase(), param);
let p3 = format!("POWERLEVEL9K_{param}");
[p1, p2, p3]
}
fn p9k_param_opt(segment: &str, state: Option<&str>, param: &str) -> Option<String> {
for name in probe_names(segment, state, param) {
if let Some(v) = getsparam(&name) {
return Some(v);
}
}
None
}
pub fn p9k_param(segment: &str, state: Option<&str>, param: &str, default: &str) -> String {
p9k_param_opt(segment, state, param).unwrap_or_else(|| default.to_string())
}
pub fn p9k_param_bool(segment: &str, state: Option<&str>, param: &str, default: bool) -> bool {
match p9k_param_opt(segment, state, param) {
Some(v) => v == "true",
None => default,
}
}
pub fn p9k_param_arr(segment: &str, state: Option<&str>, param: &str) -> Vec<String> {
for name in probe_names(segment, state, param) {
if let Some(v) = getaparam(&name) {
return v;
}
if let Some(s) = getsparam(&name) {
return vec![s];
}
}
Vec::new()
}
pub fn p9k_global(name: &str, default: &str) -> String {
getsparam(&format!("POWERLEVEL9K_{name}")).unwrap_or_else(|| default.to_string())
}
pub fn p9k_global_arr(name: &str) -> Vec<String> {
let full = format!("POWERLEVEL9K_{name}");
if let Some(v) = getaparam(&full) {
return v;
}
if let Some(s) = getsparam(&full) {
return vec![s];
}
Vec::new()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::options::{opt_state_get, opt_state_set};
use crate::ported::params::{setaparam, setsparam, unsetparam};
fn with_exec<F: FnOnce()>(body: F) {
let _g = crate::test_util::global_state_lock();
let saved = opt_state_get("exec").unwrap_or(false);
opt_state_set("exec", true);
body();
opt_state_set("exec", saved);
}
#[test]
fn state_specific_param_wins() {
with_exec(|| {
setsparam("POWERLEVEL9K_VCS_CLEAN_TESTFG1", "76");
setsparam("POWERLEVEL9K_VCS_TESTFG1", "39");
setsparam("POWERLEVEL9K_TESTFG1", "244");
assert_eq!(p9k_param("vcs", Some("CLEAN"), "TESTFG1", "def"), "76");
unsetparam("POWERLEVEL9K_VCS_CLEAN_TESTFG1");
unsetparam("POWERLEVEL9K_VCS_TESTFG1");
unsetparam("POWERLEVEL9K_TESTFG1");
});
}
#[test]
fn segment_param_fallback_when_state_var_unset() {
with_exec(|| {
unsetparam("POWERLEVEL9K_VCS_CLEAN_TESTFG2");
setsparam("POWERLEVEL9K_VCS_TESTFG2", "39");
setsparam("POWERLEVEL9K_TESTFG2", "244");
assert_eq!(p9k_param("vcs", Some("CLEAN"), "TESTFG2", "def"), "39");
unsetparam("POWERLEVEL9K_VCS_TESTFG2");
unsetparam("POWERLEVEL9K_TESTFG2");
});
}
#[test]
fn global_param_fallback_when_segment_vars_unset() {
with_exec(|| {
unsetparam("POWERLEVEL9K_VCS_CLEAN_TESTFG3");
unsetparam("POWERLEVEL9K_VCS_TESTFG3");
setsparam("POWERLEVEL9K_TESTFG3", "244");
assert_eq!(p9k_param("vcs", Some("CLEAN"), "TESTFG3", "def"), "244");
unsetparam("POWERLEVEL9K_TESTFG3");
});
}
#[test]
fn default_when_nothing_set() {
with_exec(|| {
unsetparam("POWERLEVEL9K_DIR_TESTFG4");
unsetparam("POWERLEVEL9K_TESTFG4");
assert_eq!(p9k_param("dir", None, "TESTFG4", "blue"), "blue");
});
}
#[test]
fn empty_value_is_a_hit_not_a_miss() {
with_exec(|| {
setsparam("POWERLEVEL9K_DIR_TESTFG5", "");
setsparam("POWERLEVEL9K_TESTFG5", "244");
assert_eq!(p9k_param("dir", None, "TESTFG5", "def"), "");
unsetparam("POWERLEVEL9K_DIR_TESTFG5");
unsetparam("POWERLEVEL9K_TESTFG5");
});
}
#[test]
fn dashed_segment_name_maps_to_underscores() {
with_exec(|| {
setsparam("POWERLEVEL9K_FOO_BAR_TESTFG6", "196");
assert_eq!(p9k_param("foo-bar", None, "TESTFG6", "def"), "196");
unsetparam("POWERLEVEL9K_FOO_BAR_TESTFG6");
});
}
#[test]
fn full_prompt_prefixed_segment_accepted() {
with_exec(|| {
setsparam("POWERLEVEL9K_PROMPT_CHAR_TESTFG7", "31");
assert_eq!(p9k_param("prompt_char", None, "TESTFG7", "def"), "31");
unsetparam("POWERLEVEL9K_PROMPT_CHAR_TESTFG7");
});
}
#[test]
fn bool_true_string_only() {
with_exec(|| {
setsparam("POWERLEVEL9K_DIR_TESTBOOL1", "true");
assert!(p9k_param_bool("dir", None, "TESTBOOL1", false));
setsparam("POWERLEVEL9K_DIR_TESTBOOL1", "false");
assert!(!p9k_param_bool("dir", None, "TESTBOOL1", true));
setsparam("POWERLEVEL9K_DIR_TESTBOOL1", "1");
assert!(!p9k_param_bool("dir", None, "TESTBOOL1", true));
unsetparam("POWERLEVEL9K_DIR_TESTBOOL1");
assert!(p9k_param_bool("dir", None, "TESTBOOL1", true));
assert!(!p9k_param_bool("dir", None, "TESTBOOL1", false));
});
}
#[test]
fn param_arr_reads_arrays_and_wraps_scalars() {
with_exec(|| {
setaparam(
"POWERLEVEL9K_DIR_TESTARR1",
vec!["a".into(), "b c".into()],
);
assert_eq!(
p9k_param_arr("dir", None, "TESTARR1"),
vec!["a".to_string(), "b c".to_string()]
);
unsetparam("POWERLEVEL9K_DIR_TESTARR1");
setsparam("POWERLEVEL9K_DIR_TESTARR2", "solo");
assert_eq!(
p9k_param_arr("dir", None, "TESTARR2"),
vec!["solo".to_string()]
);
unsetparam("POWERLEVEL9K_DIR_TESTARR2");
unsetparam("POWERLEVEL9K_DIR_TESTARR3");
unsetparam("POWERLEVEL9K_TESTARR3");
assert!(p9k_param_arr("dir", None, "TESTARR3").is_empty());
});
}
#[test]
fn param_arr_probe_order() {
with_exec(|| {
setaparam("POWERLEVEL9K_VCS_CLEAN_TESTARR4", vec!["x".into()]);
setaparam("POWERLEVEL9K_VCS_TESTARR4", vec!["y".into()]);
assert_eq!(
p9k_param_arr("vcs", Some("CLEAN"), "TESTARR4"),
vec!["x".to_string()]
);
unsetparam("POWERLEVEL9K_VCS_CLEAN_TESTARR4");
unsetparam("POWERLEVEL9K_VCS_TESTARR4");
});
}
#[test]
fn global_scalar_and_default() {
with_exec(|| {
setsparam("POWERLEVEL9K_TESTGLOB1", "nerdfont-complete");
assert_eq!(p9k_global("TESTGLOB1", "ascii"), "nerdfont-complete");
unsetparam("POWERLEVEL9K_TESTGLOB1");
assert_eq!(p9k_global("TESTGLOB1", "ascii"), "ascii");
});
}
#[test]
fn global_arr_forms() {
with_exec(|| {
setaparam(
"POWERLEVEL9K_TESTGLOB2",
vec!["dir".into(), "vcs".into()],
);
assert_eq!(
p9k_global_arr("TESTGLOB2"),
vec!["dir".to_string(), "vcs".to_string()]
);
unsetparam("POWERLEVEL9K_TESTGLOB2");
setsparam("POWERLEVEL9K_TESTGLOB3", "status");
assert_eq!(p9k_global_arr("TESTGLOB3"), vec!["status".to_string()]);
unsetparam("POWERLEVEL9K_TESTGLOB3");
unsetparam("POWERLEVEL9K_TESTGLOB4");
assert!(p9k_global_arr("TESTGLOB4").is_empty());
});
}
#[test]
fn split_matches_zsh_pattern() {
assert_eq!(split_lower_prefix("vcs_CLEAN"), ("vcs_", "CLEAN"));
assert_eq!(split_lower_prefix("dir"), ("dir", ""));
assert_eq!(split_lower_prefix("dir_writable"), ("dir_writable", ""));
assert_eq!(
split_lower_prefix("dir_NOT_WRITABLE"),
("dir_", "NOT_WRITABLE")
);
}
#[test]
fn probe_names_exact_order() {
assert_eq!(
probe_names("vcs", Some("CLEAN"), "FOREGROUND"),
[
"POWERLEVEL9K_VCS_CLEAN_FOREGROUND".to_string(),
"POWERLEVEL9K_VCS_FOREGROUND".to_string(),
"POWERLEVEL9K_FOREGROUND".to_string(),
]
);
assert_eq!(
probe_names("dir", None, "BACKGROUND"),
[
"POWERLEVEL9K_DIR_BACKGROUND".to_string(),
"POWERLEVEL9K_DIR_BACKGROUND".to_string(),
"POWERLEVEL9K_BACKGROUND".to_string(),
]
);
}
}