use serde::{Deserialize, Serialize};
const BLOCKED_TAGS: &[&str] = &["explicit", "sexual", "nudity", "adult"];
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PolicyProfile {
Strict,
Standard,
}
#[derive(Debug, Clone)]
pub struct Policy {
pub profile: PolicyProfile,
pub allowlist: Vec<String>,
}
impl Policy {
pub fn new(profile: PolicyProfile) -> Self {
Policy {
profile,
allowlist: Vec::new(),
}
}
pub fn with_allowlist(profile: PolicyProfile, allowlist: Vec<String>) -> Self {
Policy { profile, allowlist }
}
pub fn is_target_allowed(&self, name: &str, tags: &[&str]) -> bool {
for tag in tags {
let tag_lower = tag.to_lowercase();
for blocked in BLOCKED_TAGS {
if tag_lower.contains(blocked) {
return false;
}
}
}
let name_lower = name.to_lowercase();
for blocked in BLOCKED_TAGS {
if name_lower.contains(blocked) {
return false;
}
}
match self.profile {
PolicyProfile::Standard => true,
PolicyProfile::Strict => self.allowlist.iter().any(|a| a == name),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard_allows_normal_targets() {
let p = Policy::new(PolicyProfile::Standard);
assert!(p.is_target_allowed("height", &["body", "shape"]));
assert!(p.is_target_allowed("weight", &[]));
}
#[test]
fn blocks_explicit_tags() {
let p = Policy::new(PolicyProfile::Standard);
assert!(!p.is_target_allowed("test", &["explicit"]));
assert!(!p.is_target_allowed("test", &["sexual-content"]));
assert!(!p.is_target_allowed("explicit-body", &[]));
}
#[test]
fn strict_blocks_unlisted() {
let p = Policy::with_allowlist(
PolicyProfile::Strict,
vec!["height".to_string(), "weight".to_string()],
);
assert!(p.is_target_allowed("height", &[]));
assert!(!p.is_target_allowed("muscle", &[]));
}
}