codemod_core/rule/
builtin.rs1use super::schema::{CodemodRule, RuleConfig, RulePattern};
8
9pub struct BuiltinRules;
11
12impl BuiltinRules {
13 pub fn all() -> Vec<CodemodRule> {
15 vec![
16 Self::replace_println_with_log(),
17 Self::replace_unwrap_with_expect(),
18 Self::replace_deprecated_trim(),
19 ]
20 }
21
22 pub fn get(name: &str) -> Option<CodemodRule> {
24 Self::all().into_iter().find(|r| r.name == name)
25 }
26
27 pub fn names() -> Vec<&'static str> {
29 vec![
30 "replace-println-with-log",
31 "replace-unwrap-with-expect",
32 "replace-deprecated-trim",
33 ]
34 }
35
36 pub fn replace_println_with_log() -> CodemodRule {
50 CodemodRule {
51 name: "replace-println-with-log".into(),
52 description: "Replace println!() calls with log::info!() for structured logging".into(),
53 language: "rust".into(),
54 version: "1.0".into(),
55 pattern: RulePattern {
56 before: "println!($args)".into(),
57 after: "log::info!($args)".into(),
58 },
59 config: RuleConfig {
60 include: vec!["**/*.rs".into()],
61 exclude: vec!["tests/**".into(), "examples/**".into()],
62 respect_gitignore: true,
63 max_file_size: Some(1_000_000),
64 },
65 }
66 }
67
68 pub fn replace_unwrap_with_expect() -> CodemodRule {
78 CodemodRule {
79 name: "replace-unwrap-with-expect".into(),
80 description:
81 "Replace .unwrap() with .expect(\"...\") to encourage better error messages".into(),
82 language: "rust".into(),
83 version: "1.0".into(),
84 pattern: RulePattern {
85 before: "$expr.unwrap()".into(),
86 after: "$expr.expect(\"TODO: add error context\")".into(),
87 },
88 config: RuleConfig {
89 include: vec!["**/*.rs".into()],
90 exclude: vec!["tests/**".into()],
91 respect_gitignore: true,
92 max_file_size: Some(1_000_000),
93 },
94 }
95 }
96
97 pub fn replace_deprecated_trim() -> CodemodRule {
105 CodemodRule {
106 name: "replace-deprecated-trim".into(),
107 description: "Replace deprecated trim_left()/trim_right() with trim_start()/trim_end()"
108 .into(),
109 language: "rust".into(),
110 version: "1.0".into(),
111 pattern: RulePattern {
112 before: "$s.trim_left()".into(),
113 after: "$s.trim_start()".into(),
114 },
115 config: RuleConfig {
116 include: vec!["**/*.rs".into()],
117 exclude: vec![],
118 respect_gitignore: true,
119 max_file_size: None,
120 },
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_all_rules_valid() {
131 for rule in BuiltinRules::all() {
132 rule.validate()
133 .unwrap_or_else(|e| panic!("Built-in rule '{}' failed validation: {e}", rule.name));
134 }
135 }
136
137 #[test]
138 fn test_get_existing_rule() {
139 let rule = BuiltinRules::get("replace-println-with-log");
140 assert!(rule.is_some());
141 assert_eq!(rule.unwrap().language, "rust");
142 }
143
144 #[test]
145 fn test_get_nonexistent_rule() {
146 assert!(BuiltinRules::get("does-not-exist").is_none());
147 }
148
149 #[test]
150 fn test_names_match_rules() {
151 let names = BuiltinRules::names();
152 let rules = BuiltinRules::all();
153 assert_eq!(names.len(), rules.len());
154 for (name, rule) in names.iter().zip(rules.iter()) {
155 assert_eq!(*name, rule.name);
156 }
157 }
158}