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  }