use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Policy {
#[serde(default)]
pub allowed_paths: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<u64>,
#[serde(default)]
pub enforce_paths: bool,
}
impl Policy {
pub fn permits(&self, touched: &[String]) -> bool {
if !self.enforce_paths || self.allowed_paths.is_empty() {
return true;
}
touched
.iter()
.all(|p| self.allowed_paths.iter().any(|a| p.starts_with(a)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_unenforced_policy_permits_everything() {
let policy = Policy::default();
assert!(policy.permits(&["anywhere/at/all.rs".to_string()]));
}
#[test]
fn enforcement_confines_writes_to_declared_paths() {
let policy = Policy {
allowed_paths: vec!["crates/".to_string()],
enforce_paths: true,
..Policy::default()
};
assert!(policy.permits(&["crates/ostraka-core/src/lib.rs".to_string()]));
assert!(!policy.permits(&[".github/workflows/release.yml".to_string()]));
}
}