github.com/massongit/reviewdog@v0.0.0-20240331071725-4a16675475a8/reviewdog.go (about)

     1  package reviewdog
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  
    10  	"github.com/reviewdog/reviewdog/diff"
    11  	"github.com/reviewdog/reviewdog/filter"
    12  	"github.com/reviewdog/reviewdog/parser"
    13  	"github.com/reviewdog/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  }