aboutsummaryrefslogtreecommitdiffstats
path: root/record/bulder/bulder.go
blob: 18ad792a3df753026caeaa22e2f275a6ffc18621 (plain) (blame)
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
}