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