ledger-ui/transaction.go

121 lines
2.6 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"io"
"strconv"
"strings"
)
type Transaction struct {
Date string
Description string
Payer string
Payee string
Amount float32
}
func readTransaction(r io.Reader) (Transaction, error) {
lines := make([][]byte, 3)
for i := range lines {
line, err := nextLine(r)
if err != nil {
return Transaction{}, err
}
lines[i] = line
}
for i := range lines {
if len(lines[i]) == 0 {
return Transaction{}, fmt.Errorf("stub transaction: line %d empty: %q, %q, %q", i, lines[0], lines[1], lines[2])
}
}
var transaction Transaction
for _, foo := range []func([][]byte) error{
transaction.readDate,
transaction.readDescription,
transaction.readAmount,
transaction.readPayerPayee,
} {
if err := foo(lines); err != nil {
return Transaction{}, err
}
}
return transaction, nil
}
func nextLine(r io.Reader) ([]byte, error) {
line, err := readLine(r)
for err == nil && len(bytes.TrimSpace(line)) == 0 {
line, err = readLine(r)
}
return line, err
}
func readLine(r io.Reader) ([]byte, error) {
w := bytes.NewBuffer(make([]byte, 0, 128))
buff := make([]byte, 1)
n, err := r.Read(buff)
for err == nil && n > 0 && buff[0] != '\n' {
w.Write(buff)
n, err = r.Read(buff)
}
return w.Bytes(), err
}
func words(b []byte) [][]byte {
scanner := bufio.NewScanner(bytes.NewReader(b))
scanner.Split(bufio.ScanWords)
words := make([][]byte, 0)
for scanner.Scan() {
words = append(words, []byte(scanner.Text()))
}
return words
}
func (transaction *Transaction) readDate(lines [][]byte) error {
transaction.Date = string(words(lines[0])[0])
return nil
}
func (transaction *Transaction) readDescription(lines [][]byte) error {
transaction.Description = string(bytes.Join(words(lines[0])[1:], []byte(" ")))
return nil
}
func (transaction *Transaction) readAmount(lines [][]byte) error {
amount := string(words(lines[1])[1])
f, err := strconv.ParseFloat(strings.Trim(amount, "$"), 32)
transaction.Amount = float32(f)
return err
}
func (transaction *Transaction) readPayerPayee(lines [][]byte) error {
payer := string(words(lines[1])[0])
payee := string(words(lines[2])[0])
if transaction.Amount >= 0 {
tmp := payer
payer = payee
payee = tmp
} else {
transaction.Amount *= -1
}
transaction.Payer = payer
transaction.Payee = payee
return nil
}
func (transaction Transaction) Marshal() string {
return fmt.Sprintf(
"%-25s%s\n%25s%-50s$%.2f\n%25s%s",
transaction.Date,
transaction.Description,
"",
transaction.Payee,
transaction.Amount,
"",
transaction.Payer,
)
}