all repos

clerk @ 25c8f80b71674079b26e4189b4030a90fffb8c26

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
ast: store postings as values instead of pointers, 1 month ago
1
package linter
2
3
import (
4
	"olexsmir.xyz/clerk/internal/analyzer"
5
	"olexsmir.xyz/clerk/journal/ast"
6
)
7
8
const MissingCommodityID RuleID = "missing-commodity"
9
10
// MissingCommodity flags amounts with a missing commodity.
11
type MissingCommodity struct{}
12
13
func (MissingCommodity) ID() RuleID { return MissingCommodityID }
14
func (m *MissingCommodity) CheckJournal(an *analyzer.Analysis) []Find {
15
	var finds []Find
16
17
	for _, txn := range an.Transactions {
18
		m.checkPostings(&finds, txn.Postings)
19
	}
20
	for _, ptx := range an.PeriodicTransactions {
21
		m.checkPostings(&finds, ptx.Postings)
22
	}
23
	for _, atx := range an.AutomatedTransactions {
24
		m.checkPostings(&finds, atx.Postings)
25
	}
26
27
	for _, d := range an.Directives {
28
		switch e := d.(type) {
29
		case *ast.ConversionDirective:
30
			m.check(&finds, e.From)
31
			m.check(&finds, e.To)
32
		case *ast.CommodityDirective:
33
			if e.FormatSub != nil && e.FormatSub.Amount.Commodity != "" {
34
				m.check(&finds, e.FormatSub.Amount)
35
			}
36
		case *ast.DefaultCommodityDirective:
37
			m.check(&finds, e.Amount)
38
		case *ast.MarketPriceDirective:
39
			m.check(&finds, e.Amount)
40
		}
41
	}
42
43
	return finds
44
}
45
46
func (m *MissingCommodity) checkPostings(finds *[]Find, postings []ast.Posting) {
47
	for _, posting := range postings {
48
		if posting.Amount == nil {
49
			continue
50
		}
51
		m.check(finds, *posting.Amount)
52
		if posting.Cost != nil {
53
			m.check(finds, posting.Cost.Amount)
54
		}
55
		if posting.Balance != nil {
56
			m.check(finds, posting.Balance.Amount)
57
			if posting.Balance.Cost != nil {
58
				m.check(finds, posting.Balance.Cost.Amount)
59
			}
60
		}
61
	}
62
}
63
64
func (m *MissingCommodity) check(finds *[]Find, am ast.Amount) {
65
	if am.Commodity == "" {
66
		*finds = append(*finds, Find{
67
			Code:    m.ID(),
68
			Message: "amount missing commodity",
69
			Span:    am.Span,
70
		})
71
	}
72
}