use std::path::Path;
use crate::error::CliError;
use crate::io_util;
const VALID_TRANSFORM_TYPES: &[&str] = &[
"branch",
"set",
"push",
"io_request",
"collect",
"merge",
];
pub fn run(rules_dir: &Path) -> Result<(), CliError> {
let transforms = io_util::load_rules(rules_dir)?;
let mut total_errors = 0;
println!("=== Validating {} ===", rules_dir.display());
println!("Transforms: {}", transforms.len());
println!();
for (i, t) in transforms.iter().enumerate() {
let type_str = t.get("type").and_then(|v| v.as_str());
match type_str {
Some(ts) if VALID_TRANSFORM_TYPES.contains(&ts) => {
println!("[OK] transform[{}]: type='{}'", i, ts);
}
Some(ts) => {
println!(
"[ERROR] transform[{}]: unknown type '{}' (not in core_eval meta-instruction whitelist)",
i, ts
);
total_errors += 1;
}
None => {
println!("[ERROR] transform[{}]: missing 'type' field", i);
total_errors += 1;
}
}
}
println!();
println!("=== Summary ===");
println!("Errors: {}", total_errors);
if total_errors > 0 {
Err(CliError::other(format!(
"validation failed with {} errors",
total_errors
)))
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn test_whitelist_excludes_g8_words() {
for t in VALID_TRANSFORM_TYPES {
assert!(
!matches!(*t, "conditional" | "while_loop" | "sequence"),
"whitelist must not contain G8-forbidden words: {}",
t
);
}
}
#[test]
fn test_whitelist_includes_core_meta_instructions() {
assert!(VALID_TRANSFORM_TYPES.contains(&"branch"));
assert!(VALID_TRANSFORM_TYPES.contains(&"set"));
assert!(VALID_TRANSFORM_TYPES.contains(&"push"));
assert!(VALID_TRANSFORM_TYPES.contains(&"io_request"));
assert!(VALID_TRANSFORM_TYPES.contains(&"collect"));
assert!(VALID_TRANSFORM_TYPES.contains(&"merge"));
assert!(!VALID_TRANSFORM_TYPES.contains(&"noop"));
assert!(!VALID_TRANSFORM_TYPES.contains(&"increment"));
assert!(!VALID_TRANSFORM_TYPES.contains(&"decrement"));
}
}