diff --git a/plan/command/add.go b/plan/command/add.go index 46491b7..67a22cf 100644 --- a/plan/command/add.go +++ b/plan/command/add.go @@ -44,9 +44,9 @@ var AddCmd = &cli.Command{ }, } -func NewAddCmd(repo storage.EventRepo) *cli.Command { +func NewAddCmd(_ storage.LocalIDRepo, eventRepo storage.EventRepo) *cli.Command { AddCmd.Action = func(cCtx *cli.Context) error { - return Add(cCtx.String("name"), cCtx.String("on"), cCtx.String("at"), cCtx.String("for"), repo) + return Add(cCtx.String("name"), cCtx.String("on"), cCtx.String("at"), cCtx.String("for"), eventRepo) } return AddCmd } diff --git a/plan/command/list.go b/plan/command/list.go index 02b150f..07b8d09 100644 --- a/plan/command/list.go +++ b/plan/command/list.go @@ -13,7 +13,7 @@ var ListCmd = &cli.Command{ Usage: "List everything", } -func NewListCmd(repo storage.EventRepo) *cli.Command { +func NewListCmd(_ storage.LocalIDRepo, repo storage.EventRepo) *cli.Command { ListCmd.Action = NewListAction(repo) return ListCmd } diff --git a/plan/main.go b/plan/main.go index 6dadbc5..d93fa6c 100644 --- a/plan/main.go +++ b/plan/main.go @@ -23,7 +23,7 @@ func main() { os.Exit(1) } - repo, err := storage.NewSqlite(conf.DBPath) + localIDRepo, eventRepo, err := storage.NewSqlites(conf.DBPath) if err != nil { fmt.Printf("could not open db file: %s\n", err) os.Exit(1) @@ -33,8 +33,8 @@ func main() { Name: "plan", Usage: "Plan your day with events", Commands: []*cli.Command{ - command.NewAddCmd(repo), - command.NewListCmd(repo), + command.NewAddCmd(localIDRepo, eventRepo), + command.NewListCmd(localIDRepo, eventRepo), }, } diff --git a/plan/storage/memory.go b/plan/storage/memory.go deleted file mode 100644 index ce08fb7..0000000 --- a/plan/storage/memory.go +++ /dev/null @@ -1,100 +0,0 @@ -package storage - -import ( - "errors" - "sort" - "sync" - - "go-mod.ewintr.nl/planner/item" -) - -type Memory struct { - events map[string]item.Event - localIDs map[int]string - mutex sync.RWMutex -} - -func NewMemory() *Memory { - return &Memory{ - events: make(map[string]item.Event), - localIDs: make(map[int]string), - } -} - -func (r *Memory) Find(id string) (item.Event, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - event, exists := r.events[id] - if !exists { - return item.Event{}, errors.New("event not found") - } - return event, nil -} - -func (r *Memory) FindByLocal(localID int) (item.Event, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - id, exists := r.localIDs[localID] - if !exists { - return item.Event{}, errors.New("event not found") - } - - event, exists := r.events[id] - if !exists { - return item.Event{}, errors.New("id an localid mismatch") - } - return event, nil -} - -func (r *Memory) FindAll() (map[int]string, []item.Event, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - events := make([]item.Event, 0, len(r.events)) - for _, event := range r.events { - events = append(events, event) - } - sort.Slice(events, func(i, j int) bool { - return events[i].ID < events[j].ID - }) - - return r.localIDs, events, nil -} - -func (r *Memory) Store(e item.Event) error { - r.mutex.Lock() - defer r.mutex.Unlock() - - if _, exists := r.events[e.ID]; !exists { - cur := make([]int, 0, len(r.localIDs)) - for i := range r.localIDs { - cur = append(cur, i) - } - localID := NextLocalID(cur) - r.localIDs[localID] = e.ID - } - - r.events[e.ID] = e - - return nil -} - -func (r *Memory) Delete(id string) error { - r.mutex.Lock() - defer r.mutex.Unlock() - - if _, exists := r.events[id]; !exists { - return errors.New("event not found") - } - delete(r.events, id) - - for localID, eventID := range r.localIDs { - if id == eventID { - delete(r.localIDs, localID) - } - } - - return nil -} diff --git a/plan/storage/memory/event.go b/plan/storage/memory/event.go new file mode 100644 index 0000000..486b7f7 --- /dev/null +++ b/plan/storage/memory/event.go @@ -0,0 +1,67 @@ +package memory + +import ( + "errors" + "sort" + "sync" + + "go-mod.ewintr.nl/planner/item" +) + +type MemoryEvent struct { + events map[string]item.Event + mutex sync.RWMutex +} + +func NewMemoryEvent() *MemoryEvent { + return &MemoryEvent{ + events: make(map[string]item.Event), + } +} + +func (r *MemoryEvent) Find(id string) (item.Event, error) { + r.mutex.RLock() + defer r.mutex.RUnlock() + + event, exists := r.events[id] + if !exists { + return item.Event{}, errors.New("event not found") + } + return event, nil +} + +func (r *MemoryEvent) FindAll() ([]item.Event, error) { + r.mutex.RLock() + defer r.mutex.RUnlock() + + events := make([]item.Event, 0, len(r.events)) + for _, event := range r.events { + events = append(events, event) + } + sort.Slice(events, func(i, j int) bool { + return events[i].ID < events[j].ID + }) + + return events, nil +} + +func (r *MemoryEvent) Store(e item.Event) error { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.events[e.ID] = e + + return nil +} + +func (r *MemoryEvent) Delete(id string) error { + r.mutex.Lock() + defer r.mutex.Unlock() + + if _, exists := r.events[id]; !exists { + return errors.New("event not found") + } + delete(r.events, id) + + return nil +} diff --git a/plan/storage/memory_test.go b/plan/storage/memory/event_test.go similarity index 98% rename from plan/storage/memory_test.go rename to plan/storage/memory/event_test.go index f9d7e22..b22dbfb 100644 --- a/plan/storage/memory_test.go +++ b/plan/storage/memory/event_test.go @@ -1,4 +1,4 @@ -package storage +package memory import ( "testing" diff --git a/plan/storage/memory/memory.go b/plan/storage/memory/memory.go new file mode 100644 index 0000000..237284f --- /dev/null +++ b/plan/storage/memory/memory.go @@ -0,0 +1,64 @@ +package memory + +import ( + "sync" + + "github.com/google/uuid" + "go-mod.ewintr.nl/planner/plan/storage" +) + +type MemoryLocalID struct { + ids map[string]int + mutex sync.RWMutex +} + +func NewMemoryLocalID() *MemoryLocalID { + return &MemoryLocalID{ + ids: make(map[string]int), + } +} + +func (ml *MemoryLocalID) FindAll() (map[string]int, error) { + ml.mutex.RLock() + defer ml.mutex.RUnlock() + + return ml.ids, nil +} + +func (ml *MemoryLocalID) Next() (string, int, error) { + ml.mutex.RLock() + defer ml.mutex.RUnlock() + + id := uuid.New().String() + + cur := make([]int, 0, len(ml.ids)) + for _, i := range ml.ids { + cur = append(cur, i) + } + + localID := storage.NextLocalID(cur) + + return id, localID, nil +} + +func (ml *MemoryLocalID) Store(id string, localID int) error { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + ml.ids[id] = localID + + return nil +} + +func (ml *MemoryLocalID) Delete(id string) error { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + if _, ok := ml.ids[id]; !ok { + return ErrNotFound + } + + delete(ml.ids, id) + + return nil +} diff --git a/plan/storage/sqliteevent.go b/plan/storage/sqlite/event.go similarity index 99% rename from plan/storage/sqliteevent.go rename to plan/storage/sqlite/event.go index fa11792..afbca9b 100644 --- a/plan/storage/sqliteevent.go +++ b/plan/storage/sqlite/event.go @@ -1,4 +1,4 @@ -package storage +package sqlite import ( "database/sql" diff --git a/plan/storage/sqlite.go b/plan/storage/sqlite/sqlite.go similarity index 97% rename from plan/storage/sqlite.go rename to plan/storage/sqlite/sqlite.go index b889c1c..c240f26 100644 --- a/plan/storage/sqlite.go +++ b/plan/storage/sqlite/sqlite.go @@ -1,4 +1,4 @@ -package storage +package sqlite import ( "database/sql" @@ -17,6 +17,7 @@ var migrations = []string{ `PRAGMA journal_mode=WAL`, `PRAGMA synchronous=NORMAL`, `PRAGMA cache_size=2000`, + `CREATE TABLE localids ("id" TEXT UNIQUE, "local_id" INTEGER)`, } var ( diff --git a/plan/storage/storage.go b/plan/storage/storage.go index 00087db..b7a9476 100644 --- a/plan/storage/storage.go +++ b/plan/storage/storage.go @@ -1,13 +1,19 @@ package storage import ( + "errors" "sort" "go-mod.ewintr.nl/planner/item" ) +var ( + ErrNotFound = errors.New("not found") +) + type LocalIDRepo interface { FindAll() (map[string]int, error) + Next() (string, int, error) Store(id string, localID int) error Delete(id string) error }