github.com/bgpat/reviewdog@v0.0.0-20230909064023-077e44ca1f66/reviewdog.go (about) 1 package reviewdog 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "os" 9 10 "github.com/bgpat/reviewdog/diff" 11 "github.com/bgpat/reviewdog/filter" 12 "github.com/bgpat/reviewdog/parser" 13 "github.com/bgpat/reviewdog/proto/rdf" 14 ) 15 16 // Reviewdog represents review dog application which parses result of compiler 17 // or linter, get diff and filter the results by diff, and report filtered 18 // results. 19 type Reviewdog struct { 20 toolname string 21 p parser.Parser 22 c CommentService 23 d DiffService 24 filterMode filter.Mode 25 failOnError bool 26 } 27 28 // NewReviewdog returns a new Reviewdog. 29 func NewReviewdog(toolname string, p parser.Parser, c CommentService, d DiffService, filterMode filter.Mode, failOnError bool) *Reviewdog { 30 return &Reviewdog{p: p, c: c, d: d, toolname: toolname, filterMode: filterMode, failOnError: failOnError} 31 } 32 33 // RunFromResult creates a new Reviewdog and runs it with check results. 34 func RunFromResult(ctx context.Context, c CommentService, results []*rdf.Diagnostic, 35 filediffs []*diff.FileDiff, strip int, toolname string, filterMode filter.Mode, failOnError bool) error { 36 return (&Reviewdog{c: c, toolname: toolname, filterMode: filterMode, failOnError: failOnError}).runFromResult(ctx, results, filediffs, strip, failOnError) 37 } 38 39 // Comment represents a reported result as a comment. 40 type Comment struct { 41 Result *filter.FilteredDiagnostic 42 ToolName string 43 } 44 45 // CommentService is an interface which posts Comment. 46 type CommentService interface { 47 Post(context.Context, *Comment) error 48 } 49 50 // BulkCommentService posts comments all at once when Flush() is called. 51 // Flush() will be called at the end of reviewdog run. 52 type BulkCommentService interface { 53 CommentService 54 Flush(context.Context) error 55 } 56 57 // DiffService is an interface which get diff. 58 type DiffService interface { 59 Diff(context.Context) ([]byte, error) 60 Strip() int 61 } 62 63 func (w *Reviewdog) runFromResult(ctx context.Context, results []*rdf.Diagnostic, 64 filediffs []*diff.FileDiff, strip int, failOnError bool) error { 65 wd, err := os.Getwd() 66 if err != nil { 67 return err 68 } 69 70 checks := filter.FilterCheck(results, filediffs, strip, wd, w.filterMode) 71 hasViolations := false 72 73 for _, check := range checks { 74 if !check.ShouldReport { 75 continue 76 } 77 comment := &Comment{ 78 Result: check, 79 ToolName: w.toolname, 80 } 81 if err := w.c.Post(ctx, comment); err != nil { 82 return err 83 } 84 hasViolations = true 85 } 86 87 if bulk, ok := w.c.(BulkCommentService); ok { 88 if err := bulk.Flush(ctx); err != nil { 89 return err 90 } 91 } 92 93 if failOnError && hasViolations { 94 return fmt.Errorf("input data has violations") 95 } 96 97 return nil 98 } 99 100 // Run runs Reviewdog application. 101 func (w *Reviewdog) Run(ctx context.Context, r io.Reader) error { 102 results, err := w.p.Parse(r) 103 if err != nil { 104 return fmt.Errorf("parse error: %w", err) 105 } 106 107 d, err := w.d.Diff(ctx) 108 if err != nil { 109 return fmt.Errorf("fail to get diff: %w", err) 110 } 111 112 filediffs, err := diff.ParseMultiFile(bytes.NewReader(d)) 113 if err != nil { 114 return fmt.Errorf("fail to parse diff: %w", err) 115 } 116 117 return w.runFromResult(ctx, results, filediffs, w.d.Strip(), w.failOnError) 118 }