all repos

clerk @ a69e534

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
linter: add chronological order, 1 month ago
1
package linter
2
3
import (
4
	"fmt"
5
6
	"olexsmir.xyz/clerk/journal/ast"
7
)
8
9
// OrderDate checks that transactions are in chronological order by date.
10
type OrderDate struct{}
11
12
func (OrderDate) ID() RuleID         { return "orderdate" }
13
func (OrderDate) Severity() Severity { return SeverityWarning }
14
func (r *OrderDate) CheckJournal(j *ast.Journal) []Find {
15
	var finds []Find
16
	var anchor *ast.Date
17
	for _, entry := range j.Entries {
18
		txn, ok := entry.(*ast.Transaction)
19
		if !ok {
20
			continue
21
		}
22
23
		d := txn.Date
24
		if anchor != nil {
25
			cmp := compareDate(d, *anchor)
26
			if cmp < 0 {
27
				finds = append(finds, Find{
28
					Code:     r.ID(),
29
					Severity: r.Severity(),
30
					Message: fmt.Sprintf("transaction is out of chronological order (date %s before %s)",
31
						dateString(d), dateString(*anchor)),
32
					Span: d.Span,
33
				})
34
				continue
35
			}
36
		}
37
38
		anchor = &d
39
	}
40
41
	return finds
42
}
43
44
// compareDate returns -1 if a < b, 0 if equal, 1 if a > b.
45
func compareDate(a, b ast.Date) int {
46
	if a.Year != b.Year {
47
		if a.Year < b.Year {
48
			return -1
49
		}
50
		return 1
51
	}
52
	if a.Month != b.Month {
53
		if a.Month < b.Month {
54
			return -1
55
		}
56
		return 1
57
	}
58
	if a.Day != b.Day {
59
		if a.Day < b.Day {
60
			return -1
61
		}
62
		return 1
63
	}
64
	return 0
65
}
66
67
func dateString(d ast.Date) string {
68
	return fmt.Sprintf("%d-%02d-%02d", d.Year, d.Month, d.Day)
69
}