Skip to main content

codemod_core/rule/
mod.rs

1//! Codemod rule management — loading, saving, and validation.
2//!
3//! A *rule* is a self-contained description of a code transformation that can
4//! be serialized to YAML and shared across projects. It bundles a before/after
5//! pattern with metadata (name, description, language) and scanning
6//! configuration (include/exclude globs).
7
8pub mod builtin;
9pub mod schema;
10
11pub use builtin::BuiltinRules;
12pub use schema::{CodemodRule, RuleConfig};
13
14use std::path::Path;
15
16use crate::error::CodemodError;
17
18/// Load a codemod rule from a YAML file.
19///
20/// The rule is deserialized and then validated. Invalid rules are rejected
21/// with a [`CodemodError::Rule`].
22///
23/// # Errors
24///
25/// - [`CodemodError::Io`] if the file cannot be read.
26/// - [`CodemodError::Rule`] if the YAML is malformed or the rule fails
27///   validation.
28pub fn load_rule(path: &Path) -> crate::Result<CodemodRule> {
29    let content = std::fs::read_to_string(path).map_err(CodemodError::Io)?;
30    let rule: CodemodRule = serde_yaml::from_str(&content)
31        .map_err(|e| CodemodError::Rule(format!("Failed to parse rule file: {e}")))?;
32    rule.validate()?;
33    Ok(rule)
34}
35
36/// Save a codemod rule to a YAML file.
37///
38/// # Errors
39///
40/// - [`CodemodError::Rule`] if serialization fails.
41/// - [`CodemodError::Io`] if the file cannot be written.
42pub fn save_rule(rule: &CodemodRule, path: &Path) -> crate::Result<()> {
43    let content = serde_yaml::to_string(rule)
44        .map_err(|e| CodemodError::Rule(format!("Failed to serialize rule: {e}")))?;
45    std::fs::write(path, content).map_err(CodemodError::Io)?;
46    Ok(())
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use crate::rule::schema::RulePattern;
53    use std::fs;
54
55    #[test]
56    fn test_save_and_load_rule() {
57        let tmp = std::env::temp_dir().join("codemod_test_rule.yaml");
58
59        let rule = CodemodRule {
60            name: "test-rule".into(),
61            description: "A test rule".into(),
62            language: "rust".into(),
63            version: "1.0".into(),
64            pattern: RulePattern {
65                before: "println!($var1)".into(),
66                after: "log::info!($var1)".into(),
67            },
68            config: RuleConfig::default(),
69        };
70
71        save_rule(&rule, &tmp).unwrap();
72        let loaded = load_rule(&tmp).unwrap();
73
74        assert_eq!(loaded.name, "test-rule");
75        assert_eq!(loaded.pattern.before, "println!($var1)");
76        assert_eq!(loaded.pattern.after, "log::info!($var1)");
77
78        let _ = fs::remove_file(&tmp);
79    }
80}