rust_diff_analyzer/types/scope.rs
1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7
8/// Reason why a file was excluded from analysis
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum ExclusionReason {
11 /// File is not a Rust source file
12 NonRust,
13 /// File matches an ignore pattern
14 IgnorePattern(String),
15}
16
17/// Information about a skipped file
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct SkippedFile {
20 /// Path to the skipped file
21 pub path: PathBuf,
22 /// Reason for exclusion
23 pub reason: ExclusionReason,
24}
25
26impl SkippedFile {
27 /// Creates a new skipped file entry
28 ///
29 /// # Arguments
30 ///
31 /// * `path` - Path to the file
32 /// * `reason` - Reason for exclusion
33 ///
34 /// # Returns
35 ///
36 /// A new SkippedFile instance
37 ///
38 /// # Examples
39 ///
40 /// ```
41 /// use std::path::PathBuf;
42 ///
43 /// use rust_diff_analyzer::types::{ExclusionReason, SkippedFile};
44 ///
45 /// let skipped = SkippedFile::new(PathBuf::from("README.md"), ExclusionReason::NonRust);
46 /// assert_eq!(skipped.path, PathBuf::from("README.md"));
47 /// ```
48 pub fn new(path: PathBuf, reason: ExclusionReason) -> Self {
49 Self { path, reason }
50 }
51}
52
53/// Scope of the analysis showing what was analyzed and what was skipped
54#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
55pub struct AnalysisScope {
56 /// Files that were analyzed
57 pub analyzed_files: Vec<PathBuf>,
58 /// Files that were skipped
59 pub skipped_files: Vec<SkippedFile>,
60 /// Patterns used for exclusion
61 pub exclusion_patterns: Vec<String>,
62}
63
64impl AnalysisScope {
65 /// Creates a new empty analysis scope
66 ///
67 /// # Returns
68 ///
69 /// A new empty AnalysisScope instance
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use rust_diff_analyzer::types::AnalysisScope;
75 ///
76 /// let scope = AnalysisScope::new();
77 /// assert!(scope.analyzed_files.is_empty());
78 /// ```
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 /// Adds an analyzed file to the scope
84 ///
85 /// # Arguments
86 ///
87 /// * `path` - Path to the analyzed file
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use std::path::PathBuf;
93 ///
94 /// use rust_diff_analyzer::types::AnalysisScope;
95 ///
96 /// let mut scope = AnalysisScope::new();
97 /// scope.add_analyzed(PathBuf::from("src/lib.rs"));
98 /// assert_eq!(scope.analyzed_files.len(), 1);
99 /// ```
100 pub fn add_analyzed(&mut self, path: PathBuf) {
101 self.analyzed_files.push(path);
102 }
103
104 /// Adds a skipped file to the scope
105 ///
106 /// # Arguments
107 ///
108 /// * `path` - Path to the skipped file
109 /// * `reason` - Reason for exclusion
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use std::path::PathBuf;
115 ///
116 /// use rust_diff_analyzer::types::{AnalysisScope, ExclusionReason};
117 ///
118 /// let mut scope = AnalysisScope::new();
119 /// scope.add_skipped(
120 /// PathBuf::from("tests/test.rs"),
121 /// ExclusionReason::IgnorePattern("tests/".to_string()),
122 /// );
123 /// assert_eq!(scope.skipped_files.len(), 1);
124 /// ```
125 pub fn add_skipped(&mut self, path: PathBuf, reason: ExclusionReason) {
126 self.skipped_files.push(SkippedFile::new(path, reason));
127 }
128
129 /// Sets the exclusion patterns
130 ///
131 /// # Arguments
132 ///
133 /// * `patterns` - List of exclusion patterns
134 ///
135 /// # Examples
136 ///
137 /// ```
138 /// use rust_diff_analyzer::types::AnalysisScope;
139 ///
140 /// let mut scope = AnalysisScope::new();
141 /// scope.set_patterns(vec!["tests/".to_string(), "benches/".to_string()]);
142 /// assert_eq!(scope.exclusion_patterns.len(), 2);
143 /// ```
144 pub fn set_patterns(&mut self, patterns: Vec<String>) {
145 self.exclusion_patterns = patterns;
146 }
147
148 /// Returns count of non-Rust files skipped
149 ///
150 /// # Returns
151 ///
152 /// Number of non-Rust files
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// use std::path::PathBuf;
158 ///
159 /// use rust_diff_analyzer::types::{AnalysisScope, ExclusionReason};
160 ///
161 /// let mut scope = AnalysisScope::new();
162 /// scope.add_skipped(PathBuf::from("README.md"), ExclusionReason::NonRust);
163 /// scope.add_skipped(PathBuf::from("Cargo.toml"), ExclusionReason::NonRust);
164 /// assert_eq!(scope.non_rust_count(), 2);
165 /// ```
166 pub fn non_rust_count(&self) -> usize {
167 self.skipped_files
168 .iter()
169 .filter(|f| matches!(f.reason, ExclusionReason::NonRust))
170 .count()
171 }
172
173 /// Returns count of files skipped due to ignore patterns
174 ///
175 /// # Returns
176 ///
177 /// Number of ignored files
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// use std::path::PathBuf;
183 ///
184 /// use rust_diff_analyzer::types::{AnalysisScope, ExclusionReason};
185 ///
186 /// let mut scope = AnalysisScope::new();
187 /// scope.add_skipped(
188 /// PathBuf::from("tests/test.rs"),
189 /// ExclusionReason::IgnorePattern("tests/".to_string()),
190 /// );
191 /// assert_eq!(scope.ignored_count(), 1);
192 /// ```
193 pub fn ignored_count(&self) -> usize {
194 self.skipped_files
195 .iter()
196 .filter(|f| matches!(f.reason, ExclusionReason::IgnorePattern(_)))
197 .count()
198 }
199}