use crate::config_error::ConfigError;
use crate::config_schema::{PolicyConfig, PolicySetting};
use crate::policy_registry::{
POLICY_REGISTRY, PolicyDescriptor, PolicyId, Severity, policy_by_name, policy_descriptor, severity_name,
};
use crate::policy_trigger::{Trigger, policy_runs_on};
pub fn selected_policies(names: &[String]) -> Result<Vec<PolicyId>, ConfigError> {
let mut selected: Vec<PolicyId> = Vec::<PolicyId>::new();
for name in names {
match policy_by_name(name.as_str()) {
Some(descriptor) => selected.push(descriptor.id),
None => {
let mut known: String = String::new();
for descriptor in POLICY_REGISTRY {
if !known.is_empty() {
known.push_str(", ");
}
known.push_str(descriptor.name);
}
return Err(ConfigError::new(
format!("Unknown policy ID: {name}. Shipped policies: {known}.").as_str(),
));
}
}
}
return Ok(selected);
}
fn off_message(name: &str, explicit: bool, on: &str) -> String {
let cause: String = if explicit {
format!("cli-git.config.jsonc sets policies.\"{name}\" to \"off\"")
} else {
format!("cli-git.config.jsonc does not list \"{name}\", and an unlisted policy of this kind is off")
};
return format!(
"Policy {name} is off, so --policy {name} would check nothing and exit clean: {cause}. \
To turn it on, set \"policies\": {{ \"{name}\": \"{on}\" }} in cli-git.config.jsonc and run the command again."
);
}
fn unrun_message(name: &str, command: &str, runs_for: &[&str]) -> String {
if runs_for.is_empty() {
return format!(
"Policy {name} does not run for the cli-git check or fix commands, only when the Git command it guards is \
forwarded, so --policy {name} would check nothing and exit clean. Run that Git command to see it."
);
}
let mut commands: Vec<String> = Vec::new();
for word in runs_for {
commands.push(format!("cli-git {word}"));
}
return format!(
"Policy {name} does not run for cli-git {command}, so --policy {name} would check nothing and exit clean. \
It runs for {}.",
commands.join(" or ")
);
}
pub fn selection_error(selected: &[PolicyId], policies: &PolicyConfig, trigger: Trigger) -> Option<ConfigError> {
let mut messages: Vec<String> = Vec::new();
let mut seen: Vec<PolicyId> = Vec::new();
for id in selected {
if seen.contains(id) {
continue;
}
seen.push(*id);
let setting: PolicySetting = policies.setting(*id);
let descriptor: &PolicyDescriptor = policy_descriptor(*id);
if setting.severity == Severity::Off {
let on: &str = severity_name(descriptor.default_severity);
messages.push(off_message(descriptor.name, setting.explicit, on));
} else if !policy_runs_on(*id, trigger) {
let mut runs_for: Vec<&str> = Vec::new();
if policy_runs_on(*id, Trigger::DirectCheck) {
runs_for.push("check");
}
if policy_runs_on(*id, Trigger::DirectFix) {
runs_for.push("fix");
}
let command: &str = if trigger == Trigger::DirectFix { "fix" } else { "check" };
messages.push(unrun_message(descriptor.name, command, runs_for.as_slice()));
}
}
if messages.is_empty() {
return None;
}
return Some(ConfigError::new(messages.join(" ").as_str()));
}
#[cfg(test)]
#[path = "management_selection_tests.rs"]
mod tests;