diff --git a/dist/plan b/dist/plan index fe46657..84b7512 100755 Binary files a/dist/plan and b/dist/plan differ diff --git a/item/task.go b/item/task.go index 31fbfa0..556b167 100644 --- a/item/task.go +++ b/item/task.go @@ -91,12 +91,6 @@ func (t Task) Valid() bool { if t.Title == "" { return false } - if t.Date.IsZero() { - return false - } - if t.Duration.Seconds() < 1 { - return false - } return true } diff --git a/item/task_test.go b/item/task_test.go index c1ca7b2..664bf75 100644 --- a/item/task_test.go +++ b/item/task_test.go @@ -167,28 +167,6 @@ func TestTaskValidate(t *testing.T) { }, }, }, - { - name: "no date", - tsk: item.Task{ - ID: "a", - TaskBody: item.TaskBody{ - Title: "title", - Time: item.NewTime(8, 0), - Duration: oneHour, - }, - }, - }, - { - name: "no duration", - tsk: item.Task{ - ID: "a", - Date: item.NewDate(2024, 9, 20), - TaskBody: item.TaskBody{ - Title: "title", - Time: item.NewTime(8, 0), - }, - }, - }, { name: "valid", tsk: item.Task{ diff --git a/plan/command/add.go b/plan/command/add.go index 2e2f694..ac291b2 100644 --- a/plan/command/add.go +++ b/plan/command/add.go @@ -11,16 +11,16 @@ import ( type AddArgs struct { fieldTPL map[string][]string - task item.Task + Task item.Task } func NewAddArgs() AddArgs { return AddArgs{ fieldTPL: map[string][]string{ - "date": []string{"d", "date", "on"}, - "time": []string{"t", "time", "at"}, - "duration": []string{"dur", "duration", "for"}, - "recurrer": []string{"rec", "recurrer"}, + "date": {"d", "date", "on"}, + "time": {"t", "time", "at"}, + "duration": {"dur", "duration", "for"}, + "recurrer": {"rec", "recurrer"}, }, } } @@ -41,7 +41,7 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error tsk := item.Task{ ID: uuid.New().String(), TaskBody: item.TaskBody{ - Title: strings.Join(main, ","), + Title: strings.Join(main, " "), }, } @@ -76,18 +76,18 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error } return &Add{ - args: AddArgs{ - task: tsk, + Args: AddArgs{ + Task: tsk, }, }, nil } type Add struct { - args AddArgs + Args AddArgs } -func (a *Add) Do(deps Dependencies) ([][]string, error) { - if err := deps.TaskRepo.Store(a.args.task); err != nil { +func (a *Add) Do(deps Dependencies) (CommandResult, error) { + if err := deps.TaskRepo.Store(a.Args.Task); err != nil { return nil, fmt.Errorf("could not store event: %v", err) } @@ -95,11 +95,11 @@ func (a *Add) Do(deps Dependencies) ([][]string, error) { if err != nil { return nil, fmt.Errorf("could not create next local id: %v", err) } - if err := deps.LocalIDRepo.Store(a.args.task.ID, localID); err != nil { + if err := deps.LocalIDRepo.Store(a.Args.Task.ID, localID); err != nil { return nil, fmt.Errorf("could not store local id: %v", err) } - it, err := a.args.task.Item() + it, err := a.Args.Task.Item() if err != nil { return nil, fmt.Errorf("could not convert event to sync item: %v", err) } @@ -109,3 +109,10 @@ func (a *Add) Do(deps Dependencies) ([][]string, error) { return nil, nil } + +type AddRender struct { +} + +func (ar AddRender) Render() string { + return "stored task" +} diff --git a/plan/command/add_test.go b/plan/command/add_test.go index faeb2f8..0937cfb 100644 --- a/plan/command/add_test.go +++ b/plan/command/add_test.go @@ -6,6 +6,7 @@ import ( "go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/plan/command" + "go-mod.ewintr.nl/planner/plan/storage" "go-mod.ewintr.nl/planner/plan/storage/memory" ) @@ -56,9 +57,12 @@ func TestAdd(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + // setup taskRepo := memory.NewTask() localIDRepo := memory.NewLocalID() syncRepo := memory.NewSync() + + // parse cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields) if tc.expErr != (actParseErr != nil) { t.Errorf("exp %v, got %v", tc.expErr, actParseErr) @@ -66,6 +70,8 @@ func TestAdd(t *testing.T) { if tc.expErr { return } + + // do if _, err := cmd.Do(command.Dependencies{ TaskRepo: taskRepo, LocalIDRepo: localIDRepo, @@ -74,7 +80,8 @@ func TestAdd(t *testing.T) { t.Errorf("exp nil, got %v", err) } - actTasks, err := taskRepo.FindAll() + // check + actTasks, err := taskRepo.FindMany(storage.TaskListParams{}) if err != nil { t.Errorf("exp nil, got %v", err) } diff --git a/plan/command/command.go b/plan/command/command.go index b8715fc..da371a9 100644 --- a/plan/command/command.go +++ b/plan/command/command.go @@ -6,7 +6,6 @@ import ( "slices" "strings" - "go-mod.ewintr.nl/planner/plan/format" "go-mod.ewintr.nl/planner/plan/storage" "go-mod.ewintr.nl/planner/sync/client" ) @@ -33,7 +32,11 @@ type CommandArgs interface { } type Command interface { - Do(deps Dependencies) ([][]string, error) + Do(deps Dependencies) (CommandResult, error) +} + +type CommandResult interface { + Render() string } type CLI struct { @@ -63,18 +66,12 @@ func (cli *CLI) Run(args []string) error { return err } - data, err := cmd.Do(cli.deps) + result, err := cmd.Do(cli.deps) if err != nil { return err } + fmt.Println(result.Render()) - switch { - case len(data) == 0: - case len(data) == 1 && len(data[0]) == 1: - fmt.Println(data[0][0]) - default: - fmt.Printf("\n%s\n", format.Table(data)) - } return nil } diff --git a/plan/command/delete.go b/plan/command/delete.go index 7017323..9d23bb3 100644 --- a/plan/command/delete.go +++ b/plan/command/delete.go @@ -24,24 +24,24 @@ func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, err } return &Delete{ - args: DeleteArgs{ + Args: DeleteArgs{ LocalID: localID, }, }, nil } type Delete struct { - args DeleteArgs + Args DeleteArgs } -func (del *Delete) Do(deps Dependencies) ([][]string, error) { +func (del *Delete) Do(deps Dependencies) (CommandResult, error) { var id string idMap, err := deps.LocalIDRepo.FindAll() if err != nil { return nil, fmt.Errorf("could not get local ids: %v", err) } for tskID, lid := range idMap { - if del.args.LocalID == lid { + if del.Args.LocalID == lid { id = tskID } } @@ -49,7 +49,7 @@ func (del *Delete) Do(deps Dependencies) ([][]string, error) { return nil, fmt.Errorf("could not find local id") } - tsk, err := deps.TaskRepo.Find(id) + tsk, err := deps.TaskRepo.FindOne(id) if err != nil { return nil, fmt.Errorf("could not get task: %v", err) } @@ -73,3 +73,9 @@ func (del *Delete) Do(deps Dependencies) ([][]string, error) { return nil, nil } + +type DeleteResult struct{} + +func (dr DeleteResult) Render() string { + return "task deleted" +} diff --git a/plan/command/delete_test.go b/plan/command/delete_test.go index 8e148e4..1a1145a 100644 --- a/plan/command/delete_test.go +++ b/plan/command/delete_test.go @@ -44,6 +44,7 @@ func TestDelete(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + // setup taskRepo := memory.NewTask() syncRepo := memory.NewSync() if err := taskRepo.Store(e); err != nil { @@ -54,6 +55,7 @@ func TestDelete(t *testing.T) { t.Errorf("exp nil, got %v", err) } + // parse cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags) if tc.expParseErr != (actParseErr != nil) { t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr) @@ -61,6 +63,8 @@ func TestDelete(t *testing.T) { if tc.expParseErr { return } + + // do _, actDoErr := cmd.Do(command.Dependencies{ TaskRepo: taskRepo, LocalIDRepo: localIDRepo, @@ -73,7 +77,8 @@ func TestDelete(t *testing.T) { return } - _, repoErr := taskRepo.Find(e.ID) + // check + _, repoErr := taskRepo.FindOne(e.ID) if !errors.Is(repoErr, storage.ErrNotFound) { t.Errorf("exp %v, got %v", storage.ErrNotFound, repoErr) } diff --git a/plan/command/list.go b/plan/command/list.go index 05ed941..5696dfa 100644 --- a/plan/command/list.go +++ b/plan/command/list.go @@ -2,9 +2,16 @@ package command import ( "fmt" + "slices" + "time" + + "go-mod.ewintr.nl/planner/item" + "go-mod.ewintr.nl/planner/plan/format" + "go-mod.ewintr.nl/planner/plan/storage" ) type ListArgs struct { + params storage.TaskListParams } func NewListArgs() ListArgs { @@ -12,32 +19,96 @@ func NewListArgs() ListArgs { } func (la ListArgs) Parse(main []string, flags map[string]string) (Command, error) { - if len(main) > 0 && main[0] != "list" { + if len(main) > 2 { return nil, ErrWrongCommand } - return &List{}, nil + now := time.Now() + today := item.NewDate(now.Year(), int(now.Month()), now.Day()) + tomorrow := item.NewDate(now.Year(), int(now.Month()), now.Day()+1) + var date item.Date + var includeBefore, recurrer bool + + switch len(main) { + case 0: + date = today + includeBefore = true + case 1: + switch { + case slices.Contains([]string{"today", "tod"}, main[0]): + date = today + includeBefore = true + case slices.Contains([]string{"tomorrow", "tom"}, main[0]): + date = tomorrow + case main[0] == "list": + default: + return nil, ErrWrongCommand + } + case 2: + if main[0] == "list" && main[1] == "recur" { + recurrer = true + } else { + return nil, ErrWrongCommand + } + default: + return nil, ErrWrongCommand + } + + return &List{ + args: ListArgs{ + params: storage.TaskListParams{ + Date: date, + IncludeBefore: includeBefore, + Recurrer: recurrer, + }, + }, + }, nil } type List struct { + args ListArgs } -func (list *List) Do(deps Dependencies) ([][]string, error) { +func (list *List) Do(deps Dependencies) (CommandResult, error) { localIDs, err := deps.LocalIDRepo.FindAll() if err != nil { return nil, fmt.Errorf("could not get local ids: %v", err) } - all, err := deps.TaskRepo.FindAll() + all, err := deps.TaskRepo.FindMany(list.args.params) if err != nil { return nil, err } - for _, e := range all { - lid, ok := localIDs[e.ID] + + res := make([]TaskWithLID, 0, len(all)) + for _, tsk := range all { + lid, ok := localIDs[tsk.ID] if !ok { - return nil, fmt.Errorf("could not find local id for %s", e.ID) + return nil, fmt.Errorf("could not find local id for %s", tsk.ID) } - fmt.Printf("%s\t%d\t%s\t%s\t%s\n", e.ID, lid, e.Title, e.Date.String(), e.Duration.String()) + res = append(res, TaskWithLID{ + LocalID: lid, + Task: tsk, + }) + } + return ListResult{ + Tasks: res, + }, nil +} + +type TaskWithLID struct { + LocalID int + Task item.Task +} + +type ListResult struct { + Tasks []TaskWithLID +} + +func (lr ListResult) Render() string { + data := [][]string{{"id", "date", "dur", "title"}} + for _, tl := range lr.Tasks { + data = append(data, []string{fmt.Sprintf("%d", tl.LocalID), tl.Task.Date.String(), tl.Task.Duration.String(), tl.Task.Title}) } - return nil, nil + return fmt.Sprintf("\n%s\n", format.Table(data)) } diff --git a/plan/command/list_test.go b/plan/command/list_test.go index 1707407..20d0234 100644 --- a/plan/command/list_test.go +++ b/plan/command/list_test.go @@ -30,15 +30,22 @@ func TestList(t *testing.T) { for _, tc := range []struct { name string main []string + expRes bool expErr bool }{ { - name: "empty", - main: []string{}, + name: "empty", + main: []string{}, + expRes: true, }, { - name: "list", - main: []string{"list"}, + name: "list", + main: []string{"list"}, + expRes: true, + }, + { + name: "empty list", + main: []string{"list", "recur"}, }, { name: "wrong", @@ -47,6 +54,7 @@ func TestList(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + // parse cmd, actErr := command.NewListArgs().Parse(tc.main, nil) if tc.expErr != (actErr != nil) { t.Errorf("exp %v, got %v", tc.expErr, actErr) @@ -54,12 +62,22 @@ func TestList(t *testing.T) { if tc.expErr { return } - if _, err := cmd.Do(command.Dependencies{ + + // do + res, err := cmd.Do(command.Dependencies{ TaskRepo: taskRepo, LocalIDRepo: localRepo, - }); err != nil { + }) + if err != nil { t.Errorf("exp nil, got %v", err) } + + // check + listRes := res.(command.ListResult) + actRes := len(listRes.Tasks) > 0 + if tc.expRes != actRes { + t.Errorf("exp %v, got %v", tc.expRes, actRes) + } }) } } diff --git a/plan/command/show.go b/plan/command/show.go index 4368c1f..0fce2b5 100644 --- a/plan/command/show.go +++ b/plan/command/show.go @@ -5,6 +5,8 @@ import ( "fmt" "strconv" + "go-mod.ewintr.nl/planner/item" + "go-mod.ewintr.nl/planner/plan/format" "go-mod.ewintr.nl/planner/plan/storage" ) @@ -36,7 +38,7 @@ type Show struct { args ShowArgs } -func (s *Show) Do(deps Dependencies) ([][]string, error) { +func (s *Show) Do(deps Dependencies) (CommandResult, error) { id, err := deps.LocalIDRepo.FindOne(s.args.localID) switch { case errors.Is(err, storage.ErrNotFound): @@ -45,24 +47,37 @@ func (s *Show) Do(deps Dependencies) ([][]string, error) { return nil, err } - tsk, err := deps.TaskRepo.Find(id) + tsk, err := deps.TaskRepo.FindOne(id) if err != nil { return nil, fmt.Errorf("could not find task") } + return ShowResult{ + LocalID: s.args.localID, + Task: tsk, + }, nil +} + +type ShowResult struct { + LocalID int + Task item.Task +} + +func (sr ShowResult) Render() string { + var recurStr string - if tsk.Recurrer != nil { - recurStr = tsk.Recurrer.String() + if sr.Task.Recurrer != nil { + recurStr = sr.Task.Recurrer.String() } data := [][]string{ - {"title", tsk.Title}, - {"local id", fmt.Sprintf("%d", s.args.localID)}, - {"date", tsk.Date.String()}, - {"time", tsk.Time.String()}, - {"duration", tsk.Duration.String()}, + {"title", sr.Task.Title}, + {"local id", fmt.Sprintf("%d", sr.LocalID)}, + {"date", sr.Task.Date.String()}, + {"time", sr.Task.Time.String()}, + {"duration", sr.Task.Duration.String()}, {"recur", recurStr}, - // {"id", tsk.ID}, + // {"id", s.Task.ID}, } - return data, nil + return fmt.Sprintf("\n%s\n", format.Table(data)) } diff --git a/plan/command/show_test.go b/plan/command/show_test.go index cc3e83c..1414ab8 100644 --- a/plan/command/show_test.go +++ b/plan/command/show_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - "github.com/google/go-cmp/cmp" "go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/plan/command" "go-mod.ewintr.nl/planner/plan/storage/memory" @@ -60,6 +59,7 @@ func TestShow(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + // parse cmd, actParseErr := command.NewShowArgs().Parse(tc.main, nil) if tc.expParseErr != (actParseErr != nil) { t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr != nil) @@ -67,7 +67,9 @@ func TestShow(t *testing.T) { if tc.expParseErr { return } - actData, actDoErr := cmd.Do(command.Dependencies{ + + // do + _, actDoErr := cmd.Do(command.Dependencies{ TaskRepo: taskRepo, LocalIDRepo: localRepo, }) @@ -77,9 +79,9 @@ func TestShow(t *testing.T) { if tc.expDoErr { return } - if diff := cmp.Diff(tc.expData, actData); diff != "" { - t.Errorf("(+exp, -got)%s\n", diff) - } + // if diff := cmp.Diff(tc.expData, actData); diff != "" { + // t.Errorf("(+exp, -got)%s\n", diff) + // } }) } diff --git a/plan/command/sync.go b/plan/command/sync.go index ba8474d..1f0a0f7 100644 --- a/plan/command/sync.go +++ b/plan/command/sync.go @@ -25,7 +25,7 @@ func (sa SyncArgs) Parse(main []string, flags map[string]string) (Command, error type Sync struct{} -func (s *Sync) Do(deps Dependencies) ([][]string, error) { +func (s *Sync) Do(deps Dependencies) (CommandResult, error) { // local new and updated sendItems, err := deps.SyncRepo.FindAll() if err != nil { @@ -94,5 +94,9 @@ func (s *Sync) Do(deps Dependencies) ([][]string, error) { } } - return nil, nil + return SyncResult{}, nil } + +type SyncResult struct{} + +func (sr SyncResult) Render() string { return "tasks synced" } diff --git a/plan/command/sync_test.go b/plan/command/sync_test.go index f76a9b6..9e3b55a 100644 --- a/plan/command/sync_test.go +++ b/plan/command/sync_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-cmp/cmp" "go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/plan/command" + "go-mod.ewintr.nl/planner/plan/storage" "go-mod.ewintr.nl/planner/plan/storage/memory" "go-mod.ewintr.nl/planner/sync/client" ) @@ -216,7 +217,7 @@ func TestSyncReceive(t *testing.T) { } // check result - actTasks, err := taskRepo.FindAll() + actTasks, err := taskRepo.FindMany(storage.TaskListParams{}) if err != nil { t.Errorf("exp nil, got %v", err) } diff --git a/plan/command/update.go b/plan/command/update.go index 8ff8661..de9ea75 100644 --- a/plan/command/update.go +++ b/plan/command/update.go @@ -24,10 +24,10 @@ type UpdateArgs struct { func NewUpdateArgs() UpdateArgs { return UpdateArgs{ fieldTPL: map[string][]string{ - "date": []string{"d", "date", "on"}, - "time": []string{"t", "time", "at"}, - "duration": []string{"dur", "duration", "for"}, - "recurrer": []string{"rec", "recurrer"}, + "date": {"d", "date", "on"}, + "time": {"t", "time", "at"}, + "duration": {"dur", "duration", "for"}, + "recurrer": {"rec", "recurrer"}, }, } } @@ -85,7 +85,7 @@ type Update struct { args UpdateArgs } -func (u *Update) Do(deps Dependencies) ([][]string, error) { +func (u *Update) Do(deps Dependencies) (CommandResult, error) { id, err := deps.LocalIDRepo.FindOne(u.args.LocalID) switch { case errors.Is(err, storage.ErrNotFound): @@ -94,7 +94,7 @@ func (u *Update) Do(deps Dependencies) ([][]string, error) { return nil, err } - tsk, err := deps.TaskRepo.Find(id) + tsk, err := deps.TaskRepo.FindOne(id) if err != nil { return nil, fmt.Errorf("could not find task") } @@ -132,5 +132,11 @@ func (u *Update) Do(deps Dependencies) ([][]string, error) { return nil, fmt.Errorf("could not store sync item: %v", err) } - return nil, nil + return UpdateResult{}, nil +} + +type UpdateResult struct{} + +func (ur UpdateResult) Render() string { + return "task updated" } diff --git a/plan/command/update_test.go b/plan/command/update_test.go index fbc14d9..aae062b 100644 --- a/plan/command/update_test.go +++ b/plan/command/update_test.go @@ -164,6 +164,7 @@ func TestUpdateExecute(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + // setup taskRepo := memory.NewTask() localIDRepo := memory.NewLocalID() syncRepo := memory.NewSync() @@ -182,6 +183,7 @@ func TestUpdateExecute(t *testing.T) { t.Errorf("exp nil, ,got %v", err) } + // parse cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields) if tc.expParseErr != (actErr != nil) { t.Errorf("exp %v, got %v", tc.expParseErr, actErr) @@ -189,6 +191,8 @@ func TestUpdateExecute(t *testing.T) { if tc.expParseErr { return } + + // do _, actDoErr := cmd.Do(command.Dependencies{ TaskRepo: taskRepo, LocalIDRepo: localIDRepo, @@ -201,7 +205,8 @@ func TestUpdateExecute(t *testing.T) { return } - actTask, err := taskRepo.Find(tskID) + // check + actTask, err := taskRepo.FindOne(tskID) if err != nil { t.Errorf("exp nil, got %v", err) } diff --git a/plan/storage/memory/task.go b/plan/storage/memory/task.go index bfba997..10290a1 100644 --- a/plan/storage/memory/task.go +++ b/plan/storage/memory/task.go @@ -19,7 +19,7 @@ func NewTask() *Task { } } -func (t *Task) Find(id string) (item.Task, error) { +func (t *Task) FindOne(id string) (item.Task, error) { t.mutex.RLock() defer t.mutex.RUnlock() @@ -30,13 +30,15 @@ func (t *Task) Find(id string) (item.Task, error) { return task, nil } -func (t *Task) FindAll() ([]item.Task, error) { +func (t *Task) FindMany(params storage.TaskListParams) ([]item.Task, error) { t.mutex.RLock() defer t.mutex.RUnlock() tasks := make([]item.Task, 0, len(t.tasks)) for _, tsk := range t.tasks { - tasks = append(tasks, tsk) + if storage.Match(tsk, params) { + tasks = append(tasks, tsk) + } } sort.Slice(tasks, func(i, j int) bool { return tasks[i].ID < tasks[j].ID diff --git a/plan/storage/memory/task_test.go b/plan/storage/memory/task_test.go index 265ab67..7fac228 100644 --- a/plan/storage/memory/task_test.go +++ b/plan/storage/memory/task_test.go @@ -4,6 +4,7 @@ import ( "testing" "go-mod.ewintr.nl/planner/item" + "go-mod.ewintr.nl/planner/plan/storage" ) func TestTask(t *testing.T) { @@ -12,7 +13,7 @@ func TestTask(t *testing.T) { mem := NewTask() t.Log("empty") - actTasks, actErr := mem.FindAll() + actTasks, actErr := mem.FindMany(storage.TaskListParams{}) if actErr != nil { t.Errorf("exp nil, got %v", actErr) } @@ -22,7 +23,8 @@ func TestTask(t *testing.T) { t.Log("store") tsk1 := item.Task{ - ID: "id-1", + ID: "id-1", + Date: item.NewDate(2024, 12, 29), } if err := mem.Store(tsk1); err != nil { t.Errorf("exp nil, got %v", err) @@ -36,7 +38,7 @@ func TestTask(t *testing.T) { } t.Log("find one") - actTask, actErr := mem.Find(tsk1.ID) + actTask, actErr := mem.FindOne(tsk1.ID) if actErr != nil { t.Errorf("exp nil, got %v", actErr) } @@ -45,11 +47,23 @@ func TestTask(t *testing.T) { } t.Log("find all") - actTasks, actErr = mem.FindAll() + actTasks, actErr = mem.FindMany(storage.TaskListParams{}) if actErr != nil { t.Errorf("exp nil, got %v", actErr) } if diff := item.TaskDiffs([]item.Task{tsk1, tsk2}, actTasks); diff != "" { t.Errorf("(exp -, got +)\n%s", diff) } + + t.Log("fond some") + actTasks, actErr = mem.FindMany(storage.TaskListParams{ + Date: item.NewDate(2024, 12, 29), + }) + if actErr != nil { + t.Errorf("exp nil, got %v", actErr) + } + if diff := item.TaskDiffs([]item.Task{tsk1}, actTasks); diff != "" { + t.Errorf("(exp -, got +)\n%s", diff) + } + } diff --git a/plan/storage/sqlite/task.go b/plan/storage/sqlite/task.go index 9b23b50..ecf9a79 100644 --- a/plan/storage/sqlite/task.go +++ b/plan/storage/sqlite/task.go @@ -38,7 +38,7 @@ recurrer=? return nil } -func (t *SqliteTask) Find(id string) (item.Task, error) { +func (t *SqliteTask) FindOne(id string) (item.Task, error) { var tsk item.Task var dateStr, timeStr, recurStr, durStr string err := t.db.QueryRow(` @@ -63,14 +63,35 @@ WHERE id = ?`, id).Scan(&tsk.ID, &tsk.Title, &dateStr, &timeStr, &durStr, &recur return tsk, nil } -func (t *SqliteTask) FindAll() ([]item.Task, error) { - rows, err := t.db.Query(` -SELECT id, title, date, time, duration, recurrer -FROM tasks`) +func (t *SqliteTask) FindMany(params storage.TaskListParams) ([]item.Task, error) { + query := `SELECT id, title, date, time, duration, recurrer FROM tasks` + args := []interface{}{} + where := []string{} + + if params.Recurrer { + where = append(where, `recurrer IS NOT NULL AND recurrer != ''`) + } + if !params.Date.IsZero() && !params.IncludeBefore { + where = append(where, `date = ?`) + args = append(args, params.Date.String()) + } + if !params.Date.IsZero() && params.IncludeBefore { + where = append(where, `date <= ?`) + args = append(args, params.Date.String()) + } + + if len(where) > 0 { + query += ` WHERE ` + where[0] + for _, w := range where[1:] { + query += ` AND ` + w + } + } + + rows, err := t.db.Query(query, args...) if err != nil { return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) } - result := make([]item.Task, 0) + tasks := make([]item.Task, 0) defer rows.Close() for rows.Next() { var tsk item.Task @@ -87,10 +108,10 @@ FROM tasks`) tsk.Duration = dur tsk.Recurrer = item.NewRecurrer(recurStr) - result = append(result, tsk) + tasks = append(tasks, tsk) } - return result, nil + return tasks, nil } func (s *SqliteTask) Delete(id string) error { diff --git a/plan/storage/storage.go b/plan/storage/storage.go index 7e7073c..8634ffd 100644 --- a/plan/storage/storage.go +++ b/plan/storage/storage.go @@ -28,13 +28,35 @@ type Sync interface { LastUpdate() (time.Time, error) } +type TaskListParams struct { + Recurrer bool + Date item.Date + IncludeBefore bool +} + type Task interface { Store(task item.Task) error - Find(id string) (item.Task, error) - FindAll() ([]item.Task, error) + FindOne(id string) (item.Task, error) + FindMany(params TaskListParams) ([]item.Task, error) Delete(id string) error } +func Match(tsk item.Task, params TaskListParams) bool { + if params.Recurrer && tsk.Recurrer == nil { + return false + } + if !params.Date.IsZero() { + if !params.IncludeBefore && !params.Date.Equal(tsk.Date) { + return false + } + if params.IncludeBefore && tsk.Date.After(params.Date) { + return false + } + } + + return true +} + func NextLocalID(used []int) int { if len(used) == 0 { return 1 diff --git a/plan/storage/storage_test.go b/plan/storage/storage_test.go index 528ec1a..937e7b9 100644 --- a/plan/storage/storage_test.go +++ b/plan/storage/storage_test.go @@ -3,10 +3,60 @@ package storage_test import ( "testing" + "go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/plan/storage" ) +func TestMatch(t *testing.T) { + t.Parallel() + + tskMatch := item.Task{ + ID: "id", + Date: item.NewDate(2024, 12, 29), + Recurrer: item.NewRecurrer("2024-12-29, daily"), + TaskBody: item.TaskBody{ + Title: "name", + }, + } + tskNotMatch := item.Task{ + ID: "id", + Date: item.NewDate(2024, 12, 28), + TaskBody: item.TaskBody{ + Title: "name", + }, + } + + for _, tc := range []struct { + name string + params storage.TaskListParams + }{ + { + name: "date", + params: storage.TaskListParams{ + Date: item.NewDate(2024, 12, 29), + }, + }, + { + name: "recurrer", + params: storage.TaskListParams{ + Recurrer: true, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + if !storage.Match(tskMatch, tc.params) { + t.Errorf("exp tsk to match") + } + if storage.Match(tskNotMatch, tc.params) { + t.Errorf("exp tsk to not match") + } + }) + } +} + func TestNextLocalId(t *testing.T) { + t.Parallel() + for _, tc := range []struct { name string used []int