@@ -11,6 +11,9 @@ use squawk_linter::Linter;
1111use squawk_linter:: Rule ;
1212use squawk_linter:: Version ;
1313use squawk_syntax:: SourceFile ;
14+ use std:: hash:: DefaultHasher ;
15+ use std:: hash:: Hash ;
16+ use std:: hash:: Hasher ;
1417use std:: io;
1518use std:: path:: PathBuf ;
1619use std:: process:: ExitCode ;
@@ -359,6 +362,86 @@ pub fn fmt_github_annotations<W: io::Write>(f: &mut W, reports: &[CheckReport])
359362 Ok ( ( ) )
360363}
361364
365+ #[ derive( Serialize ) ]
366+ struct GitLabLines {
367+ begin : usize ,
368+ end : usize ,
369+ }
370+
371+ #[ derive( Serialize ) ]
372+ struct GitLabLocation {
373+ path : String ,
374+ lines : GitLabLines ,
375+ }
376+
377+ #[ derive( Serialize ) ]
378+ struct GitLabIssue {
379+ description : String ,
380+ severity : String ,
381+ fingerprint : String ,
382+ location : GitLabLocation ,
383+ check_name : String ,
384+ }
385+
386+ impl From < & ViolationLevel > for String {
387+ fn from ( level : & ViolationLevel ) -> Self {
388+ match level {
389+ ViolationLevel :: Warning => "minor" . to_string ( ) ,
390+ ViolationLevel :: Error => "major" . to_string ( ) ,
391+ }
392+ }
393+ }
394+
395+ fn make_fingerprint ( v : & ReportViolation ) -> String {
396+ let key = format ! (
397+ "{}:{}-{}:{}:{}:{}" ,
398+ v. file, v. line, v. line_end, v. rule_name, v. message, v. level
399+ ) ;
400+ let mut hasher = DefaultHasher :: new ( ) ;
401+ key. hash ( & mut hasher) ;
402+ format ! ( "{:x}" , hasher. finish( ) )
403+ }
404+
405+ fn to_gitlab_issue ( v : & ReportViolation ) -> GitLabIssue {
406+ let mut desc = v. message . clone ( ) ;
407+ if let Some ( help) = & v. help {
408+ if !help. trim ( ) . is_empty ( ) {
409+ desc. push_str ( " Suggestion: " ) ;
410+ desc. push_str ( help. trim ( ) ) ;
411+ }
412+ }
413+
414+ GitLabIssue {
415+ description : desc,
416+ severity : String :: from ( & v. level ) ,
417+ fingerprint : make_fingerprint ( v) ,
418+ location : GitLabLocation {
419+ path : v. file . clone ( ) ,
420+ lines : GitLabLines {
421+ begin : v. line ,
422+ end : if v. line_end >= v. line {
423+ v. line_end
424+ } else {
425+ v. line
426+ } ,
427+ } ,
428+ } ,
429+ check_name : v. rule_name . clone ( ) ,
430+ }
431+ }
432+
433+ fn fmt_gitlab < W : io:: Write > ( f : & mut W , reports : Vec < CheckReport > ) -> Result < ( ) > {
434+ let issues: Vec < GitLabIssue > = reports
435+ . into_iter ( )
436+ . flat_map ( |r| r. violations . into_iter ( ) )
437+ . map ( |v| to_gitlab_issue ( & v) )
438+ . collect ( ) ;
439+
440+ let json = serde_json:: to_string ( & issues) ?;
441+ writeln ! ( f, "{json}" ) ?;
442+ Ok ( ( ) )
443+ }
444+
362445#[ derive( Debug ) ]
363446pub struct CheckReport {
364447 pub filename : String ,
@@ -379,6 +462,7 @@ pub fn print_violations<W: io::Write>(
379462 Reporter :: Gcc => fmt_gcc ( writer, & reports) ,
380463 Reporter :: Json => fmt_json ( writer, reports) ,
381464 Reporter :: Tty => fmt_tty ( writer, & reports) ,
465+ Reporter :: Gitlab => fmt_gitlab ( writer, reports) ,
382466 }
383467}
384468
0 commit comments