package ledger import ( "errors" "fmt" "io" "os" "time" ) type File string func NewFile(p string) (File, error) { f := File(p) _, err := f.Deltas() return f, err } func (file File) Deltas(like ...Like) ([]Delta, error) { transactions, err := file.transactions() if err != nil { return nil, err } result := make([]Delta, 0, len(transactions)*2) for _, transaction := range transactions { sums := map[string]float64{} for _, acc := range transaction.recipients { sums[acc.currency] += acc.value delta := newDelta( transaction.date, transaction.description, acc.name, acc.value, acc.currency, ) if likes(like).all(delta) { result = append(result, delta) } } for currency, value := range sums { if value == 0 { continue } if transaction.payee == "" { return nil, fmt.Errorf("didnt find net zero and no dumping ground payee set: %+v", transaction) } delta := newDelta( transaction.date, transaction.description, transaction.payee, value, currency, ) if likes(like).all(delta) { result = append(result, delta) } } } return result, nil } type transaction struct { date time.Time description string payee string recipients []transactionRecipient } type transactionRecipient struct { name string value float64 currency string } func (t transaction) empty() bool { return fmt.Sprint(t) == fmt.Sprint(transaction{}) } func (file File) transactions() ([]transaction, error) { f, err := os.Open(string(file)) if err != nil { return nil, err } defer f.Close() result := make([]transaction, 0) for { one, err := readTransaction(f) if !one.empty() { result = append(result, one) } if errors.Is(err, io.EOF) { return result, nil } if err != nil { return result, err } } } func readTransaction(r io.Reader) (transaction, error) { return transaction{}, io.EOF }