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