cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.56.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/reporter/console.go

145lines · modecode

1package reporter
2
3import (
4 "fmt"
5 "io"
6 "log/slog"
7 "os"
8 "sort"
9 "strings"
10
11 "github.com/fatih/color"
12
13 "github.com/cloudflare/pint/internal/checks"
14)
15
16func NewConsoleReporter(output io.Writer, minSeverity checks.Severity) ConsoleReporter {
17 return ConsoleReporter{output: output, minSeverity: minSeverity}
18}
19
20type ConsoleReporter struct {
21 output io.Writer
22 minSeverity checks.Severity
23}
24
25func (cr ConsoleReporter) Submit(summary Summary) (err error) {
26 reports := summary.Reports()
27 sort.Slice(reports, func(i, j int) bool {
28 if reports[i].SourcePath < reports[j].SourcePath {
29 return true
30 }
31 if reports[i].SourcePath > reports[j].SourcePath {
32 return false
33 }
34 if reports[i].Problem.Lines.First < reports[j].Problem.Lines.First {
35 return true
36 }
37 if reports[i].Problem.Lines.First > reports[j].Problem.Lines.First {
38 return false
39 }
40 if reports[i].Problem.Reporter < reports[j].Problem.Reporter {
41 return true
42 }
43 if reports[i].Problem.Reporter > reports[j].Problem.Reporter {
44 return false
45 }
46 return reports[i].Problem.Text < reports[j].Problem.Text
47 })
48
49 perFile := map[string][]string{}
50 for _, report := range reports {
51 if report.Problem.Severity < cr.minSeverity {
52 continue
53 }
54
55 if _, ok := perFile[report.SourcePath]; !ok {
56 perFile[report.SourcePath] = []string{}
57 }
58
59 var content string
60 if report.Problem.Anchor == checks.AnchorAfter {
61 content, err = readFile(report.SourcePath)
62 if err != nil {
63 return err
64 }
65 }
66
67 path := report.SourcePath
68 if report.SourcePath != report.ReportedPath {
69 path = fmt.Sprintf("%s ~> %s", report.SourcePath, report.ReportedPath)
70 }
71 path = color.CyanString("%s:%s", path, report.Problem.Lines)
72 if report.Problem.Anchor == checks.AnchorBefore {
73 path += " " + color.RedString("(deleted)")
74 }
75 path += " "
76
77 msg := []string{path}
78 switch report.Problem.Severity {
79 case checks.Bug, checks.Fatal:
80 msg = append(msg, color.RedString("%s: %s", report.Problem.Severity, report.Problem.Text))
81 case checks.Warning:
82 msg = append(msg, color.YellowString("%s: %s", report.Problem.Severity, report.Problem.Text))
83 case checks.Information:
84 msg = append(msg, color.HiBlackString("%s: %s", report.Problem.Severity, report.Problem.Text))
85 }
86 msg = append(msg, color.MagentaString(" (%s)\n", report.Problem.Reporter))
87
88 if report.Problem.Anchor == checks.AnchorAfter {
89 lines := strings.Split(content, "\n")
90 lastLine := report.Problem.Lines.Last
91 if lastLine > len(lines)-1 {
92 lastLine = len(lines) - 1
93 slog.Warn(
94 "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",
95 slog.String("path", report.SourcePath),
96 )
97 }
98
99 nrFmt := fmt.Sprintf("%%%dd", countDigits(lastLine)+1)
100 for i := report.Problem.Lines.First; i <= lastLine; i++ {
101 msg = append(msg, color.WhiteString(nrFmt+" | %s\n", i, lines[i-1]))
102 }
103 }
104
105 perFile[report.SourcePath] = append(perFile[report.SourcePath], strings.Join(msg, ""))
106 }
107
108 paths := []string{}
109 for path := range perFile {
110 paths = append(paths, path)
111 }
112 sort.Strings(paths)
113
114 for _, path := range paths {
115 msgs := perFile[path]
116 for _, msg := range msgs {
117 fmt.Fprintln(cr.output, msg)
118 }
119 }
120
121 return nil
122}
123
124func readFile(path string) (string, error) {
125 f, err := os.Open(path)
126 if err != nil {
127 return "", err
128 }
129 defer f.Close()
130
131 content, err := io.ReadAll(f)
132 if err != nil {
133 return "", err
134 }
135
136 return string(content), nil
137}
138
139func countDigits(n int) (c int) {
140 for n > 0 {
141 n /= 10
142 c++
143 }
144 return c
145}
146