1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
package bulder
import (
"bufio"
"encoding/csv"
"fmt"
"io"
"strconv"
"strings"
"time"
"github.com/mpolden/journal/record"
)
// Reader implements a reader for Bulder-encoded (CSV) records.
type Reader struct {
rd io.Reader
}
// NewReader returns a new reader for Bulder-encoded records.
func NewReader(rd io.Reader) *Reader {
return &Reader{
rd: rd,
}
}
// Read all records from the underlying reader.
func (r *Reader) Read() ([]record.Record, error) {
buf := bufio.NewReader(r.rd)
c := csv.NewReader(buf)
c.Comma = ';'
var rs []record.Record
line := 0
oldFormat := false
for {
csvRecord, err := c.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
line++
if len(csvRecord) < 12 {
continue
}
if line == 1 {
oldFormat = csvRecord[3] == "Balanse"
continue // Skip header
}
t, err := time.Parse("2006-01-02", csvRecord[0])
if err != nil {
return nil, fmt.Errorf("invalid time on line %d: %q: %w", line, csvRecord[0], err)
}
amountValue := csvRecord[1]
if amountValue == "" {
amountValue = csvRecord[2]
}
amount, err := parseAmount(amountValue)
if err != nil {
return nil, fmt.Errorf("invalid amount on line %d: %q: %w", line, amountValue, err)
}
var balance int64
if balanceValue := csvRecord[3]; oldFormat && balanceValue != "" {
balance, err = parseAmount(balanceValue)
if err != nil {
return nil, fmt.Errorf("invalid balance on line %d: %q: %w", line, balanceValue, err)
}
}
indexOffset := 0
if oldFormat {
indexOffset = 1
}
var text strings.Builder
paymentType := csvRecord[7+indexOffset]
paymentText := csvRecord[8+indexOffset]
text.WriteString(paymentType)
text.WriteString(",")
text.WriteString(paymentText)
category := csvRecord[10]
subCategory := csvRecord[11]
if category != "" {
text.WriteString(",")
text.WriteString(category)
}
if subCategory != "" {
text.WriteString(",")
text.WriteString(subCategory)
}
rs = append(rs, record.Record{Time: t, Text: text.String(), Amount: amount, Balance: balance})
}
return rs, nil
}
func parseAmount(s string) (int64, error) {
v := strings.ReplaceAll(s, ",", "")
n, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, err
}
return n, nil
}
|