all repos

clerk @ master

missing tooling for ledger/hledger

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

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