11//! Core domain models for code quality violations and validation results
2- //!
2+ //!
33//! CDD Principle: Rich Domain Models - Violations are entities with behavior, not just data
44//! - Violations can classify themselves, suggest fixes, and maintain context
55//! - ValidationReport acts as an aggregate root managing collections of violations
66//! - Domain events can be generated when patterns are detected or when validation completes
77
8+ use chrono:: { DateTime , Utc } ;
89use serde:: { Deserialize , Serialize } ;
910use std:: path:: PathBuf ;
10- use chrono:: { DateTime , Utc } ;
1111
1212/// Severity levels for code quality violations
1313#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Serialize , Deserialize , Hash ) ]
@@ -26,12 +26,12 @@ impl Severity {
2626 pub fn is_blocking ( self ) -> bool {
2727 matches ! ( self , Self :: Error )
2828 }
29-
29+
3030 /// Convert to string for display
3131 pub fn as_str ( self ) -> & ' static str {
3232 match self {
3333 Self :: Info => "info" ,
34- Self :: Warning => "warning" ,
34+ Self :: Warning => "warning" ,
3535 Self :: Error => "error" ,
3636 }
3737 }
@@ -80,39 +80,39 @@ impl Violation {
8080 detected_at : Utc :: now ( ) ,
8181 }
8282 }
83-
83+
8484 /// Set line and column position
8585 pub fn with_position ( mut self , line : u32 , column : u32 ) -> Self {
8686 self . line_number = Some ( line) ;
8787 self . column_number = Some ( column) ;
8888 self
8989 }
90-
90+
9191 /// Add source code context
9292 pub fn with_context ( mut self , context : impl Into < String > ) -> Self {
9393 self . context = Some ( context. into ( ) ) ;
9494 self
9595 }
96-
96+
9797 /// Add a suggested fix
9898 pub fn with_suggestion ( mut self , suggestion : impl Into < String > ) -> Self {
9999 self . suggested_fix = Some ( suggestion. into ( ) ) ;
100100 self
101101 }
102-
102+
103103 /// Whether this violation is blocking (prevents commits/builds)
104104 pub fn is_blocking ( & self ) -> bool {
105105 self . severity . is_blocking ( )
106106 }
107-
107+
108108 /// Format violation for display
109109 pub fn format_display ( & self ) -> String {
110110 let location = match ( self . line_number , self . column_number ) {
111111 ( Some ( line) , Some ( col) ) => format ! ( ":{}:{}" , line, col) ,
112112 ( Some ( line) , None ) => format ! ( ":{}" , line) ,
113113 _ => String :: new ( ) ,
114114 } ;
115-
115+
116116 format ! (
117117 "{}{} [{}] {}" ,
118118 self . file_path. display( ) ,
@@ -149,12 +149,12 @@ impl ViolationCounts {
149149 pub fn total ( & self ) -> usize {
150150 self . error + self . warning + self . info
151151 }
152-
152+
153153 /// Whether there are any blocking violations
154154 pub fn has_blocking ( & self ) -> bool {
155155 self . error > 0
156156 }
157-
157+
158158 /// Add a violation to the counts
159159 pub fn add ( & mut self , severity : Severity ) {
160160 match severity {
@@ -188,55 +188,58 @@ impl ValidationReport {
188188 config_fingerprint : None ,
189189 }
190190 }
191-
191+
192192 /// Add a violation to the report
193193 pub fn add_violation ( & mut self , violation : Violation ) {
194194 self . summary . violations_by_severity . add ( violation. severity ) ;
195195 self . violations . push ( violation) ;
196196 }
197-
197+
198198 /// Whether the report contains any violations
199199 pub fn has_violations ( & self ) -> bool {
200200 !self . violations . is_empty ( )
201201 }
202-
202+
203203 /// Whether the report contains blocking violations (errors)
204204 pub fn has_errors ( & self ) -> bool {
205205 self . summary . violations_by_severity . has_blocking ( )
206206 }
207-
207+
208208 /// Get violations of a specific severity
209209 pub fn violations_by_severity ( & self , severity : Severity ) -> impl Iterator < Item = & Violation > {
210- self . violations . iter ( ) . filter ( move |v| v. severity == severity)
210+ self . violations
211+ . iter ( )
212+ . filter ( move |v| v. severity == severity)
211213 }
212-
214+
213215 /// Set the number of files analyzed
214216 pub fn set_files_analyzed ( & mut self , count : usize ) {
215217 self . summary . total_files = count;
216218 }
217-
219+
218220 /// Set the execution time
219221 pub fn set_execution_time ( & mut self , duration_ms : u64 ) {
220222 self . summary . execution_time_ms = duration_ms;
221223 }
222-
224+
223225 /// Set the configuration fingerprint
224226 pub fn set_config_fingerprint ( & mut self , fingerprint : impl Into < String > ) {
225227 self . config_fingerprint = Some ( fingerprint. into ( ) ) ;
226228 }
227-
229+
228230 /// Merge another report into this one
229231 pub fn merge ( & mut self , other : ValidationReport ) {
230232 for violation in other. violations {
231233 self . add_violation ( violation) ;
232234 }
233235 self . summary . total_files += other. summary . total_files ;
234236 }
235-
237+
236238 /// Sort violations by file path and line number for consistent output
237239 pub fn sort_violations ( & mut self ) {
238240 self . violations . sort_by ( |a, b| {
239- a. file_path . cmp ( & b. file_path )
241+ a. file_path
242+ . cmp ( & b. file_path )
240243 . then_with ( || a. line_number . unwrap_or ( 0 ) . cmp ( & b. line_number . unwrap_or ( 0 ) ) )
241244 . then_with ( || a. severity . cmp ( & b. severity ) )
242245 } ) ;
@@ -255,22 +258,22 @@ pub enum GuardianError {
255258 /// Configuration file could not be loaded or parsed
256259 #[ error( "Configuration error: {message}" ) ]
257260 Configuration { message : String } ,
258-
261+
259262 /// File could not be read or accessed
260263 #[ error( "IO error: {source}" ) ]
261264 Io {
262265 #[ from]
263266 source : std:: io:: Error ,
264267 } ,
265-
268+
266269 /// Pattern compilation failed
267270 #[ error( "Pattern error: {message}" ) ]
268271 Pattern { message : String } ,
269-
272+
270273 /// Analysis failed for a specific file
271274 #[ error( "Analysis error in {file}: {message}" ) ]
272275 Analysis { file : String , message : String } ,
273-
276+
274277 /// Cache operation failed
275278 #[ error( "Cache error: {message}" ) ]
276279 Cache { message : String } ,
@@ -279,25 +282,31 @@ pub enum GuardianError {
279282impl GuardianError {
280283 /// Create a configuration error
281284 pub fn config ( message : impl Into < String > ) -> Self {
282- Self :: Configuration { message : message. into ( ) }
285+ Self :: Configuration {
286+ message : message. into ( ) ,
287+ }
283288 }
284-
289+
285290 /// Create a pattern error
286291 pub fn pattern ( message : impl Into < String > ) -> Self {
287- Self :: Pattern { message : message. into ( ) }
292+ Self :: Pattern {
293+ message : message. into ( ) ,
294+ }
288295 }
289-
296+
290297 /// Create an analysis error
291298 pub fn analysis ( file : impl Into < String > , message : impl Into < String > ) -> Self {
292- Self :: Analysis {
293- file : file. into ( ) ,
294- message : message. into ( )
299+ Self :: Analysis {
300+ file : file. into ( ) ,
301+ message : message. into ( ) ,
295302 }
296303 }
297-
304+
298305 /// Create a cache error
299306 pub fn cache ( message : impl Into < String > ) -> Self {
300- Self :: Cache { message : message. into ( ) }
307+ Self :: Cache {
308+ message : message. into ( ) ,
309+ }
301310 }
302311}
303312
@@ -308,7 +317,7 @@ pub type GuardianResult<T> = Result<T, GuardianError>;
308317mod tests {
309318 use super :: * ;
310319 use std:: path:: Path ;
311-
320+
312321 #[ test]
313322 fn test_violation_creation ( ) {
314323 let violation = Violation :: new (
@@ -317,14 +326,14 @@ mod tests {
317326 PathBuf :: from ( "src/lib.rs" ) ,
318327 "Test message" ,
319328 ) ;
320-
329+
321330 assert_eq ! ( violation. rule_id, "test_rule" ) ;
322331 assert_eq ! ( violation. severity, Severity :: Error ) ;
323332 assert_eq ! ( violation. file_path, Path :: new( "src/lib.rs" ) ) ;
324333 assert_eq ! ( violation. message, "Test message" ) ;
325334 assert ! ( violation. is_blocking( ) ) ;
326335 }
327-
336+
328337 #[ test]
329338 fn test_violation_with_position ( ) {
330339 let violation = Violation :: new (
@@ -335,43 +344,43 @@ mod tests {
335344 )
336345 . with_position ( 42 , 15 )
337346 . with_context ( "let x = todo!();" ) ;
338-
347+
339348 assert_eq ! ( violation. line_number, Some ( 42 ) ) ;
340349 assert_eq ! ( violation. column_number, Some ( 15 ) ) ;
341350 assert_eq ! ( violation. context, Some ( "let x = todo!();" . to_string( ) ) ) ;
342351 assert ! ( !violation. is_blocking( ) ) ;
343352 }
344-
353+
345354 #[ test]
346355 fn test_validation_report ( ) {
347356 let mut report = ValidationReport :: new ( ) ;
348-
357+
349358 report. add_violation ( Violation :: new (
350359 "rule1" ,
351360 Severity :: Error ,
352361 PathBuf :: from ( "src/main.rs" ) ,
353362 "Error message" ,
354363 ) ) ;
355-
364+
356365 report. add_violation ( Violation :: new (
357- "rule2" ,
366+ "rule2" ,
358367 Severity :: Warning ,
359368 PathBuf :: from ( "src/lib.rs" ) ,
360369 "Warning message" ,
361370 ) ) ;
362-
371+
363372 assert ! ( report. has_violations( ) ) ;
364373 assert ! ( report. has_errors( ) ) ;
365374 assert_eq ! ( report. summary. violations_by_severity. total( ) , 2 ) ;
366375 assert_eq ! ( report. summary. violations_by_severity. error, 1 ) ;
367376 assert_eq ! ( report. summary. violations_by_severity. warning, 1 ) ;
368377 }
369-
378+
370379 #[ test]
371380 fn test_severity_ordering ( ) {
372381 assert ! ( Severity :: Error > Severity :: Warning ) ;
373382 assert ! ( Severity :: Warning > Severity :: Info ) ;
374383 assert ! ( Severity :: Error . is_blocking( ) ) ;
375384 assert ! ( !Severity :: Warning . is_blocking( ) ) ;
376385 }
377- }
386+ }
0 commit comments