ledger-ui/ledger.go

98 lines
2.0 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
)
type Ledger struct {
path string
}
func NewLedger(path string) (Ledger, error) {
info, err := os.Stat(path)
if err == nil && info.IsDir() {
return Ledger{}, errors.New("path is dir")
}
return Ledger{
path: path,
}, err
}
func (ledger Ledger) SetTransaction(i int, transaction Transaction) error {
transactions, err := ledger.Transactions()
if err != nil {
return err
}
if i >= len(transactions) {
return errors.New("i too high")
}
transactions[i] = transaction
return ledger.SetTransactions(transactions)
}
func (ledger Ledger) SetTransactions(transactions []Transaction) error {
f, err := ioutil.TempFile(os.TempDir(), path.Base(ledger.path)+".*")
if err != nil {
return err
}
for _, transaction := range transactions {
if _, err := fmt.Fprintf(f, "%s\n", transaction.Marshal()); err != nil {
return err
}
}
if err := f.Close(); err != nil {
return err
}
return os.Rename(f.Name(), ledger.path)
}
func (ledger Ledger) Balances() (Balances, error) {
transactions, err := ledger.Transactions()
if err != nil {
return nil, err
}
balances := make(Balances)
for _, transaction := range transactions {
balances.Add(transaction.Payee, transaction.Amount)
balances.Add(transaction.Payer, -1*transaction.Amount)
}
return balances, nil
}
func (ledger Ledger) Transactions() ([]Transaction, error) {
f, err := ledger.open()
if err != nil {
return nil, err
}
defer f.Close()
transactions := make([]Transaction, 0)
for err == nil {
var transaction Transaction
transaction, err = readTransaction(f)
if err == io.EOF {
} else if err == nil {
transactions = append(transactions, transaction)
} else {
err = fmt.Errorf("error parsing transaction #%d: %v", len(transactions), err)
}
}
if err == io.EOF {
err = nil
}
return transactions, err
}
func (ledger Ledger) open() (io.ReadCloser, error) {
f, err := os.Open(ledger.path)
if os.IsNotExist(err) {
return ioutil.NopCloser(bytes.NewReader([]byte{})), nil
}
return f, err
}