use anyhow::{Result, bail};
use serde_json::{Map, Value, json};
use super::Generated;
use super::settings::{is_linux_spelling, object_at, strip_deny_rules};
pub(super) fn write_deny_rules(
top: &mut Map<String, Value>,
generated: &Generated,
owned: &mut Vec<String>,
) -> Result<()> {
if let Some(Value::Object(permissions)) = top.get_mut("permissions") {
strip_deny_rules(permissions, owned);
}
owned.clear();
if generated.deny_rules.is_empty() {
return Ok(());
}
let deny = object_at(top, "permissions")?
.entry("deny")
.or_insert_with(|| json!([]));
let Value::Array(deny) = deny else {
bail!("`permissions.deny` in the settings file is not an array");
};
for rule in &generated.deny_rules {
if !deny.iter().any(|r| r == rule.as_str()) {
deny.push(json!(rule));
owned.push(rule.clone());
}
}
Ok(())
}
pub(super) fn is_owned_rule(rule: &Value, owned: &[String]) -> bool {
rule.as_str().is_some_and(|r| owned.iter().any(|o| o == r)) || is_linux_spelling(rule)
}
pub(super) fn deny_rules_in_sync(root: &Value, generated: &Generated, owned: &[String]) -> bool {
let deny: Vec<&Value> = root
.pointer("/permissions/deny")
.and_then(Value::as_array)
.into_iter()
.flatten()
.collect();
generated
.deny_rules
.iter()
.all(|rule| deny.iter().any(|r| *r == rule.as_str()))
&& deny
.iter()
.filter(|rule| is_owned_rule(rule, owned))
.all(|rule| generated.deny_rules.iter().any(|g| *rule == g.as_str()))
}
pub fn apply_rules(
root: &mut Value,
generated: &Generated,
owned: &mut Vec<String>,
) -> Result<bool> {
let before = root.clone();
super::remove(root, owned);
let Value::Object(top) = root else {
bail!("the settings file is not a JSON object");
};
write_deny_rules(top, generated, owned)?;
Ok(*root != before)
}
#[cfg(test)]
mod tests;