use regex::Regex;
pub struct Rule {
pub id: &'static str,
#[allow(dead_code)]
pub description: &'static str,
pub extensions: &'static [&'static str],
pub pattern: &'static str,
}
pub const RULES: &[Rule] = &[
Rule {
id: "typescript-no-any",
description: "Detect explicit `any` usage in TypeScript.",
extensions: &["ts", "tsx"],
pattern: r"(?P<a>:\s*any\b)|(?P<b><any>)|(?P<c>\bany\[\])|(?P<d>Array<any>)",
},
Rule {
id: "python-missing-typing",
description: "Detect Python `def` declarations without type annotations.",
extensions: &["py"],
pattern: r"^\s*def\s+\w+\(([^):]*)\)\s*:\s*$",
},
Rule {
id: "django-fbv",
description: "Detect Django function-based views taking `request` as first arg.",
extensions: &["py"],
pattern: r"^\s*def\s+\w+\(\s*request\s*[,)]",
},
];
pub fn find_rule(id: &str) -> Option<&'static Rule> {
RULES.iter().find(|r| r.id == id)
}
pub fn compile(rule: &Rule) -> Regex {
Regex::new(&format!("(?m){}", rule.pattern)).expect("built-in rule regex must compile")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn typescript_no_any_matches_colon_any() {
let re = compile(find_rule("typescript-no-any").unwrap());
assert!(re.is_match("const x: any = 1;"));
assert!(re.is_match("function f<T>(x: any) {}"));
assert!(re.is_match("let y: any[] = [];"));
assert!(re.is_match("let z: Array<any> = [];"));
}
#[test]
fn typescript_no_any_skips_clean_code() {
let re = compile(find_rule("typescript-no-any").unwrap());
assert!(!re.is_match("const x: number = 1;"));
assert!(!re.is_match("const company = 'jimolab';")); }
#[test]
fn python_missing_typing_matches_untyped_def() {
let re = compile(find_rule("python-missing-typing").unwrap());
assert!(re.is_match("def foo(x, y):"));
assert!(re.is_match(" def bar():"));
}
#[test]
fn python_missing_typing_skips_annotated_def() {
let re = compile(find_rule("python-missing-typing").unwrap());
assert!(!re.is_match("def foo(x: int) -> int:"));
assert!(!re.is_match("def bar() -> None:"));
}
#[test]
fn django_fbv_matches_request_arg() {
let re = compile(find_rule("django-fbv").unwrap());
assert!(re.is_match("def home(request):"));
assert!(re.is_match("def detail(request, pk):"));
}
#[test]
fn django_fbv_skips_cbv_methods() {
let re = compile(find_rule("django-fbv").unwrap());
assert!(!re.is_match("def get(self, request, *args, **kwargs):"));
}
#[test]
fn find_rule_returns_none_for_unknown_id() {
assert!(find_rule("not-a-rule").is_none());
}
}