Compare commits
7 Commits
8ab5a0edf5
...
4831914251
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4831914251 | ||
|
|
eb3af4b54f | ||
|
|
849a8696f5 | ||
|
|
1a2c88687f | ||
|
|
889dc48d6c | ||
|
|
881162357b | ||
|
|
62e65c47df |
@@ -3,7 +3,7 @@ package cli
|
||||
type Config struct {
|
||||
Files FileList
|
||||
Query struct {
|
||||
Period string
|
||||
Period Period
|
||||
Sort string
|
||||
NoRounding bool
|
||||
Depth int
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FlagStringArray []string
|
||||
@@ -33,3 +34,35 @@ func (fileList *FileList) Set(s string) error {
|
||||
}
|
||||
return (*FlagStringArray)(fileList).Set(s)
|
||||
}
|
||||
|
||||
type Period struct {
|
||||
Start time.Time
|
||||
Stop time.Time
|
||||
}
|
||||
|
||||
func (period Period) Empty() bool {
|
||||
return period.Stop.Sub(period.Start) == 0
|
||||
}
|
||||
|
||||
func (period *Period) String() string {
|
||||
return fmt.Sprintf("%s..%s", period.Start, period.Stop)
|
||||
}
|
||||
|
||||
func (period *Period) Set(s string) error {
|
||||
if result, err := time.Parse("2006", s); err == nil {
|
||||
period.Start = result
|
||||
period.Stop = result.AddDate(1, 0, 0).Add(-1 * time.Minute)
|
||||
return nil
|
||||
}
|
||||
if result, err := time.Parse("2006-01", s); err == nil {
|
||||
period.Start = result
|
||||
period.Stop = result.AddDate(0, 1, 0).Add(-1 * time.Minute)
|
||||
return nil
|
||||
}
|
||||
if result, err := time.Parse("2006-01-02", s); err == nil {
|
||||
period.Start = result
|
||||
period.Stop = result.AddDate(0, 0, 1).Add(-1 * time.Minute)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unimplemented format: %s", s)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cli
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
@@ -14,7 +15,7 @@ func Main() {
|
||||
|
||||
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
fs.Var(&config.Files, "f", "paths to files")
|
||||
fs.StringVar(&config.Query.Period, "period", "", "period")
|
||||
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")
|
||||
@@ -37,18 +38,38 @@ func Main() {
|
||||
}
|
||||
cmd := positional[0]
|
||||
|
||||
likes := []ledger.Like{}
|
||||
for _, query := range positional[1:] {
|
||||
panic(query)
|
||||
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(likes...)
|
||||
deltas, err := ledgerFiles.Deltas()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if period := config.Query.Period; period != "" {
|
||||
panic(period)
|
||||
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 {
|
||||
@@ -58,7 +79,40 @@ func Main() {
|
||||
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":
|
||||
register := deltas.Register()
|
||||
if likePattern != "" {
|
||||
register = register.Like(likePattern)
|
||||
}
|
||||
if notLikePattern != "" {
|
||||
register = register.NotLike(notLikePattern)
|
||||
}
|
||||
|
||||
var prev ledger.Balances
|
||||
for _, date := range register.Dates() {
|
||||
balances := register[date]
|
||||
|
||||
if newBalances := balances.Sub(prev).Nonzero(); len(newBalances) > 0 {
|
||||
fmt.Println(date)
|
||||
FPrintBalances(os.Stdout, newBalances)
|
||||
}
|
||||
|
||||
prev = balances
|
||||
}
|
||||
default:
|
||||
panic("unknown command " + positional[0])
|
||||
}
|
||||
}
|
||||
|
||||
func FPrintBalances(w io.Writer, balances ledger.Balances) {
|
||||
keys := []string{}
|
||||
for k := range balances {
|
||||
keys = append(keys, k)
|
||||
@@ -74,16 +128,18 @@ func Main() {
|
||||
|
||||
format := fmt.Sprintf("%%-%ds\t%%s%%.2f\n", max)
|
||||
for _, key := range keys {
|
||||
for currency, v := range balances[key] {
|
||||
if currency != "$" {
|
||||
currency += " "
|
||||
currencies := []ledger.Currency{}
|
||||
for currency := range balances[key] {
|
||||
currencies = append(currencies, currency)
|
||||
}
|
||||
fmt.Printf(format, key, currency, v)
|
||||
slices.Sort(currencies)
|
||||
|
||||
for _, currency := range currencies {
|
||||
printableCurrency := currency
|
||||
if printableCurrency != "$" {
|
||||
printableCurrency += " "
|
||||
}
|
||||
fmt.Fprintf(w, format, key, printableCurrency, balances[key][currency])
|
||||
}
|
||||
}
|
||||
case "reg":
|
||||
// register := deltas.Register()
|
||||
default:
|
||||
panic("unknown command " + positional[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,30 @@ type Balances map[string]Balance
|
||||
|
||||
type Balance map[Currency]float64
|
||||
|
||||
func (balances Balances) Sub(other Balances) Balances {
|
||||
result := make(Balances)
|
||||
for k, v := range balances {
|
||||
result[k] = v.Sub(other[k])
|
||||
}
|
||||
for k, v := range other {
|
||||
if _, ok := balances[k]; ok {
|
||||
continue
|
||||
}
|
||||
result[k] = v.Invert()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (balances Balances) Nonzero() Balances {
|
||||
result := make(Balances)
|
||||
for k, v := range balances {
|
||||
if nonzero := v.Nonzero(); len(nonzero) > 0 {
|
||||
result[k] = nonzero
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (balances Balances) NotLike(pattern string) Balances {
|
||||
result := make(Balances)
|
||||
p := regexp.MustCompile(pattern)
|
||||
@@ -98,6 +122,39 @@ func (balances Balances) Push(d Delta) {
|
||||
balances[d.Name].Push(d)
|
||||
}
|
||||
|
||||
func (balance Balance) Sub(other Balance) Balance {
|
||||
return balance.Sum(other.Invert())
|
||||
}
|
||||
|
||||
func (balance Balance) Sum(other Balance) Balance {
|
||||
result := make(Balance)
|
||||
for k, v := range balance {
|
||||
result[k] += v
|
||||
}
|
||||
for k, v := range other {
|
||||
result[k] += v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (balance Balance) Nonzero() Balance {
|
||||
result := make(Balance)
|
||||
for k, v := range balance {
|
||||
if v != 0 {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (balance Balance) Invert() Balance {
|
||||
result := make(Balance)
|
||||
for k, v := range balance {
|
||||
result[k] = v * -1.0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (balance Balance) Push(d Delta) {
|
||||
if _, ok := balance[d.Currency]; !ok {
|
||||
balance[d.Currency] = 0
|
||||
|
||||
@@ -51,6 +51,26 @@ func (register Register) Names() []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func (register Register) Like(pattern string) Register {
|
||||
result := make(Register)
|
||||
for k, v := range register {
|
||||
if got := v.Like(pattern); len(got) > 0 {
|
||||
result[k] = got
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (register Register) NotLike(pattern string) Register {
|
||||
result := make(Register)
|
||||
for k, v := range register {
|
||||
if got := v.NotLike(pattern); len(got) > 0 {
|
||||
result[k] = got
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (register Register) Dates() []string {
|
||||
result := make([]string, 0, len(register))
|
||||
for k := range register {
|
||||
|
||||
Reference in New Issue
Block a user