all repos

clerk @ 232cfab

missing tooling for ledger/hledger

clerk/format.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
formatter..., 1 month ago
1
package main
2
3
import (
4
	"bytes"
5
	"flag"
6
	"fmt"
7
	"io"
8
	"os"
9
10
	"olexsmir.xyz/clerk/journal"
11
	"olexsmir.xyz/clerk/journal/printer"
12
)
13
14
func runFormat(args []string) {
15
	fs := flag.NewFlagSet("format", flag.ExitOnError)
16
	check := fs.Bool("c", false, "Exit code 0 if already formatted, 1 otherwise")
17
	diff := fs.Bool("d", false, "Display diffs instead of rewriting files")
18
	list := fs.Bool("l", false, "List files whose formatting differs")
19
	write := fs.Bool("w", false, "Write result back to file instead of stdout")
20
	fs.Usage = func() {
21
		fmt.Fprintf(os.Stderr, "Usage: clerk format [flags] [path ...]\n")
22
		fs.PrintDefaults()
23
	}
24
	fs.Parse(args)
25
26
	paths := fs.Args()
27
28
	// Read from stdin if no paths given
29
	if len(paths) == 0 {
30
		src, err := io.ReadAll(os.Stdin)
31
		if err != nil {
32
			fmt.Fprintf(os.Stderr, "error reading stdin: %v\n", err)
33
			os.Exit(1)
34
		}
35
36
		pf, err := journal.NewLoader().LoadBytes("stdin", src)
37
		if err != nil {
38
			fmt.Fprintf(os.Stderr, "parse error: %v\n", err)
39
			os.Exit(1)
40
		}
41
		if len(pf.Errors) > 0 {
42
			fmt.Fprintf(os.Stderr, "parse error: %v\n", pf.Errors[0].Message)
43
			os.Exit(1)
44
		}
45
46
		var buf bytes.Buffer
47
		if err := printer.Fprint(&buf, pf.Ast); err != nil {
48
			fmt.Fprintf(os.Stderr, "format error: %v\n", err)
49
			os.Exit(1)
50
		}
51
52
		formatted := buf.Bytes()
53
54
		switch {
55
		case *check:
56
			if bytes.Equal(src, formatted) {
57
				os.Exit(0)
58
			}
59
			os.Exit(1)
60
		case *diff:
61
			diffLines("stdin", src, formatted)
62
		default:
63
			os.Stdout.Write(formatted)
64
		}
65
		return
66
	}
67
68
	// Process each file
69
	exitCode := 0
70
	for _, path := range paths {
71
		info, err := os.Stat(path)
72
		if err != nil {
73
			fmt.Fprintf(os.Stderr, "error: %s: %v\n", path, err)
74
			exitCode = 1
75
			continue
76
		}
77
		if info.IsDir() {
78
			fmt.Fprintf(os.Stderr, "error: %s: is a directory\n", path)
79
			exitCode = 1
80
			continue
81
		}
82
83
		src, err := os.ReadFile(path)
84
		if err != nil {
85
			fmt.Fprintf(os.Stderr, "error: %s: %v\n", path, err)
86
			exitCode = 1
87
			continue
88
		}
89
90
		pf, err := journal.NewLoader().LoadBytes(path, src)
91
		if err != nil {
92
			fmt.Fprintf(os.Stderr, "error: %s: %v\n", path, err)
93
			exitCode = 1
94
			continue
95
		}
96
		if len(pf.Errors) > 0 || len(pf.FileErrors) > 0 {
97
			fmt.Fprintf(os.Stderr, "error: %s: has errors, refusing to format\n", path)
98
			exitCode = 1
99
			continue
100
		}
101
102
		var buf bytes.Buffer
103
		if err := printer.Fprint(&buf, pf.Ast); err != nil {
104
			fmt.Fprintf(os.Stderr, "error: %s: %v\n", path, err)
105
			exitCode = 1
106
			continue
107
		}
108
109
		formatted := buf.Bytes()
110
		changed := !bytes.Equal(src, formatted)
111
112
		switch {
113
		case *check:
114
			if changed {
115
				fmt.Fprintf(os.Stderr, "%s: not formatted\n", path)
116
				exitCode = 1
117
			}
118
		case *list:
119
			if changed {
120
				fmt.Println(path)
121
			}
122
		case *diff:
123
			if changed {
124
				diffLines(path, src, formatted)
125
			}
126
		case *write:
127
			if changed {
128
				if err := os.WriteFile(path, formatted, 0o644); err != nil {
129
					fmt.Fprintf(os.Stderr, "error: %s: %v\n", path, err)
130
					exitCode = 1
131
				}
132
			}
133
		default:
134
			if _, err := os.Stdout.Write(formatted); err != nil {
135
				fmt.Fprintf(os.Stderr, "error writing stdout: %v\n", err)
136
				exitCode = 1
137
			}
138
		}
139
	}
140
	os.Exit(exitCode)
141
}
142
143
func diffLines(path string, src, formatted []byte) {
144
	fmt.Printf("--- %s\n+++ %s\n", path, path)
145
	srcLines := bytes.Split(src, []byte("\n"))
146
	fmtLines := bytes.Split(formatted, []byte("\n"))
147
	lines := max(len(fmtLines), len(srcLines))
148
	for i := range lines {
149
		var sLine, fLine []byte
150
		if i < len(srcLines) {
151
			sLine = srcLines[i]
152
		}
153
		if i < len(fmtLines) {
154
			fLine = fmtLines[i]
155
		}
156
		if !bytes.Equal(sLine, fLine) {
157
			if len(sLine) > 0 {
158
				fmt.Printf("-%s\n", sLine)
159
			} else {
160
				fmt.Println("-")
161
			}
162
			if len(fLine) > 0 {
163
				fmt.Printf("+%s\n", fLine)
164
			} else {
165
				fmt.Println("+")
166
			}
167
		}
168
	}
169
}