use crate::core::types::{PolicyRule, PolicyRuleType, Resource};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetSpec {
pub field: String,
pub value: String,
}
type FieldSetter = fn(&mut Resource, &str);
const SETTABLE: &[(&str, FieldSetter)] = &[
("mode", |r, v| r.mode = Some(v.to_string())),
("owner", |r, v| r.owner = Some(v.to_string())),
("group", |r, v| r.group = Some(v.to_string())),
("state", |r, v| r.state = Some(v.to_string())),
("provider", |r, v| r.provider = Some(v.to_string())),
];
pub fn derive(rule: &PolicyRule) -> Result<TargetSpec, String> {
match rule.rule_type {
PolicyRuleType::Assert => derive_assert(rule),
PolicyRuleType::Deny | PolicyRuleType::Warn => Err(
"a deny/warn rule names a value that is FORBIDDEN, not one that is required — \
there is no target to write, and forjar will not invent one"
.to_string(),
),
PolicyRuleType::Require => Err(
"a require rule names a field that must be set, not the value to set it to".to_string(),
),
PolicyRuleType::Limit => {
Err("a limit rule bounds the size of a list; there is no scalar to set".to_string())
}
}
}
fn derive_assert(rule: &PolicyRule) -> Result<TargetSpec, String> {
let field = rule
.condition_field
.as_deref()
.ok_or("an assert rule with no condition_field names no field to set")?;
let value = rule
.condition_value
.as_deref()
.ok_or("an assert rule with no condition_value names no value to write")?;
if !is_settable(field) {
return Err(format!(
"`{field}` is not one of the scalar fields forjar will rewrite ({})",
settable_fields().join(", ")
));
}
Ok(TargetSpec {
field: field.to_string(),
value: value.to_string(),
})
}
pub fn is_settable(field: &str) -> bool {
SETTABLE.iter().any(|(name, _)| *name == field)
}
pub fn settable_fields() -> Vec<&'static str> {
SETTABLE.iter().map(|(name, _)| *name).collect()
}
pub fn set_field(resource: &mut Resource, field: &str, value: &str) -> bool {
match SETTABLE.iter().find(|(name, _)| *name == field) {
Some((_, set)) => {
set(resource, value);
true
}
None => false,
}
}