all repos

clerk @ fbfbb0b5688aeeeea8d39c44eee36f775995f802

missing tooling for ledger/hledger

clerk/journal/ast/dump.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
linter: add missing transaction status, 1 month ago
1
package ast
2
3
import (
4
	"fmt"
5
	"strings"
6
)
7
8
func Dump(f *Journal) string {
9
	var b strings.Builder
10
	dumpJournal(&b, f)
11
	return b.String()
12
}
13
14
func dumpJournal(b *strings.Builder, f *Journal) {
15
	fmt.Fprintf(b, "Journal\n")
16
	for _, e := range f.Entries {
17
		dumpEntry(b, e, 1)
18
	}
19
	if len(f.Errors) > 0 {
20
		fmt.Fprintf(b, "  Errors\n")
21
		for _, err := range f.Errors {
22
			fmt.Fprintf(b, "    %s: %s\n", err.Span, err.Message)
23
		}
24
	}
25
}
26
27
func dumpEntry(b *strings.Builder, e Entry, depth int) {
28
	switch e := e.(type) {
29
	case *Transaction:
30
		dumpTransaction(b, e, depth)
31
	case *PeriodicTransaction:
32
		dumpPeriodicTransaction(b, e, depth)
33
	case *AutomatedTransaction:
34
		dumpAutomatedTransaction(b, e, depth)
35
	case *BlankLine:
36
		indent(b, depth)
37
		fmt.Fprintf(b, "BlankLine %s\n", e.Span)
38
	case *Comment:
39
		dumpComment(b, e, depth)
40
	case *AccountDirective:
41
		dumpAccountDirective(b, e, depth)
42
	case *CommodityDirective:
43
		dumpCommodityDirective(b, e, depth)
44
	case *IncludeDirective:
45
		indent(b, depth)
46
		fmt.Fprintf(b, "IncludeDirective %s\n", e.Span)
47
		indent(b, depth+1)
48
		fmt.Fprintf(b, "Path: %q\n", e.Path)
49
		dumpOptComment(b, e.Comment, depth+1)
50
	case *AliasDirective:
51
		indent(b, depth)
52
		fmt.Fprintf(b, "AliasDirective %s\n", e.Span)
53
		indent(b, depth+1)
54
		fmt.Fprintf(b, "From: %q\n", e.From)
55
		indent(b, depth+1)
56
		fmt.Fprintf(b, "To: %q\n", e.To)
57
	case *PayeeDirective:
58
		indent(b, depth)
59
		fmt.Fprintf(b, "PayeeDirective %s\n", e.Span)
60
		indent(b, depth+1)
61
		fmt.Fprintf(b, "Name: %q\n", e.Name)
62
		dumpOptComment(b, e.Comment, depth+1)
63
	case *TagDirective:
64
		indent(b, depth)
65
		fmt.Fprintf(b, "TagDirective %s\n", e.Span)
66
		indent(b, depth+1)
67
		fmt.Fprintf(b, "Name: %q\n", e.Name)
68
		dumpOptComment(b, e.Comment, depth+1)
69
	case *YearDirective:
70
		indent(b, depth)
71
		fmt.Fprintf(b, "YearDirective %s\n", e.Span)
72
		indent(b, depth+1)
73
		fmt.Fprintf(b, "Year: %d\n", e.Year)
74
	case *DecimalMarkDirective:
75
		indent(b, depth)
76
		fmt.Fprintf(b, "DecimalMarkDirective %s\n", e.Span)
77
		indent(b, depth+1)
78
		fmt.Fprintf(b, "Mark: %q\n", string(e.Mark))
79
	case *MarketPriceDirective:
80
		dumpMarketPriceDirective(b, e, depth)
81
	case *ConversionDirective:
82
		dumpConversionDirective(b, e, depth)
83
	case *DefaultCommodityDirective:
84
		indent(b, depth)
85
		fmt.Fprintf(b, "DefaultCommodityDirective %s\n", e.Span)
86
		dumpAmount(b, &e.Amount, depth+1)
87
	case *ApplyDirective:
88
		indent(b, depth)
89
		fmt.Fprintf(b, "ApplyDirective %s\n", e.Span)
90
		indent(b, depth+1)
91
		fmt.Fprintf(b, "Expr: %q\n", e.Expr)
92
		dumpOptComment(b, e.Comment, depth+1)
93
	case *EndDirective:
94
		indent(b, depth)
95
		fmt.Fprintf(b, "EndDirective %s\n", e.Span)
96
		indent(b, depth+1)
97
		fmt.Fprintf(b, "Expr: %q\n", e.Expr)
98
		dumpOptComment(b, e.Comment, depth+1)
99
	case *CommentBlockDirective:
100
		indent(b, depth)
101
		fmt.Fprintf(b, "CommentBlockDirective %s\n", e.Span)
102
		indent(b, depth+1)
103
		fmt.Fprintf(b, "Header: %q\n", e.Header)
104
		indent(b, depth+1)
105
		fmt.Fprintf(b, "Content: %q\n", e.Content)
106
		dumpOptComment(b, e.Comment, depth+1)
107
	case *IgnoredDirective:
108
		indent(b, depth)
109
		fmt.Fprintf(b, "IgnoredDirective %s\n", e.Span)
110
		indent(b, depth+1)
111
		fmt.Fprintf(b, "Text: %s\n", e.Text)
112
	default:
113
		indent(b, depth)
114
		fmt.Fprintf(b, "Unknown %T\n", e)
115
	}
116
}
117
118
func indent(b *strings.Builder, depth int) {
119
	b.WriteString(strings.Repeat("  ", depth))
120
}
121
122
func dumpTransaction(b *strings.Builder, t *Transaction, depth int) {
123
	indent(b, depth)
124
	fmt.Fprintf(b, "Transaction %s\n", t.Span)
125
	indent(b, depth+1)
126
	fmt.Fprintf(b, "Date: %s\n", dumpDate(t.Date))
127
	if t.SecondDate != nil {
128
		indent(b, depth+1)
129
		fmt.Fprintf(b, "SecondDate: %s\n", dumpDate(*t.SecondDate))
130
	}
131
	if t.Status.Value != StatusNone {
132
		indent(b, depth+1)
133
		fmt.Fprintf(b, "State: %q\n", t.Status.Value)
134
	}
135
	if t.Code != nil {
136
		indent(b, depth+1)
137
		fmt.Fprintf(b, "Code: %q\n", *t.Code)
138
	}
139
	if t.Payee != nil {
140
		indent(b, depth+1)
141
		fmt.Fprintf(b, "Payee: %q %s\n", t.Payee.Name, t.Payee.Span)
142
	}
143
	if t.Note != nil {
144
		indent(b, depth+1)
145
		fmt.Fprintf(b, "Note: %q\n", *t.Note)
146
	}
147
	dumpOptComment(b, t.Comment, depth+1)
148
	if len(t.HeaderComments) > 0 {
149
		indent(b, depth+1)
150
		fmt.Fprintf(b, "HeaderComments %s\n", t.Span)
151
		for _, c := range t.HeaderComments {
152
			dumpComment(b, c, depth+2)
153
		}
154
	}
155
	for _, p := range t.Postings {
156
		dumpPosting(b, p, depth+1)
157
	}
158
}
159
160
func dumpAutomatedTransaction(b *strings.Builder, t *AutomatedTransaction, depth int) {
161
	indent(b, depth)
162
	fmt.Fprintf(b, "AutomatedTransaction %s\n", t.Span)
163
	indent(b, depth+1)
164
	fmt.Fprintf(b, "Expr: %q\n", t.Expr)
165
	dumpOptComment(b, t.Comment, depth+1)
166
	if len(t.HeaderComments) > 0 {
167
		indent(b, depth+1)
168
		fmt.Fprintf(b, "HeaderComments %s\n", t.Span)
169
		for _, c := range t.HeaderComments {
170
			dumpComment(b, c, depth+2)
171
		}
172
	}
173
	for _, p := range t.Postings {
174
		dumpPosting(b, p, depth+1)
175
	}
176
}
177
178
func dumpPeriodicTransaction(b *strings.Builder, t *PeriodicTransaction, depth int) {
179
	indent(b, depth)
180
	fmt.Fprintf(b, "PeriodicTransaction %s\n", t.Span)
181
	indent(b, depth+1)
182
	fmt.Fprintf(b, "Period: %q\n", t.Period.Raw)
183
	if t.Period.From != nil {
184
		indent(b, depth+1)
185
		fmt.Fprintf(b, "From: %s\n", dumpDate(*t.Period.From))
186
	}
187
	if t.Period.To != nil {
188
		indent(b, depth+1)
189
		fmt.Fprintf(b, "To: %s\n", dumpDate(*t.Period.To))
190
	}
191
	if t.Status.Value != StatusNone {
192
		indent(b, depth+1)
193
		fmt.Fprintf(b, "Status: %q\n", t.Status.Value)
194
	}
195
	if t.Code != nil {
196
		indent(b, depth+1)
197
		fmt.Fprintf(b, "Code: %q\n", *t.Code)
198
	}
199
	if t.Description != nil {
200
		indent(b, depth+1)
201
		fmt.Fprintf(b, "Description: %q\n", *t.Description)
202
	}
203
	dumpOptComment(b, t.Comment, depth+1)
204
	if len(t.HeaderComments) > 0 {
205
		indent(b, depth+1)
206
		fmt.Fprintf(b, "HeaderComments\n")
207
		for _, c := range t.HeaderComments {
208
			dumpComment(b, c, depth+2)
209
		}
210
	}
211
	for _, p := range t.Postings {
212
		dumpPosting(b, p, depth+1)
213
	}
214
}
215
216
func dumpPosting(b *strings.Builder, p *Posting, depth int) {
217
	indent(b, depth)
218
	fmt.Fprintf(b, "Posting %s\n", p.Span)
219
	if p.Type != PostingReal {
220
		indent(b, depth+1)
221
		fmt.Fprintf(b, "Type: %s\n", p.Type)
222
	}
223
	if p.Status.Value != StatusNone {
224
		indent(b, depth+1)
225
		fmt.Fprintf(b, "Status: %q\n", p.Status.Value)
226
	}
227
	dumpAccount(b, p.Account, depth+1)
228
	if p.Amount != nil {
229
		dumpAmount(b, p.Amount, depth+1)
230
	} else {
231
		indent(b, depth+1)
232
		fmt.Fprintf(b, "Amount: <elided>\n")
233
	}
234
	if p.Cost != nil {
235
		dumpCost(b, p.Cost, depth+1)
236
	}
237
	if p.Balance != nil {
238
		dumpBalanceAssertion(b, p.Balance, depth+1)
239
	}
240
	dumpOptComment(b, p.Comment, depth+1)
241
	if len(p.Comments) > 0 {
242
		for _, c := range p.Comments {
243
			dumpComment(b, &c, depth+1)
244
		}
245
	}
246
}
247
248
func dumpAmount(b *strings.Builder, a *Amount, depth int) {
249
	indent(b, depth)
250
	fmt.Fprintf(b, "Amount %s\n", a.Span)
251
	indent(b, depth+1)
252
	fmt.Fprintf(b, "Quantity: %s\n", a.Quantity.String())
253
	indent(b, depth+1)
254
	fmt.Fprintf(b, "Commodity: %q\n", a.Commodity)
255
	indent(b, depth+1)
256
	fmt.Fprintf(b, "CommodityPos: %s\n", a.CommodityPos)
257
	indent(b, depth+1)
258
	fmt.Fprintf(b, "HasSpace: %v\n", a.HasSpace)
259
	if a.IsExpr {
260
		indent(b, depth+1)
261
		fmt.Fprintf(b, "IsExpr: true\n")
262
	}
263
	if a.Expr != "" {
264
		indent(b, depth+1)
265
		fmt.Fprintf(b, "Expr: %q\n", a.Expr)
266
	}
267
	indent(b, depth+1)
268
	fmt.Fprintf(b, "Precision: %d\n", a.QuantityFmt.Precision)
269
	indent(b, depth+1)
270
	fmt.Fprintf(b, "Decimal: %q\n", string(a.QuantityFmt.Decimal))
271
	if a.QuantityFmt.Thousands != 0 {
272
		indent(b, depth+1)
273
		fmt.Fprintf(b, "Thousands: %q\n", string(a.QuantityFmt.Thousands))
274
	}
275
}
276
277
func dumpCost(b *strings.Builder, c *Cost, depth int) {
278
	indent(b, depth)
279
	if c.IsTotal {
280
		fmt.Fprintf(b, "Cost(total) %s\n", c.Span)
281
	} else {
282
		fmt.Fprintf(b, "Cost(unit) %s\n", c.Span)
283
	}
284
	dumpAmount(b, &c.Amount, depth+1)
285
}
286
287
func dumpBalanceAssertion(b *strings.Builder, ba *BalanceAssertion, depth int) {
288
	indent(b, depth)
289
	fmt.Fprintf(b, "BalanceAssertion %s\n", ba.Span)
290
	indent(b, depth+1)
291
	fmt.Fprintf(b, "IsStrict: %v\n", ba.IsStrict)
292
	indent(b, depth+1)
293
	fmt.Fprintf(b, "IsInclusive: %v\n", ba.IsInclusive)
294
	dumpAmount(b, &ba.Amount, depth+1)
295
	if ba.Cost != nil {
296
		dumpCost(b, ba.Cost, depth+1)
297
	}
298
}
299
300
func dumpAccount(b *strings.Builder, a Account, depth int) {
301
	indent(b, depth)
302
	fmt.Fprintf(b, "Account %s\n", a.Span)
303
	for _, sub := range a.Name {
304
		indent(b, depth+1)
305
		fmt.Fprintf(b, "SubAccount: %q %s\n", sub.Name, sub.Span)
306
	}
307
}
308
309
func dumpAccountDirective(b *strings.Builder, a *AccountDirective, depth int) {
310
	indent(b, depth)
311
	fmt.Fprintf(b, "AccountDirective %s\n", a.Span)
312
	dumpAccount(b, a.Account, depth+1)
313
	dumpOptComment(b, a.Comment, depth+1)
314
}
315
316
func dumpCommodityDirective(b *strings.Builder, c *CommodityDirective, depth int) {
317
	indent(b, depth)
318
	fmt.Fprintf(b, "CommodityDirective %s\n", c.Span)
319
	indent(b, depth+1)
320
	fmt.Fprintf(b, "Commodity: %q\n", c.Commodity)
321
	if c.Format.QuantityFmt.Decimal != 0 {
322
		dumpAmount(b, &c.Format, depth+1)
323
	}
324
	dumpOptComment(b, c.Comment, depth+1)
325
}
326
327
func dumpMarketPriceDirective(b *strings.Builder, m *MarketPriceDirective, depth int) {
328
	indent(b, depth)
329
	fmt.Fprintf(b, "MarketPriceDirective %s\n", m.Span)
330
	indent(b, depth+1)
331
	fmt.Fprintf(b, "Date: %s\n", dumpDate(m.DateTime.Date))
332
	if m.DateTime.Time != nil {
333
		indent(b, depth+1)
334
		fmt.Fprintf(b, "Time: %02d:%02d:%02d\n", m.DateTime.Time.Hour, m.DateTime.Time.Minute, m.DateTime.Time.Second)
335
	}
336
	indent(b, depth+1)
337
	fmt.Fprintf(b, "Commodity: %q\n", m.Commodity)
338
	dumpAmount(b, &m.Amount, depth+1)
339
}
340
341
func dumpConversionDirective(b *strings.Builder, c *ConversionDirective, depth int) {
342
	indent(b, depth)
343
	fmt.Fprintf(b, "ConversionDirective %s\n", c.Span)
344
	indent(b, depth+1)
345
	fmt.Fprintf(b, "From:\n")
346
	dumpAmount(b, &c.From, depth+2)
347
	indent(b, depth+1)
348
	fmt.Fprintf(b, "To:\n")
349
	dumpAmount(b, &c.To, depth+2)
350
	dumpOptComment(b, c.Comment, depth+1)
351
}
352
353
func dumpComment(b *strings.Builder, c *Comment, depth int) {
354
	indent(b, depth)
355
	fmt.Fprintf(b, "Comment %s\n", c.Span)
356
	indent(b, depth+1)
357
	fmt.Fprintf(b, "Marker: %q\n", string(c.Marker))
358
	indent(b, depth+1)
359
	fmt.Fprintf(b, "Text: %q\n", c.Text)
360
}
361
362
func dumpOptComment(b *strings.Builder, c *Comment, depth int) {
363
	if c == nil {
364
		return
365
	}
366
	dumpComment(b, c, depth)
367
}
368
369
func dumpDate(d Date) string {
370
	return fmt.Sprintf("%04d%s%02d%s%02d", d.Year, string(d.Sep), d.Month, string(d.Sep), d.Day)
371
}