all repos

clerk @ 6fdb9097048e212574439fb0da84d0c94aa7e01b

missing tooling for ledger/hledger

clerk/journal/printer/amount.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
formatter, 17 hours ago
1
package printer
2
3
import (
4
	"strconv"
5
6
	"olexsmir.xyz/clerk/internal/decimal"
7
	"olexsmir.xyz/clerk/journal/ast"
8
)
9
10
func (p *printer) writeAmount(a *ast.Amount, pos CommodityPos) {
11
	if a == nil {
12
		return
13
	}
14
	if a.IsExpr {
15
		p.buf.WriteString(a.Expr)
16
		return
17
	}
18
19
	comm := a.Commodity
20
21
	if comm == "" {
22
		p.writeDecimal(a.Quantity, a.QuantityFmt, 2)
23
		return
24
	}
25
26
	switch pos {
27
	case CommodityBefore:
28
		p.buf.WriteString(comm)
29
		if a.HasSpace {
30
			p.buf.WriteByte(' ')
31
		}
32
		p.writeDecimal(a.Quantity, a.QuantityFmt, 2)
33
	case CommodityAfter:
34
		p.writeDecimal(a.Quantity, a.QuantityFmt, 2)
35
		if a.HasSpace {
36
			p.buf.WriteByte(' ')
37
		}
38
		p.buf.WriteString(comm)
39
	default:
40
		panic("invalid CommodityPos value")
41
	}
42
}
43
44
func (p *printer) writeCost(c *ast.Cost, pos CommodityPos) {
45
	if c == nil {
46
		return
47
	}
48
	if c.IsTotal {
49
		p.buf.WriteString(" @@ ")
50
	} else {
51
		p.buf.WriteString(" @ ")
52
	}
53
	p.writeAmount(&c.Amount, pos)
54
}
55
56
func (p *printer) writeBalanceAssertion(ba *ast.BalanceAssertion, pos CommodityPos) {
57
	if ba == nil {
58
		return
59
	}
60
	p.buf.WriteByte(' ')
61
	switch {
62
	case ba.IsInclusive:
63
		p.buf.WriteString("=== ")
64
	case ba.IsStrict:
65
		p.buf.WriteString("== ")
66
	default:
67
		p.buf.WriteString("= ")
68
	}
69
	p.writeAmount(&ba.Amount, pos)
70
}
71
72
func (p *printer) writeDecimal(d decimal.Decimal, fmt ast.QuantityFormat, forcePrec int) {
73
	if d.IsZero() {
74
		p.writeZero(fmt, forcePrec)
75
		return
76
	}
77
78
	coeff := d.Coeff()
79
	if coeff == nil || coeff.Sign() == 0 {
80
		p.writeZero(fmt, forcePrec)
81
		return
82
	}
83
84
	offset := d.Scale()
85
	neg := coeff.Sign() < 0
86
87
	raw := coeff.String()
88
	if neg {
89
		raw = raw[1:]
90
	}
91
92
	decSep := byte('.')
93
	if fmt.Decimal != 0 {
94
		decSep = fmt.Decimal
95
	}
96
97
	if offset >= len(raw) {
98
		zeros := offset - len(raw) + 1
99
		if neg {
100
			p.buf.WriteByte('-')
101
		}
102
		p.buf.WriteByte('0')
103
		p.buf.WriteByte(decSep)
104
		for i := 0; i < zeros-1; i++ {
105
			p.buf.WriteByte('0')
106
		}
107
		if len(raw) < forcePrec {
108
			p.buf.WriteString(raw)
109
			for i := len(raw); i < forcePrec; i++ {
110
				p.buf.WriteByte('0')
111
			}
112
		} else {
113
			p.buf.WriteString(raw[:forcePrec])
114
		}
115
		return
116
	}
117
118
	split := len(raw) - offset
119
	intPart := raw[:split]
120
	fracPart := raw[split:]
121
122
	intPart = trimLeadingZeros(intPart)
123
	if intPart == "" {
124
		intPart = "0"
125
	}
126
127
	if neg {
128
		p.buf.WriteByte('-')
129
	}
130
131
	if fmt.Thousands != 0 && len(intPart) > 3 {
132
		p.writeThousands(intPart, fmt.Thousands)
133
	} else {
134
		p.buf.WriteString(intPart)
135
	}
136
137
	p.buf.WriteByte(decSep)
138
139
	if len(fracPart) < forcePrec {
140
		p.buf.WriteString(fracPart)
141
		for i := len(fracPart); i < forcePrec; i++ {
142
			p.buf.WriteByte('0')
143
		}
144
	} else {
145
		p.buf.WriteString(fracPart[:forcePrec])
146
	}
147
}
148
149
func (p *printer) writeZero(fmt ast.QuantityFormat, prec int) {
150
	p.buf.WriteByte('0')
151
	sep := "."
152
	if fmt.Decimal != 0 {
153
		sep = string(fmt.Decimal)
154
	}
155
	p.buf.WriteString(sep)
156
	for range prec {
157
		p.buf.WriteByte('0')
158
	}
159
}
160
161
func (p *printer) writeThousands(s string, sep byte) {
162
	n := len(s)
163
	for i := range n {
164
		if i > 0 && (n-i)%3 == 0 {
165
			p.buf.WriteByte(sep)
166
		}
167
		p.buf.WriteByte(s[i])
168
	}
169
}
170
171
func trimLeadingZeros(s string) string {
172
	for i := 0; i < len(s); i++ {
173
		if s[i] != '0' {
174
			return s[i:]
175
		}
176
	}
177
	return ""
178
}
179
180
func quoteString(s string) string {
181
	needsQuote := false
182
	for _, c := range s {
183
		if c == ' ' || c == '\t' || c == '"' || c == ';' || c == '#' {
184
			needsQuote = true
185
			break
186
		}
187
	}
188
	if !needsQuote {
189
		return s
190
	}
191
	return strconv.Quote(s)
192
}