rust_diff_analyzer/classifier/
rules.rs1use crate::{
5 config::Config,
6 types::{Change, SemanticUnit, SemanticUnitKind},
7};
8
9pub fn calculate_weight(unit: &SemanticUnit, config: &Config) -> usize {
42 let weights = &config.weights;
43
44 match unit.kind {
45 SemanticUnitKind::Function => {
46 if unit.visibility.is_public() {
47 weights.public_function
48 } else {
49 weights.private_function
50 }
51 }
52 SemanticUnitKind::Struct | SemanticUnitKind::Enum => {
53 if unit.visibility.is_public() {
54 weights.public_struct
55 } else {
56 weights.private_struct
57 }
58 }
59 SemanticUnitKind::Impl => weights.impl_block,
60 SemanticUnitKind::Trait => weights.trait_definition,
61 SemanticUnitKind::Const | SemanticUnitKind::Static => weights.const_static,
62 SemanticUnitKind::TypeAlias => weights.const_static,
63 SemanticUnitKind::Macro => weights.private_function,
64 SemanticUnitKind::Module => weights.const_static,
65 }
66}
67
68pub fn exceeded_per_type_limits(
89 changes: &[Change],
90 config: &Config,
91) -> Vec<(&'static str, usize, usize)> {
92 let per_type = match &config.limits.per_type {
93 Some(limits) => limits,
94 None => return Vec::new(),
95 };
96
97 let kinds = [
98 SemanticUnitKind::Function,
99 SemanticUnitKind::Struct,
100 SemanticUnitKind::Enum,
101 SemanticUnitKind::Trait,
102 SemanticUnitKind::Impl,
103 SemanticUnitKind::Const,
104 SemanticUnitKind::Static,
105 SemanticUnitKind::TypeAlias,
106 SemanticUnitKind::Macro,
107 SemanticUnitKind::Module,
108 ];
109 let limits = [
110 per_type.functions,
111 per_type.structs,
112 per_type.enums,
113 per_type.traits,
114 per_type.impl_blocks,
115 per_type.consts,
116 per_type.statics,
117 per_type.type_aliases,
118 per_type.macros,
119 per_type.modules,
120 ];
121
122 let mut counts = [0usize; 10];
123 for change in changes {
124 if !change.classification.is_production() {
125 continue;
126 }
127 if let Some(index) = kinds.iter().position(|k| *k == change.unit.kind) {
128 counts[index] += 1;
129 }
130 }
131
132 kinds
133 .iter()
134 .zip(limits)
135 .zip(counts)
136 .filter_map(|((kind, limit), count)| {
137 limit.and_then(|limit| (count > limit).then_some((kind.as_str(), count, limit)))
138 })
139 .collect()
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::types::{LineSpan, Visibility};
146
147 #[test]
148 fn test_weight_calculation() {
149 let config = Config::default();
150
151 let pub_fn = SemanticUnit::new(
152 SemanticUnitKind::Function,
153 "public".to_string(),
154 Visibility::Public,
155 LineSpan::new(1, 10),
156 vec![],
157 );
158 assert_eq!(calculate_weight(&pub_fn, &config), 3);
159
160 let priv_fn = SemanticUnit::new(
161 SemanticUnitKind::Function,
162 "private".to_string(),
163 Visibility::Private,
164 LineSpan::new(1, 10),
165 vec![],
166 );
167 assert_eq!(calculate_weight(&priv_fn, &config), 1);
168
169 let trait_def = SemanticUnit::new(
170 SemanticUnitKind::Trait,
171 "MyTrait".to_string(),
172 Visibility::Public,
173 LineSpan::new(1, 10),
174 vec![],
175 );
176 assert_eq!(calculate_weight(&trait_def, &config), 4);
177 }
178
179 #[test]
180 fn test_exceeded_per_type_limits() {
181 use std::path::PathBuf;
182
183 use crate::{
184 config::PerTypeLimits,
185 types::{Change, CodeType},
186 };
187
188 let make_change = |kind: SemanticUnitKind| {
189 Change::new(
190 PathBuf::from("src/lib.rs"),
191 SemanticUnit::new(
192 kind,
193 "unit".to_string(),
194 Visibility::Public,
195 LineSpan::new(1, 5),
196 vec![],
197 ),
198 CodeType::Production,
199 3,
200 0,
201 )
202 };
203
204 let mut config = Config::default();
205 config.limits.per_type = Some(PerTypeLimits {
206 functions: Some(1),
207 ..PerTypeLimits::default()
208 });
209
210 let changes = vec![
211 make_change(SemanticUnitKind::Function),
212 make_change(SemanticUnitKind::Function),
213 make_change(SemanticUnitKind::Struct),
214 ];
215
216 let exceeded = exceeded_per_type_limits(&changes, &config);
217 assert_eq!(exceeded, vec![("function", 2, 1)]);
218
219 config.limits.per_type = None;
220 assert!(exceeded_per_type_limits(&changes, &config).is_empty());
221 }
222}