diff --git a/ledger/file.go b/ledger/file.go index fc34331..cdd8b6b 100644 --- a/ledger/file.go +++ b/ledger/file.go @@ -2,10 +2,10 @@ package ledger import ( "bytes" - "errors" "fmt" "io" "os" + "strconv" "strings" ) @@ -129,7 +129,11 @@ func readTransaction(r io.Reader) (io.Reader, transaction, error) { newR, recipient, err := readTransactionRecipient(r) r = newR if !recipient.empty() { - result.recipients = append(result.recipients, recipient) + if recipient.currency == "" { + result.payee = recipient.name + } else { + result.recipients = append(result.recipients, recipient) + } } if err != nil || recipient.empty() { return r, result, err @@ -226,6 +230,7 @@ func readTransactionRecipient(r io.Reader) (io.Reader, transactionRecipient, err if isSpaceByte(b) && b != '\n' { continue } + r = io.MultiReader(bytes.NewReader([]byte{b}), r) break } @@ -259,5 +264,52 @@ func isNumericByte(b byte) bool { // from "[^\n]*\n?.*" read "[^\n]*\n?" func readTransactionValueCurrency(r io.Reader) (io.Reader, float64, string, error) { - return nil, 0, "", errors.New("not impl read a value currency") + restOfLine := make([]byte, 0, 16) + for { + b, err := readOne(r) + if b == '\n' || err == io.EOF { + break + } + if err != nil { + return r, 0, "", err + } + restOfLine = append(restOfLine, b) + } + restOfLine = bytes.TrimSpace(restOfLine) + if len(restOfLine) == 0 { + return r, 0, "", nil + } + + firstNonNumericIdx := -1 + firstNumericIdx := -1 + for i := range restOfLine { + if !isNumericByte(restOfLine[i]) && firstNonNumericIdx == -1 { + firstNonNumericIdx = i + } + if isNumericByte(restOfLine[i]) && firstNumericIdx == -1 { + firstNumericIdx = i + } + } + + if firstNonNumericIdx == -1 || firstNumericIdx == -1 { + return r, 0, "", fmt.Errorf("needed numeric and non-numeric bytes in %q", restOfLine) + } + + var valueS, currency string + if firstNonNumericIdx < firstNumericIdx { + currency = string(restOfLine[firstNonNumericIdx:firstNumericIdx]) + valueS = string(restOfLine[firstNumericIdx:]) + } else { + valueS = string(restOfLine[firstNumericIdx:firstNonNumericIdx]) + currency = string(restOfLine[firstNonNumericIdx:]) + } + valueS = strings.TrimSpace(valueS) + currency = strings.TrimSpace(currency) + + value, err := strconv.ParseFloat(valueS, 64) + if err != nil { + return r, 0, "", fmt.Errorf("value %q is not a float: %w", valueS, err) + } + + return r, value, currency, nil }