github.com/haya14busa/reviewdog@v0.0.0-20180723114510-ffb00ef78fd3/reviewdog.go (about) 1 package reviewdog 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "os" 9 10 "github.com/haya14busa/reviewdog/diff" 11 ) 12 13 // Version is version of reviewdog CLI. 14 const Version = "0.9.11" 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 22 c CommentService 23 d DiffService 24 } 25 26 // NewReviewdog returns a new Reviewdog. 27 func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService) *Reviewdog { 28 return &Reviewdog{p: p, c: c, d: d, toolname: toolname} 29 } 30 31 func RunFromResult(ctx context.Context, c CommentService, results []*CheckResult, 32 filediffs []*diff.FileDiff, strip int, toolname string) error { 33 return (&Reviewdog{c: c, toolname: toolname}).runFromResult(ctx, results, filediffs, strip) 34 } 35 36 // CheckResult represents a checked result of static analysis tools. 37 // :h error-file-format 38 type CheckResult struct { 39 Path string // relative file path 40 Lnum int // line number 41 Col int // column number (1 <tab> == 1 character column) 42 Message string // error message 43 Lines []string // Original error lines (often one line) 44 } 45 46 // Parser is an interface which parses compilers, linters, or any tools 47 // results. 48 type Parser interface { 49 Parse(r io.Reader) ([]*CheckResult, error) 50 } 51 52 // Comment represents a reported result as a comment. 53 type Comment struct { 54 *CheckResult 55 Body string 56 LnumDiff int 57 ToolName string 58 } 59 60 // CommentService is an interface which posts Comment. 61 type CommentService interface { 62 Post(context.Context, *Comment) error 63 } 64 65 // BulkCommentService posts comments all at once when Flush() is called. 66 // Flush() will be called at the end of reviewdog run. 67 type BulkCommentService interface { 68 CommentService 69 Flush(context.Context) error 70 } 71 72 // DiffService is an interface which get diff. 73 type DiffService interface { 74 Diff(context.Context) ([]byte, error) 75 Strip() int 76 } 77 78 func (w *Reviewdog) runFromResult(ctx context.Context, results []*CheckResult, 79 filediffs []*diff.FileDiff, strip int) error { 80 wd, err := os.Getwd() 81 if err != nil { 82 return err 83 } 84 85 checks := FilterCheck(results, filediffs, strip, wd) 86 for _, check := range checks { 87 if !check.InDiff { 88 continue 89 } 90 comment := &Comment{ 91 CheckResult: check.CheckResult, 92 Body: check.Message, // TODO: format message 93 LnumDiff: check.LnumDiff, 94 ToolName: w.toolname, 95 } 96 if err := w.c.Post(ctx, comment); err != nil { 97 return err 98 } 99 } 100 101 if bulk, ok := w.c.(BulkCommentService); ok { 102 return bulk.Flush(ctx) 103 } 104 105 return nil 106 } 107 108 // Run runs Reviewdog application. 109 func (w *Reviewdog) Run(ctx context.Context, r io.Reader) error { 110 results, err := w.p.Parse(r) 111 if err != nil { 112 return fmt.Errorf("parse error: %v", err) 113 } 114 115 d, err := w.d.Diff(ctx) 116 if err != nil { 117 return fmt.Errorf("fail to get diff: %v", err) 118 } 119 120 filediffs, err := diff.ParseMultiFile(bytes.NewReader(d)) 121 if err != nil { 122 return fmt.Errorf("fail to parse diff: %v", err) 123 } 124 125 return w.runFromResult(ctx, results, filediffs, w.d.Strip()) 126 }