omni_dev/data/
scopes_lint.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct DeadPattern {
8 pub scope: String,
10 pub pattern: String,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ScopesLintReport {
17 pub roots: Vec<String>,
19 pub project_only: bool,
21 pub scopes_checked: usize,
23 pub files_checked: usize,
25 pub dead_patterns: Vec<DeadPattern>,
27 pub unscoped_files: Vec<String>,
29}
30
31impl ScopesLintReport {
32 #[must_use]
34 pub fn has_violations(&self) -> bool {
35 !self.dead_patterns.is_empty() || !self.unscoped_files.is_empty()
36 }
37
38 #[must_use]
44 pub fn exit_code(&self) -> i32 {
45 i32::from(self.has_violations())
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 fn clean_report() -> ScopesLintReport {
54 ScopesLintReport {
55 roots: vec!["src".to_string()],
56 project_only: true,
57 scopes_checked: 1,
58 files_checked: 1,
59 dead_patterns: Vec::new(),
60 unscoped_files: Vec::new(),
61 }
62 }
63
64 #[test]
65 fn clean_report_has_no_violations() {
66 let report = clean_report();
67 assert!(!report.has_violations());
68 assert_eq!(report.exit_code(), 0);
69 }
70
71 #[test]
72 fn dead_pattern_only_is_a_violation() {
73 let mut report = clean_report();
74 report.dead_patterns.push(DeadPattern {
75 scope: "cli".to_string(),
76 pattern: "src/nonexistent/**".to_string(),
77 });
78 assert!(report.has_violations());
79 assert_eq!(report.exit_code(), 1);
80 }
81
82 #[test]
83 fn unscoped_file_only_is_a_violation() {
84 let mut report = clean_report();
85 report.unscoped_files.push("src/worktrees.rs".to_string());
86 assert!(report.has_violations());
87 assert_eq!(report.exit_code(), 1);
88 }
89}