use crate::gitconfig as config;
use crate::rules::{Rule, Stance};
pub const KEY_ENABLED: &str = "amont.agent.enabled";
pub const KEY_STANCE: &str = "amont.agent.stance";
pub const ENV_OFF: &str = "AMONT_AGENT_OFF";
const ALLOWED: &[&str] = &["observe", "advise", "deny"];
pub fn switched_off() -> bool {
std::env::var_os(ENV_OFF).is_some_and(|v| !v.is_empty() && v != "0")
}
pub fn key_for(rule: &Rule) -> String {
format!("amont.agent.{}.stance", rule.id)
}
pub fn resolve(rule: &Rule) -> Stance {
if switched_off() || !config::boolean_or(KEY_ENABLED, true) {
return Stance::Observe;
}
let floor = config::enumerated_or(KEY_STANCE, ALLOWED, rule.default_stance.as_str());
let mine = config::enumerated_or(&key_for(rule), ALLOWED, floor);
ladder(rule, Stance::parse(mine))
}
pub fn ladder(rule: &Rule, configured: Option<Stance>) -> Stance {
configured
.unwrap_or(rule.default_stance)
.min(rule.max_stance)
}
pub fn resolve_assertion(assertion: &crate::assertions::Assertion) -> Stance {
if switched_off() || !config::boolean_or(KEY_ENABLED, true) {
return Stance::Observe;
}
let floor = config::enumerated_or(KEY_STANCE, ALLOWED, assertion.default_stance.as_str());
let key = format!("amont.agent.{}.stance", assertion.id);
let mine = config::enumerated_or(&key, ALLOWED, floor);
Stance::parse(mine).unwrap_or(assertion.default_stance)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rules::RULES;
#[test]
fn rule_ids_are_lowercase_kebab() {
for r in RULES {
assert!(
r.id.bytes()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == b'-'),
"rule id `{}` is not lowercase kebab-case",
r.id
);
assert!(!r.id.starts_with('-') && !r.id.ends_with('-'), "{}", r.id);
}
}
fn capped(max: Stance) -> Rule {
Rule {
id: "capped",
default_stance: Stance::Observe,
max_stance: max,
evidence: crate::rules::Evidence {
per_1000: 0.0,
measured: "-",
trend: crate::rules::Trend::Rare,
},
examine: crate::rules::Examine::Legacy(|_| None),
confirm: None,
}
}
#[test]
fn no_configured_stance_passes_the_ceiling() {
let rule = capped(Stance::Advise);
assert_eq!(ladder(&rule, Some(Stance::Deny)), Stance::Advise);
assert_eq!(ladder(&rule, Some(Stance::Advise)), Stance::Advise);
assert_eq!(ladder(&rule, Some(Stance::Observe)), Stance::Observe);
assert_eq!(ladder(&rule, None), Stance::Observe);
}
#[test]
fn an_uncapped_rule_still_resolves_as_configured() {
let rule = capped(Stance::Deny);
assert_eq!(ladder(&rule, Some(Stance::Deny)), Stance::Deny);
for r in RULES {
assert!(
r.max_stance >= r.default_stance,
"`{}` ships above its own ceiling",
r.id
);
}
}
#[test]
fn the_key_names_the_rule() {
let r = &RULES[0];
assert_eq!(key_for(r), format!("amont.agent.{}.stance", r.id));
}
#[test]
fn rule_ids_are_unique() {
let mut seen = std::collections::BTreeSet::new();
for r in RULES {
assert!(seen.insert(r.id), "duplicate rule id `{}`", r.id);
}
}
}