aaai_core/templates/
library.rs1use crate::config::definition::{AuditStrategy, LineAction, LineRule, RegexTarget};
8use crate::diff::entry::DiffType;
9
10#[derive(Debug, Clone)]
12pub struct RuleTemplate {
13 pub id: &'static str,
14 pub name: &'static str,
15 pub name_ja: &'static str,
16 pub description: &'static str,
17 pub diff_type: DiffType,
18 pub strategy: fn() -> AuditStrategy,
20}
21
22pub static TEMPLATES: &[RuleTemplate] = &[
24 RuleTemplate {
25 id: "version_bump",
26 name: "Version number update",
27 name_ja: "バージョン番号の更新",
28 description: "A semver-like version string changed in a modified file (Regex).",
29 diff_type: DiffType::Modified,
30 strategy: || AuditStrategy::Regex {
31 pattern: r"^\d+\.\d+\.\d+".to_string(),
32 target: RegexTarget::AddedLines,
33 },
34 },
35 RuleTemplate {
36 id: "port_change",
37 name: "Port number change",
38 name_ja: "ポート番号の変更",
39 description: "A `port = N` line changed to `port = M` (LineMatch template — fill in actual values).",
40 diff_type: DiffType::Modified,
41 strategy: || AuditStrategy::LineMatch {
42 rules: vec![
43 LineRule { action: LineAction::Removed, line: "port = ".to_string() },
44 LineRule { action: LineAction::Added, line: "port = ".to_string() },
45 ],
46 },
47 },
48 RuleTemplate {
49 id: "file_added_any",
50 name: "File added (content not inspected)",
51 name_ja: "ファイルの追加(内容確認なし)",
52 description: "A new file was intentionally added; no content check required.",
53 diff_type: DiffType::Added,
54 strategy: || AuditStrategy::None,
55 },
56 RuleTemplate {
57 id: "file_removed_any",
58 name: "File removed (content not inspected)",
59 name_ja: "ファイルの削除(内容確認なし)",
60 description: "A file was intentionally deleted; no content check required.",
61 diff_type: DiffType::Removed,
62 strategy: || AuditStrategy::None,
63 },
64 RuleTemplate {
65 id: "config_line_change",
66 name: "Config key=value change",
67 name_ja: "設定値の変更",
68 description: "A `key = value` pair changed (LineMatch template — fill in key and values).",
69 diff_type: DiffType::Modified,
70 strategy: || AuditStrategy::LineMatch {
71 rules: vec![
72 LineRule { action: LineAction::Removed, line: "key = old_value".to_string() },
73 LineRule { action: LineAction::Added, line: "key = new_value".to_string() },
74 ],
75 },
76 },
77 RuleTemplate {
78 id: "date_string_change",
79 name: "Date string update",
80 name_ja: "日付文字列の更新",
81 description: "A date like 2025-01-15 changed; validates ISO-8601 format (Regex).",
82 diff_type: DiffType::Modified,
83 strategy: || AuditStrategy::Regex {
84 pattern: r"^\d{4}-\d{2}-\d{2}".to_string(),
85 target: RegexTarget::AddedLines,
86 },
87 },
88 RuleTemplate {
89 id: "exact_binary",
90 name: "Binary / generated file (checksum)",
91 name_ja: "バイナリ・生成ファイル(チェックサム)",
92 description: "Verify a binary or generated file by its SHA-256 digest (fill in hash).",
93 diff_type: DiffType::Modified,
94 strategy: || AuditStrategy::Checksum {
95 expected_sha256: String::new(),
96 },
97 },
98 RuleTemplate {
99 id: "feature_flag_toggle",
100 name: "Feature flag toggle",
101 name_ja: "フィーチャーフラグの切り替え",
102 description: "A boolean flag changed from false to true or vice versa (Regex).",
103 diff_type: DiffType::Modified,
104 strategy: || AuditStrategy::Regex {
105 pattern: r"^(true|false)$".to_string(),
106 target: RegexTarget::AddedLines,
107 },
108 },
109];
110
111pub fn find(id: &str) -> Option<&'static RuleTemplate> {
113 TEMPLATES.iter().find(|t| t.id == id)
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn all_templates_produce_valid_strategies() {
122 for tmpl in TEMPLATES {
123 let strat = (tmpl.strategy)();
124 let _ = strat.label();
127 }
128 }
129
130 #[test]
131 fn find_by_id_works() {
132 assert!(find("version_bump").is_some());
133 assert!(find("nonexistent").is_none());
134 }
135
136 #[test]
137 fn no_duplicate_ids() {
138 let mut ids: Vec<&str> = TEMPLATES.iter().map(|t| t.id).collect();
139 let original_len = ids.len();
140 ids.dedup();
141 assert_eq!(ids.len(), original_len, "duplicate template IDs found");
142 }
143}