all repos

clerk @ a69e534d08842f6663eecddd5380d88cf40d2096

missing tooling for ledger/hledger

clerk/internal/linter/report.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
linter: report all output in one json array (for json output), 1 month ago
1
package linter
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
	"io"
8
	"os"
9
	"path/filepath"
10
)
11
12
// PathStyle controls how file paths are shown in output.
13
type PathStyle int
14
15
const (
16
	PathBasename PathStyle = iota // just the filename (e.g. "entry.journal")
17
	PathAbsolute                  // full absolute path
18
	PathRelative                  // relative to current directory
19
)
20
21
// Fprint writes finds in text format: file:line:col code: message.
22
func Fprint(w io.Writer, style PathStyle, finds []Find) {
23
	for _, find := range finds {
24
		fmt.Fprintf(w, "%s:%d:%d: %s: %s\n",
25
			formatPath(style, find.Span.Start.File),
26
			find.Span.Start.Line, find.Span.Start.Col,
27
			find.Code, find.Message)
28
	}
29
}
30
31
type FindJSON struct {
32
	Message  string `json:"message"`
33
	Severity string `json:"severity"`
34
	Code     string `json:"code"`
35
	File     string `json:"file"`
36
	Line     int    `json:"line"`
37
	Column   int    `json:"column"`
38
}
39
40
// FprintJSON writes finds as [FindJSON] array.
41
func FprintJSON(w io.Writer, style PathStyle, finds []Find) error {
42
	jsonFinds := make([]FindJSON, len(finds))
43
	for i, find := range finds {
44
		jsonFinds[i] = FindJSON{
45
			Message:  find.Message,
46
			Severity: find.Severity.String(),
47
			Code:     string(find.Code),
48
			File:     formatPath(style, find.Span.Start.File),
49
			Line:     find.Span.Start.Line,
50
			Column:   find.Span.Start.Col,
51
		}
52
	}
53
	return json.NewEncoder(w).Encode(jsonFinds)
54
}
55
56
// Reporter collects lint findings across files and flushes them in the desired format.
57
type Reporter struct {
58
	w     io.Writer
59
	finds []Find
60
	style PathStyle
61
}
62
63
func NewReporter(w io.Writer, style PathStyle) *Reporter {
64
	return &Reporter{w: w, style: style}
65
}
66
67
func (r *Reporter) Collect(finds []Find) {
68
	r.finds = append(r.finds, finds...)
69
}
70
71
func (r *Reporter) Flush(format string) error {
72
	switch format {
73
	case "json":
74
		return FprintJSON(r.w, r.style, r.finds)
75
	case "text":
76
		Fprint(r.w, r.style, r.finds)
77
		return nil
78
	default:
79
		return errors.New("unsupported format")
80
	}
81
}
82
83
func formatPath(style PathStyle, p string) string {
84
	switch style {
85
	case PathBasename:
86
		return filepath.Base(p)
87
	case PathAbsolute:
88
		return p
89
	case PathRelative:
90
		wd, err := os.Getwd()
91
		if err == nil {
92
			if rel, err := filepath.Rel(wd, p); err == nil {
93
				return rel
94
			}
95
		}
96
		return p
97
	default:
98
		panic("impossible PathStyle value")
99
	}
100
}