evorule_cli/commands/
validate.rs1use std::path::Path;
27
28use evorule_tcb::META_INSTRUCTION_TYPES;
29
30use crate::error::CliError;
31use crate::io_util;
32
33pub fn run(rules_dir: &Path) -> Result<(), CliError> {
39 let transforms = io_util::load_rules(rules_dir)?;
40
41 let mut total_errors = 0;
42
43 println!("=== Validating {} ===", rules_dir.display());
44 println!("Transforms: {}", transforms.len());
45 println!();
46
47 for (i, t) in transforms.iter().enumerate() {
48 let type_str = t.get("type").and_then(|v| v.as_str());
49 match type_str {
50 Some(ts) if META_INSTRUCTION_TYPES.contains(&ts) => {
51 println!("[OK] transform[{}]: type='{}'", i, ts);
52 }
53 Some(ts) => {
54 println!(
55 "[ERROR] transform[{}]: unknown type '{}' (not in core_eval meta-instruction whitelist)",
56 i, ts
57 );
58 total_errors += 1;
59 }
60 None => {
61 println!("[ERROR] transform[{}]: missing 'type' field", i);
62 total_errors += 1;
63 }
64 }
65 }
66
67 println!();
68 println!("=== Summary ===");
69 println!("Errors: {}", total_errors);
70
71 if total_errors > 0 {
72 Err(CliError::other(format!(
73 "validation failed with {} errors",
74 total_errors
75 )))
76 } else {
77 Ok(())
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 #![allow(clippy::unwrap_used)]
84 use super::*;
85
86 #[test]
87 fn test_whitelist_excludes_g8_words() {
88 for t in META_INSTRUCTION_TYPES {
91 assert!(
92 !matches!(*t, "conditional" | "while_loop" | "sequence"),
93 "whitelist must not contain G8-forbidden words: {}",
94 t
95 );
96 }
97 }
98
99 #[test]
100 fn test_whitelist_includes_core_meta_instructions() {
101 assert!(META_INSTRUCTION_TYPES.contains(&"branch"));
103 assert!(META_INSTRUCTION_TYPES.contains(&"set"));
104 assert!(META_INSTRUCTION_TYPES.contains(&"push"));
105 assert!(META_INSTRUCTION_TYPES.contains(&"io_request"));
106 assert!(META_INSTRUCTION_TYPES.contains(&"enforce"));
107 assert!(!META_INSTRUCTION_TYPES.contains(&"collect"));
109 assert!(!META_INSTRUCTION_TYPES.contains(&"merge"));
110 assert!(!META_INSTRUCTION_TYPES.contains(&"noop"));
112 assert!(!META_INSTRUCTION_TYPES.contains(&"increment"));
113 assert!(!META_INSTRUCTION_TYPES.contains(&"decrement"));
114 }
115}