use crate::facts::shell::Argv;
pub(super) const GIT_HEADS: &[&str] = &["git"];
pub(super) fn is_git(argv: &Argv) -> bool {
GIT_HEADS.contains(&argv.head_basename())
}
pub(super) fn git_subcommand(argv: &Argv) -> Option<&str> {
if !is_git(argv) {
return None;
}
let mut iter = argv.args.iter();
while let Some(a) = iter.next() {
match a.as_str() {
"-c" | "--config" | "-C" | "--git-dir" | "--work-tree" | "--namespace"
| "--exec-path" | "--super-prefix" => {
iter.next();
continue;
},
s if s.starts_with("--config=")
|| s.starts_with("--git-dir=")
|| s.starts_with("--work-tree=")
|| s.starts_with("--namespace=")
|| s.starts_with("--exec-path=")
|| s.starts_with("--super-prefix=") =>
{
continue;
},
s if s.starts_with('-') => continue,
s => return Some(s),
}
}
None
}
pub(super) fn args_after_subcommand<'a>(argv: &'a Argv, sub: &str) -> Vec<&'a str> {
let mut iter = argv.args.iter().map(String::as_str);
for a in iter.by_ref() {
if a == sub {
break;
}
}
iter.collect()
}
pub(super) fn config_overrides(argv: &Argv) -> impl Iterator<Item = &str> + '_ {
let mut iter = argv.args.iter();
std::iter::from_fn(move || {
while let Some(a) = iter.next() {
match a.as_str() {
"-c" | "--config" => {
if let Some(v) = iter.next() {
return Some(v.as_str());
}
},
s => {
if let Some(rest) = s.strip_prefix("--config=") {
return Some(rest);
}
},
}
}
None
})
}
#[derive(Clone, Copy)]
pub(super) enum BypassMatch {
Any,
Falsy,
Truthy,
}
fn is_falsy(v: &str) -> bool {
let v = v.trim();
["false", "no", "off", "0", ""]
.iter()
.any(|t| v.eq_ignore_ascii_case(t))
}
fn is_truthy(v: &str) -> bool {
let v = v.trim();
["true", "yes", "on", "1"]
.iter()
.any(|t| v.eq_ignore_ascii_case(t))
}
pub(super) fn bypass_value_matches(mode: BypassMatch, value: &str) -> bool {
match mode {
BypassMatch::Any => !value.trim().is_empty(),
BypassMatch::Falsy => is_falsy(value),
BypassMatch::Truthy => is_truthy(value),
}
}
pub(super) fn matches_env_keys(
argv: &Argv,
scope: Option<&[&str]>,
keys: &[(&str, BypassMatch)],
) -> bool {
let Some(sub) = git_subcommand(argv) else {
return false;
};
if let Some(allowed) = scope
&& !allowed.contains(&sub)
{
return false;
}
argv.env_assignments.iter().any(|ea| {
keys.iter().any(|(target, mode)| {
ea.key.eq_ignore_ascii_case(target) && bypass_value_matches(*mode, &ea.value)
})
})
}