use crate::config::definition::{AuditStrategy, LineAction, LineRule, RegexTarget};
use crate::diff::entry::DiffType;
#[derive(Debug, Clone)]
pub struct RuleTemplate {
pub id: &'static str,
pub name: &'static str,
pub name_ja: &'static str,
pub description: &'static str,
pub diff_type: DiffType,
pub strategy: fn() -> AuditStrategy,
}
pub static TEMPLATES: &[RuleTemplate] = &[
RuleTemplate {
id: "version_bump",
name: "Version number update",
name_ja: "バージョン番号の更新",
description: "A semver-like version string changed in a modified file (Regex).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::Regex {
pattern: r"^\d+\.\d+\.\d+".to_string(),
target: RegexTarget::AddedLines,
},
},
RuleTemplate {
id: "port_change",
name: "Port number change",
name_ja: "ポート番号の変更",
description: "A `port = N` line changed to `port = M` (LineMatch template — fill in actual values).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::LineMatch {
rules: vec![
LineRule { action: LineAction::Removed, line: "port = ".to_string() },
LineRule { action: LineAction::Added, line: "port = ".to_string() },
],
},
},
RuleTemplate {
id: "file_added_any",
name: "File added (content not inspected)",
name_ja: "ファイルの追加(内容確認なし)",
description: "A new file was intentionally added; no content check required.",
diff_type: DiffType::Added,
strategy: || AuditStrategy::None,
},
RuleTemplate {
id: "file_removed_any",
name: "File removed (content not inspected)",
name_ja: "ファイルの削除(内容確認なし)",
description: "A file was intentionally deleted; no content check required.",
diff_type: DiffType::Removed,
strategy: || AuditStrategy::None,
},
RuleTemplate {
id: "config_line_change",
name: "Config key=value change",
name_ja: "設定値の変更",
description: "A `key = value` pair changed (LineMatch template — fill in key and values).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::LineMatch {
rules: vec![
LineRule { action: LineAction::Removed, line: "key = old_value".to_string() },
LineRule { action: LineAction::Added, line: "key = new_value".to_string() },
],
},
},
RuleTemplate {
id: "date_string_change",
name: "Date string update",
name_ja: "日付文字列の更新",
description: "A date like 2025-01-15 changed; validates ISO-8601 format (Regex).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::Regex {
pattern: r"^\d{4}-\d{2}-\d{2}".to_string(),
target: RegexTarget::AddedLines,
},
},
RuleTemplate {
id: "exact_binary",
name: "Binary / generated file (checksum)",
name_ja: "バイナリ・生成ファイル(チェックサム)",
description: "Verify a binary or generated file by its SHA-256 digest (fill in hash).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::Checksum {
expected_sha256: String::new(),
},
},
RuleTemplate {
id: "feature_flag_toggle",
name: "Feature flag toggle",
name_ja: "フィーチャーフラグの切り替え",
description: "A boolean flag changed from false to true or vice versa (Regex).",
diff_type: DiffType::Modified,
strategy: || AuditStrategy::Regex {
pattern: r"^(true|false)$".to_string(),
target: RegexTarget::AddedLines,
},
},
];
pub fn find(id: &str) -> Option<&'static RuleTemplate> {
TEMPLATES.iter().find(|t| t.id == id)
}
#[cfg(test)]
mod tests;