1
0
mirror of https://github.com/mgechev/revive.git synced 2024-11-28 08:49:11 +02:00
revive/revivelib/core.go
Bernardo Heynemann 318db94210
Separating lib from cli (#655)
* Separating lib from cli

* Renamed NewRevive to New

* Added GetLintFailures helper function

* Moved formatter to call to format since that's when it's needed

* makes fields of Revive struct non-public

* minor modifs in tests: remove unnamed constats

* Added lint package management to lint command

* README message for using revive as a library

* README formatting

* Removed unused method

* Slightly improved wording in README

* Handling format errors

* Renaming file to better reflect intent

* Refactoring pattern usage

* README heads

* renames excludePaths into excludePatterns

Co-authored-by: Bernardo Heynemann <bernardo.heynemann@coinbase.com>
Co-authored-by: chavacava <salvadorcavadini+github@gmail.com>
2022-03-29 17:31:52 +02:00

205 lines
4.4 KiB
Go

package revivelib
import (
"io/ioutil"
"log"
"strings"
"github.com/mgechev/dots"
"github.com/mgechev/revive/config"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/logging"
"github.com/pkg/errors"
)
// Revive is responsible for running linters and formatters
// and returning a set of results.
type Revive struct {
config *lint.Config
lintingRules []lint.Rule
logger *log.Logger
maxOpenFiles int
}
// New creates a new instance of Revive lint runner.
func New(
conf *lint.Config,
setExitStatus bool,
maxOpenFiles int,
extraRules ...ExtraRule,
) (*Revive, error) {
log, err := logging.GetLogger()
if err != nil {
return nil, errors.Wrap(err, "initializing revive - getting logger")
}
if setExitStatus {
conf.ErrorCode = 1
conf.WarningCode = 1
}
extraRuleInstances := make([]lint.Rule, len(extraRules))
for i, extraRule := range extraRules {
extraRuleInstances[i] = extraRule.Rule
ruleName := extraRule.Rule.Name()
_, isRuleAlreadyConfigured := conf.Rules[ruleName]
if !isRuleAlreadyConfigured {
conf.Rules[ruleName] = extraRule.DefaultConfig
}
}
lintingRules, err := config.GetLintingRules(conf, extraRuleInstances)
if err != nil {
return nil, errors.Wrap(err, "initializing revive - gettint lint rules")
}
log.Println("Config loaded")
return &Revive{
logger: log,
config: conf,
lintingRules: lintingRules,
maxOpenFiles: maxOpenFiles,
}, nil
}
// Lint the included patterns, skipping excluded ones
func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
includePatterns := []string{}
excludePatterns := []string{}
for _, lintpkg := range patterns {
if lintpkg.IsExclude() {
excludePatterns = append(excludePatterns, lintpkg.GetPattern())
} else {
includePatterns = append(includePatterns, lintpkg.GetPattern())
}
}
if len(excludePatterns) == 0 { // if no excludes were set
excludePatterns = r.config.Exclude // use those from the configuration
}
packages, err := getPackages(includePatterns, excludePatterns)
if err != nil {
return nil, errors.Wrap(err, "linting - getting packages")
}
revive := lint.New(func(file string) ([]byte, error) {
contents, err := ioutil.ReadFile(file)
if err != nil {
return nil, errors.Wrap(err, "reading file "+file)
}
return contents, nil
}, r.maxOpenFiles)
failures, err := revive.Lint(packages, r.lintingRules, *r.config)
if err != nil {
return nil, errors.Wrap(err, "linting - retrieving failures channel")
}
return failures, nil
}
// Format gets the output for a given failures channel from Lint.
func (r *Revive) Format(
formatterName string,
failuresChan <-chan lint.Failure,
) (string, int, error) {
conf := r.config
formatChan := make(chan lint.Failure)
exitChan := make(chan bool)
formatter, err := config.GetFormatter(formatterName)
if err != nil {
return "", 0, errors.Wrap(err, "formatting - getting formatter")
}
var (
output string
formatErr error
)
go func() {
output, formatErr = formatter.Format(formatChan, *conf)
exitChan <- true
}()
exitCode := 0
for failure := range failuresChan {
if failure.Confidence < conf.Confidence {
continue
}
if exitCode == 0 {
exitCode = conf.WarningCode
}
if c, ok := conf.Rules[failure.RuleName]; ok && c.Severity == lint.SeverityError {
exitCode = conf.ErrorCode
}
if c, ok := conf.Directives[failure.RuleName]; ok && c.Severity == lint.SeverityError {
exitCode = conf.ErrorCode
}
formatChan <- failure
}
close(formatChan)
<-exitChan
if formatErr != nil {
return "", exitCode, errors.Wrap(err, "formatting")
}
return output, exitCode, nil
}
func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]string, error) {
globs := normalizeSplit(includePatterns)
if len(globs) == 0 {
globs = append(globs, ".")
}
packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePatterns))
if err != nil {
return nil, errors.Wrap(err, "getting packages - resolving packages in dots")
}
return packages, nil
}
func normalizeSplit(strs []string) []string {
res := []string{}
for _, s := range strs {
t := strings.Trim(s, " \t")
if len(t) > 0 {
res = append(res, t)
}
}
return res
}
// ArrayFlags type for string list.
type ArrayFlags []string
func (i *ArrayFlags) String() string {
return strings.Join([]string(*i), " ")
}
// Set value for array flags.
func (i *ArrayFlags) Set(value string) error {
*i = append(*i, value)
return nil
}