ana-ledger/cmd/cli/main.go

165 lines
3.9 KiB
Go

package cli
import (
"flag"
"fmt"
"io"
"os"
"slices"
"gogs.inhome.blapointe.com/ana-ledger/src/ledger"
)
func Main() {
var config Config
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
fs.Var(&config.Files, "f", "paths to files")
fs.Var(&config.Query.Period, "period", "period")
fs.StringVar(&config.Query.Sort, "S", "", "sort ie date")
fs.BoolVar(&config.Query.NoRounding, "no-rounding", false, "no rounding")
fs.IntVar(&config.Query.Depth, "depth", 0, "depth grouping")
fs.BoolVar(&config.Query.Reverse, "r", false, "reverse printed accounts")
fs.BoolVar(&config.Query.NoExchanging, "no-exchanging", true, "omit currency exchanges")
if err := fs.Parse(os.Args[1:]); err != nil {
panic(err)
}
files := config.Files.Strings()
if len(files) == 0 {
panic("must specify at least one file")
}
ledgerFiles, err := ledger.NewFiles(files[0], files[1:]...)
if err != nil {
panic(err)
}
positional := fs.Args()
if len(positional) == 0 || len(positional[0]) < 3 {
panic("positional arguments required, ie bal|reg PATTERN MATCHING")
}
cmd := positional[0]
likePattern := ""
notLikePattern := ""
i := 1
for i < len(positional) {
switch positional[i] {
case "not":
_ = notLikePattern
panic(positional[i:])
case "and":
panic(positional[i:])
default:
if likePattern == "" {
likePattern = positional[i]
} else {
likePattern = fmt.Sprintf("%s|%s", likePattern, positional[i])
}
}
i += 1
}
deltas, err := ledgerFiles.Deltas()
if err != nil {
panic(err)
}
if period := config.Query.Period; !period.Empty() {
after := period.Start.Format("2006-01-02")
before := period.Stop.Format("2006-01-02")
deltas = deltas.Like(
ledger.LikeAfter(after),
ledger.LikeBefore(before),
)
}
if depth := config.Query.Depth; depth > 0 {
panic(depth)
}
switch cmd[:3] {
case "bal":
balances := deltas.Balances()
if likePattern != "" {
balances = balances.Like(likePattern)
}
if notLikePattern != "" {
balances = balances.NotLike(notLikePattern)
}
FPrintBalances(os.Stdout, "", balances)
case "reg":
transactions := deltas.Transactions()
likes := []ledger.Like{}
if likePattern != "" {
like := ledger.LikeName(likePattern)
transactions = transactions.Like(like)
if config.Query.Reverse {
like = ledger.LikeNot(like)
}
likes = append(likes, like)
}
if notLikePattern != "" {
like := ledger.NotLikeName(notLikePattern)
transactions = transactions.NotLike(like)
if config.Query.Reverse {
like = ledger.LikeNot(like)
}
likes = append(likes, like)
}
for _, transaction := range transactions {
balances := ledger.Deltas(transaction).Like(likes...).Balances()
shouldPrint := false
shouldPrint = shouldPrint || len(balances) > 2
if config.Query.NoExchanging {
shouldPrint = shouldPrint || len(balances) > 1
for _, v := range balances {
shouldPrint = shouldPrint || len(v) == 1
}
} else {
shouldPrint = shouldPrint || len(balances) > 0
}
if shouldPrint {
fmt.Printf("%s\t%s\n", transaction[0].Date, transaction[0].Description)
FPrintBalances(os.Stdout, "\t\t", ledger.Deltas(transaction).Like(likes...).Balances())
}
}
default:
panic("unknown command " + positional[0])
}
}
func FPrintBalances(w io.Writer, linePrefix string, balances ledger.Balances) {
keys := []string{}
for k := range balances {
keys = append(keys, k)
}
slices.Sort(keys)
max := 0
for _, k := range keys {
if n := len(k); n > max {
max = n
}
}
format := fmt.Sprintf("%s%%-%ds\t%%s%%.2f\n", linePrefix, max)
for _, key := range keys {
currencies := []ledger.Currency{}
for currency := range balances[key] {
currencies = append(currencies, currency)
}
slices.Sort(currencies)
for _, currency := range currencies {
printableCurrency := currency
if printableCurrency != "$" {
printableCurrency += " "
}
fmt.Fprintf(w, format, key, printableCurrency, balances[key][currency])
}
}
}