fuse_rule/
cli.rs

1use crate::config::FuseRuleConfig;
2use crate::evaluator::{DataFusionEvaluator, RuleEvaluator};
3use crate::rule::Rule;
4use crate::RuleEngine;
5use anyhow::Result;
6use clap::Parser;
7
8#[derive(Parser)]
9pub struct ValidateArgs {
10    /// Path to configuration file
11    #[arg(short, long)]
12    config: String,
13
14    /// Rule predicate to validate
15    #[arg(short, long)]
16    predicate: Option<String>,
17}
18
19pub async fn validate_rule(config_path: &str, predicate: Option<&str>) -> Result<()> {
20    let config = FuseRuleConfig::from_file(config_path)?;
21
22    if let Some(pred) = predicate {
23        // Validate specific predicate
24        let evaluator = DataFusionEvaluator::new();
25        let schema = RuleEngine::from_config(config.clone())
26            .await
27            .map(|e| e.schema())?;
28
29        let test_rule = Rule {
30            id: "test".to_string(),
31            name: "Test Rule".to_string(),
32            predicate: pred.to_string(),
33            action: "logger".to_string(),
34            window_seconds: None,
35            version: 1,
36            enabled: true,
37        };
38
39        match evaluator.compile(test_rule, &schema) {
40            Ok(_) => {
41                println!("✅ Predicate is valid");
42                Ok(())
43            }
44            Err(e) => {
45                eprintln!("❌ Predicate validation failed: {}", e);
46                Err(e)
47            }
48        }
49    } else {
50        // Validate all rules in config
51        let evaluator = DataFusionEvaluator::new();
52        let engine = RuleEngine::from_config(config.clone()).await?;
53        let schema = engine.schema();
54
55        let mut all_valid = true;
56        for rule_cfg in &config.rules {
57            let rule = Rule {
58                id: rule_cfg.id.clone(),
59                name: rule_cfg.name.clone(),
60                predicate: rule_cfg.predicate.clone(),
61                action: rule_cfg.action.clone(),
62                window_seconds: rule_cfg.window_seconds,
63                version: rule_cfg.version,
64                enabled: rule_cfg.enabled,
65            };
66
67            match evaluator.compile(rule.clone(), &schema) {
68                Ok(compiled) => {
69                    println!("✅ Rule '{}' ({}): Valid", rule.name, rule.id);
70                    if compiled.has_aggregates {
71                        println!("   └─ Contains aggregate functions");
72                    }
73                }
74                Err(e) => {
75                    eprintln!("❌ Rule '{}' ({}): Invalid - {}", rule.name, rule.id, e);
76                    all_valid = false;
77                }
78            }
79        }
80
81        if all_valid {
82            println!("\n✅ All rules are valid");
83            Ok(())
84        } else {
85            anyhow::bail!("Some rules failed validation");
86        }
87    }
88}