cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.28.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/reporter/console.go

155lines · 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].Path < reports[j].Path {
32 return true
33 }
34 if reports[i].Path > reports[j].Path {
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.Path).
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.Path]; !ok {
67 perFile[report.Path] = []string{}
68 }
69
70 content, err := readFile(report.Path)
71 if err != nil {
72 return err
73 }
74
75 msg := []string{}
76 firstLine, lastLine := report.Problem.LineRange()
77 msg = append(msg, color.CyanString("%s:%s: ", report.Path, printLineRange(firstLine, lastLine)))
78 switch report.Problem.Severity {
79 case checks.Bug, checks.Fatal:
80 msg = append(msg, color.RedString(report.Problem.Text))
81 case checks.Warning:
82 msg = append(msg, color.YellowString(report.Problem.Text))
83 case checks.Information:
84 msg = append(msg, color.HiBlackString(report.Problem.Text))
85 }
86 msg = append(msg, color.MagentaString(" (%s)\n", report.Problem.Reporter))
87
88 lines := strings.Split(content, "\n")
89 if lastLine > len(lines)-1 {
90 lastLine = len(lines) - 1
91 log.Warn().Str("path", report.Path).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")
92 }
93
94 nrFmt := fmt.Sprintf("%%%dd", countDigits(lastLine)+1)
95 var inPlaceholder bool
96 for i := firstLine; i <= lastLine; i++ {
97 switch {
98 case slices.Contains(report.Problem.Lines, i):
99 msg = append(msg, color.WhiteString(nrFmt+" | %s\n", i, lines[i-1]))
100 inPlaceholder = false
101 case inPlaceholder:
102 //
103 default:
104 msg = append(msg, color.WhiteString(" %s\n", strings.Repeat(".", countDigits(lastLine))))
105 inPlaceholder = true
106 }
107 }
108 perFile[report.Path] = append(perFile[report.Path], strings.Join(msg, ""))
109 }
110
111 paths := []string{}
112 for path := range perFile {
113 paths = append(paths, path)
114 }
115 sort.Strings(paths)
116
117 for _, path := range paths {
118 msgs := perFile[path]
119 for _, msg := range msgs {
120 fmt.Fprintln(cr.output, msg)
121 }
122 }
123
124 return nil
125}
126
127func readFile(path string) (string, error) {
128 f, err := os.Open(path)
129 if err != nil {
130 return "", err
131 }
132 defer f.Close()
133
134 content, err := io.ReadAll(f)
135 if err != nil {
136 return "", err
137 }
138
139 return string(content), nil
140}
141
142func printLineRange(s, e int) string {
143 if s == e {
144 return strconv.Itoa(s)
145 }
146 return fmt.Sprintf("%d-%d", s, e)
147}
148
149func countDigits(n int) (c int) {
150 for n > 0 {
151 n /= 10
152 c++
153 }
154 return c
155}
156