From fc95242c94bf433e405751408b5852dcce314da8 Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Thu, 6 Jan 2022 22:13:22 -0500 Subject: [PATCH] add pttodo export file option --- config.go | 4 ++- upload.go | 43 +++++++++++++++++++++++++++ upload_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index 8421b2a..73186ed 100755 --- a/config.go +++ b/config.go @@ -17,11 +17,13 @@ type Uploader int const ( UploaderTodo = Uploader(iota) UploaderLedger + UploaderPTTodo ) var uploaders = map[string]Uploader{ "todo": UploaderTodo, "ledger": UploaderLedger, + "pttodo": UploaderPTTodo, } type Config struct { @@ -48,7 +50,7 @@ func NewConfig() Config { as.Append(args.STRING, "emailpass", "email password", "diblloewfncwssof") as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993") - as.Append(args.STRING, "uploader", "todo, ledger", "todo") + as.Append(args.STRING, "uploader", "todo, ledger, pttodo", "todo") as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com") as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y") diff --git a/upload.go b/upload.go index f575dce..10a0b82 100755 --- a/upload.go +++ b/upload.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "errors" "fmt" "io/ioutil" @@ -8,10 +9,13 @@ import ( "net/http" "net/url" "os" + "path" "regexp" "strconv" "strings" "time" + + yaml "gopkg.in/yaml.v2" ) func Upload(config Config, transaction *Transaction) error { @@ -20,6 +24,8 @@ func Upload(config Config, transaction *Transaction) error { return uploadTodo(config, transaction) case UploaderLedger: return uploadLedger(config, transaction) + case UploaderPTTodo: + return uploadPTTodo(config, transaction) default: return errors.New("not impl: uploader") } @@ -49,6 +55,43 @@ func uploadTodo(config Config, transaction *Transaction) error { return nil } +func uploadPTTodo(config Config, transaction *Transaction) error { + b, err := ioutil.ReadFile(config.TodoAddr) + if os.IsNotExist(err) { + b = []byte("todo:\n") + } else if err != nil { + return err + } else if len(b) == 0 { + b = []byte("todo:\n") + } + f, err := ioutil.TempFile(os.TempDir(), path.Base(config.TodoAddr)) + if err != nil { + return err + } + defer f.Close() + sep := []byte{'\n'} + seek := []byte("todo:") + for len(b) > 0 { + idx := bytes.Index(b, sep) + if idx == -1 { + idx = len(b) - 1 + } + fmt.Fprintf(f, "%s\n", b[:idx]) + if bytes.Equal(bytes.TrimSpace(b[:idx]), seek) { + fmt.Fprintf(f, `- {"todo":%q, "tags":%q}%s`, transaction.Format(), config.TodoTag, "\n") + } + b = b[idx+1:] + } + f.Close() + var v interface{} + if b, err := ioutil.ReadFile(f.Name()); err != nil { + return err + } else if err := yaml.Unmarshal(b, &v); err != nil { + return err + } + return os.Rename(f.Name(), config.TodoAddr) +} + func uploadLedger(config Config, transaction *Transaction) error { f, err := os.OpenFile(config.TodoAddr, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { diff --git a/upload_test.go b/upload_test.go index 97bd0c9..a29d5be 100644 --- a/upload_test.go +++ b/upload_test.go @@ -4,12 +4,92 @@ import ( "bytes" "io/ioutil" "local/storage" + "os" "path" "testing" "github.com/google/uuid" ) +func TestUploadPTTodo(t *testing.T) { + addr := path.Join(t.TempDir(), "test.upload.pttodo") + config := Config{TodoAddr: addr, TodoTag: "expense"} + xaction := func() *Transaction { + return &Transaction{ + ID: "id", + Bank: UCCU, + Amount: "1.23", + Vendor: "vendor vendor", + Date: "today", + } + } + t.Run("full file", func(t *testing.T) { + if err := ioutil.WriteFile(addr, []byte(` +todo: +- first +- todo: second +scheduled: [] +done: [] + `), os.ModePerm); err != nil { + t.Fatal(err) + } + err := uploadPTTodo(config, xaction()) + if err != nil { + t.Error(err) + } + b, err := ioutil.ReadFile(addr) + if err != nil { + t.Error(err) + } + if bytes.Compare(bytes.TrimSpace(b), bytes.TrimSpace([]byte(` +todo: +- {"todo":"(today) /UCCU: 1.23 @ vendor vendor", "tags":"expense"} +- first +- todo: second +scheduled: [] +done: [] + `))) != 0 { + t.Errorf("full file came out wrong: got %s", b) + } + if !bytes.Contains(b, []byte(xaction().Format())) { + t.Errorf("full file didnt get target: %s", string(b)) + } + t.Logf("%s", b) + }) + t.Run("no file", func(t *testing.T) { + os.Remove(addr) + err := uploadPTTodo(config, xaction()) + if err != nil { + t.Error(err) + } + b, err := ioutil.ReadFile(addr) + if err != nil { + t.Error(err) + } + if !bytes.Contains(b, []byte(xaction().Format())) { + t.Errorf("no file didnt get target: %s", string(b)) + } + t.Logf("%s", b) + }) + t.Run("empty file", func(t *testing.T) { + if err := ioutil.WriteFile(addr, []byte{}, os.ModePerm); err != nil { + t.Fatal(err) + } + err := uploadPTTodo(config, xaction()) + if err != nil { + t.Error(err) + } + b, err := ioutil.ReadFile(addr) + if err != nil { + t.Error(err) + } + if !bytes.Contains(b, []byte(xaction().Format())) { + t.Errorf("empty file didnt get target: %s", string(b)) + } + t.Logf("%s", b) + }) +} + func TestUploadLedger(t *testing.T) { cases := map[string]struct { transaction Transaction