github.com/alexey-mercari/reviewdog@v0.10.1-0.20200514053941-928943b10766/parser.go (about)

     1  package reviewdog
     2  
     3  import (
     4  	"encoding/xml"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/reviewdog/errorformat"
    10  	"github.com/reviewdog/errorformat/fmts"
    11  )
    12  
    13  // ParserOpt represents option to create Parser. Either FormatName or
    14  // Errorformat should be specified.
    15  type ParserOpt struct {
    16  	FormatName  string
    17  	Errorformat []string
    18  }
    19  
    20  // NewParser returns Parser based on ParserOpt.
    21  func NewParser(opt *ParserOpt) (Parser, error) {
    22  	name := opt.FormatName
    23  
    24  	if name != "" && len(opt.Errorformat) > 0 {
    25  		return nil, errors.New("you cannot specify both format name and errorformat at the same time")
    26  	}
    27  
    28  	if name == "checkstyle" {
    29  		return NewCheckStyleParser(), nil
    30  	}
    31  	// use defined errorformat
    32  	if name != "" {
    33  		efm, ok := fmts.DefinedFmts()[name]
    34  		if !ok {
    35  			return nil, fmt.Errorf("%q is not supported. consider to add new errorformat to https://github.com/reviewdog/errorformat", name)
    36  		}
    37  		opt.Errorformat = efm.Errorformat
    38  	}
    39  	if len(opt.Errorformat) == 0 {
    40  		return nil, errors.New("errorformat is empty")
    41  	}
    42  	return NewErrorformatParserString(opt.Errorformat)
    43  }
    44  
    45  var _ Parser = &ErrorformatParser{}
    46  
    47  // ErrorformatParser is errorformat parser.
    48  type ErrorformatParser struct {
    49  	efm *errorformat.Errorformat
    50  }
    51  
    52  // NewErrorformatParser returns a new ErrorformatParser.
    53  func NewErrorformatParser(efm *errorformat.Errorformat) *ErrorformatParser {
    54  	return &ErrorformatParser{efm: efm}
    55  }
    56  
    57  // NewErrorformatParserString returns a new ErrorformatParser from errorformat
    58  // in string representation.
    59  func NewErrorformatParserString(efms []string) (*ErrorformatParser, error) {
    60  	efm, err := errorformat.NewErrorformat(efms)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return NewErrorformatParser(efm), nil
    65  }
    66  
    67  func (p *ErrorformatParser) Parse(r io.Reader) ([]*CheckResult, error) {
    68  	s := p.efm.NewScanner(r)
    69  	var rs []*CheckResult
    70  	for s.Scan() {
    71  		e := s.Entry()
    72  		if e.Valid {
    73  			rs = append(rs, &CheckResult{
    74  				Path:    e.Filename,
    75  				Lnum:    e.Lnum,
    76  				Col:     e.Col,
    77  				Message: e.Text,
    78  				Lines:   e.Lines,
    79  			})
    80  		}
    81  	}
    82  	return rs, nil
    83  }
    84  
    85  var _ Parser = &CheckStyleParser{}
    86  
    87  // CheckStyleParser is checkstyle parser.
    88  type CheckStyleParser struct{}
    89  
    90  // NewCheckStyleParser returns a new CheckStyleParser.
    91  func NewCheckStyleParser() Parser {
    92  	return &CheckStyleParser{}
    93  }
    94  
    95  func (p *CheckStyleParser) Parse(r io.Reader) ([]*CheckResult, error) {
    96  	var cs = new(CheckStyleResult)
    97  	if err := xml.NewDecoder(r).Decode(cs); err != nil {
    98  		return nil, err
    99  	}
   100  	var rs []*CheckResult
   101  	for _, file := range cs.Files {
   102  		for _, cerr := range file.Errors {
   103  			rs = append(rs, &CheckResult{
   104  				Path:    file.Name,
   105  				Lnum:    cerr.Line,
   106  				Col:     cerr.Column,
   107  				Message: cerr.Message,
   108  				Lines: []string{
   109  					fmt.Sprintf("%v:%d:%d: %v: %v (%v)",
   110  						file.Name, cerr.Line, cerr.Column, cerr.Severity, cerr.Message, cerr.Source),
   111  				},
   112  			})
   113  		}
   114  	}
   115  	return rs, nil
   116  }
   117  
   118  // CheckStyleResult represents checkstyle XML result.
   119  // <?xml version="1.0" encoding="utf-8"?><checkstyle version="4.3"><file ...></file>...</checkstyle>
   120  //
   121  // References:
   122  //   - http://checkstyle.sourceforge.net/
   123  //   - http://eslint.org/docs/user-guide/formatters/#checkstyle
   124  type CheckStyleResult struct {
   125  	XMLName xml.Name          `xml:"checkstyle"`
   126  	Version string            `xml:"version,attr"`
   127  	Files   []*CheckStyleFile `xml:"file,omitempty"`
   128  }
   129  
   130  // CheckStyleFile represents <file name="fname"><error ... />...</file>
   131  type CheckStyleFile struct {
   132  	Name   string             `xml:"name,attr"`
   133  	Errors []*CheckStyleError `xml:"error"`
   134  }
   135  
   136  // CheckStyleError represents <error line="1" column="10" severity="error" message="msg" source="src" />
   137  type CheckStyleError struct {
   138  	Column   int    `xml:"column,attr,omitempty"`
   139  	Line     int    `xml:"line,attr"`
   140  	Message  string `xml:"message,attr"`
   141  	Severity string `xml:"severity,attr,omitempty"`
   142  	Source   string `xml:"source,attr,omitempty"`
   143  }