cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.43.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/reporter/console.go

162lines · modecode

1package reporter
2
3import (
4 "fmt"
5 "io"
6 "os"
7 "sort"
8 "strconv"
9 "strings"
10
11 "github.com/fatih/color"
12 "github.com/rs/zerolog/log"
13 "golang.org/x/exp/slices"
14
15 "github.com/cloudflare/pint/internal/checks"
16 "github.com/cloudflare/pint/internal/output"
17)
18
19func NewConsoleReporter(output io.Writer, minSeverity checks.Severity) ConsoleReporter {
20 return ConsoleReporter{output: output, minSeverity: minSeverity}
21}
22
23type ConsoleReporter struct {
24 output io.Writer
25 minSeverity checks.Severity
26}
27
28func (cr ConsoleReporter) Submit(summary Summary) error {
29 reports := summary.Reports()
30 sort.Slice(reports, func(i, j int) bool {
31 if reports[i].SourcePath < reports[j].SourcePath {
32 return true
33 }
34 if reports[i].SourcePath > reports[j].SourcePath {
35 return false
36 }
37 if reports[i].Problem.Lines[0] < reports[j].Problem.Lines[0] {
38 return true
39 }
40 if reports[i].Problem.Lines[0] > reports[j].Problem.Lines[0] {
41 return false
42 }
43 if reports[i].Problem.Reporter < reports[j].Problem.Reporter {
44 return true
45 }
46 if reports[i].Problem.Reporter > reports[j].Problem.Reporter {
47 return false
48 }
49 return reports[i].Problem.Text < reports[j].Problem.Text
50 })
51
52 perFile := map[string][]string{}
53 for _, report := range reports {
54 if report.Problem.Severity < cr.minSeverity {
55 continue
56 }
57
58 if !shouldReport(report) {
59 log.Debug().
60 Str("path", report.SourcePath).
61 Str("lines", output.FormatLineRangeString(report.Problem.Lines)).
62 Msg("Problem reported on unmodified line, skipping")
63 continue
64 }
65
66 if _, ok := perFile[report.SourcePath]; !ok {
67 perFile[report.SourcePath] = []string{}
68 }
69
70 content, err := readFile(report.SourcePath)
71 if err != nil {
72 return err
73 }
74
75 msg := []string{}
76
77 firstLine, lastLine := report.Problem.LineRange()
78
79 path := report.SourcePath
80 if report.SourcePath != report.ReportedPath {
81 path = fmt.Sprintf("%s ~> %s", report.SourcePath, report.ReportedPath)
82 }
83
84 msg = append(msg, color.CyanString("%s:%s ", path, printLineRange(firstLine, lastLine)))
85 switch report.Problem.Severity {
86 case checks.Bug, checks.Fatal:
87 msg = append(msg, color.RedString("%s: %s", report.Problem.Severity, report.Problem.Text))
88 case checks.Warning:
89 msg = append(msg, color.YellowString("%s: %s", report.Problem.Severity, report.Problem.Text))
90 case checks.Information:
91 msg = append(msg, color.HiBlackString("%s: %s", report.Problem.Severity, report.Problem.Text))
92 }
93 msg = append(msg, color.MagentaString(" (%s)\n", report.Problem.Reporter))
94
95 lines := strings.Split(content, "\n")
96 if lastLine > len(lines)-1 {
97 lastLine = len(lines) - 1
98 log.Warn().Str("path", report.SourcePath).Msgf("Tried to read more lines than present in the source file, this is likely due to '\n' usage in some rules, see https://github.com/cloudflare/pint/issues/20 for details")
99 }
100
101 nrFmt := fmt.Sprintf("%%%dd", countDigits(lastLine)+1)
102 var inPlaceholder bool
103 for i := firstLine; i <= lastLine; i++ {
104 switch {
105 case slices.Contains(report.Problem.Lines, i):
106 msg = append(msg, color.WhiteString(nrFmt+" | %s\n", i, lines[i-1]))
107 inPlaceholder = false
108 case inPlaceholder:
109 //
110 default:
111 msg = append(msg, color.WhiteString(" %s\n", strings.Repeat(".", countDigits(lastLine))))
112 inPlaceholder = true
113 }
114 }
115 perFile[report.SourcePath] = append(perFile[report.SourcePath], strings.Join(msg, ""))
116 }
117
118 paths := []string{}
119 for path := range perFile {
120 paths = append(paths, path)
121 }
122 sort.Strings(paths)
123
124 for _, path := range paths {
125 msgs := perFile[path]
126 for _, msg := range msgs {
127 fmt.Fprintln(cr.output, msg)
128 }
129 }
130
131 return nil
132}
133
134func readFile(path string) (string, error) {
135 f, err := os.Open(path)
136 if err != nil {
137 return "", err
138 }
139 defer f.Close()
140
141 content, err := io.ReadAll(f)
142 if err != nil {
143 return "", err
144 }
145
146 return string(content), nil
147}
148
149func printLineRange(s, e int) string {
150 if s == e {
151 return strconv.Itoa(s)
152 }
153 return fmt.Sprintf("%d-%d", s, e)
154}
155
156func countDigits(n int) (c int) {
157 for n > 0 {
158 n /= 10
159 c++
160 }
161 return c
162}
163