diff --git a/cmd/cli/command/command.go b/cmd/cli/command/command.go index 10b7f1c..28782ce 100644 --- a/cmd/cli/command/command.go +++ b/cmd/cli/command/command.go @@ -1,27 +1,13 @@ package command import ( - "errors" "fmt" "git.ewintr.nl/gte/internal/configuration" - "git.ewintr.nl/gte/internal/process" - "git.ewintr.nl/gte/internal/storage" - "git.ewintr.nl/gte/internal/task" - "git.ewintr.nl/gte/pkg/mstore" ) -var ( - ErrInitCommand = errors.New("could not initialize command") - ErrFailedCommand = errors.New("could not execute command") -) - -type Result struct { - Message string -} - type Command interface { - Do() (Result, error) + Do() string } func Parse(args []string, conf *configuration.Configuration) (Command, error) { @@ -40,87 +26,6 @@ func Parse(args []string, conf *configuration.Configuration) (Command, error) { } } -type Empty struct{} - -func NewEmpty() (*Empty, error) { - return &Empty{}, nil -} - -func (cmd *Empty) Do() (Result, error) { - return Result{ - Message: "did nothing\n", - }, nil -} - -type Sync struct { - syncer *process.Sync -} - -func NewSync(conf *configuration.Configuration) (*Sync, error) { - msgStore := mstore.NewIMAP(conf.IMAP()) - remote := storage.NewRemoteRepository(msgStore) - local, err := storage.NewSqlite(conf.Sqlite()) - if err != nil { - return &Sync{}, fmt.Errorf("%w: %v", ErrInitCommand, err) - } - syncer := process.NewSync(remote, local) - - return &Sync{ - syncer: syncer, - }, nil -} - -func (s *Sync) Do() (Result, error) { - result, err := s.syncer.Process() - if err != nil { - return Result{}, fmt.Errorf("%w: %v", ErrFailedCommand, err) - } - - return Result{ - Message: fmt.Sprintf("synced %d tasks\n", result.Count), - }, nil -} - -type Today struct { - local storage.LocalRepository -} - -func NewToday(conf *configuration.Configuration) (*Today, error) { - local, err := storage.NewSqlite(conf.Sqlite()) - if err != nil { - return &Today{}, fmt.Errorf("%w: %v", ErrInitCommand, err) - } - - return &Today{ - local: local, - }, nil -} - -func (t *Today) Do() (Result, error) { - tasks, err := t.local.FindAllInFolder(task.FOLDER_PLANNED) - if err != nil { - return Result{}, fmt.Errorf("%w: %v", ErrFailedCommand, err) - } - - todayTasks := []*task.Task{} - for _, t := range tasks { - if t.Due == task.Today || task.Today.After(t.Due) { - todayTasks = append(todayTasks, t) - } - } - - if len(todayTasks) == 0 { - return Result{ - Message: "nothing left", - }, nil - } - - var msg string - for _, t := range todayTasks { - msg += fmt.Sprintf("%s - %s\n", t.Project, t.Action) - } - - return Result{ - Message: msg, - }, nil +func FormatError(err error) string { + return fmt.Sprintf("could not perform command.\n\nerror: %s\n", err.Error()) } diff --git a/cmd/cli/command/empty.go b/cmd/cli/command/empty.go new file mode 100644 index 0000000..ec28fe8 --- /dev/null +++ b/cmd/cli/command/empty.go @@ -0,0 +1,11 @@ +package command + +type Empty struct{} + +func NewEmpty() (*Empty, error) { + return &Empty{}, nil +} + +func (cmd *Empty) Do() string { + return "did nothing\n" +} diff --git a/cmd/cli/command/sync.go b/cmd/cli/command/sync.go new file mode 100644 index 0000000..9bbb076 --- /dev/null +++ b/cmd/cli/command/sync.go @@ -0,0 +1,37 @@ +package command + +import ( + "fmt" + + "git.ewintr.nl/gte/internal/configuration" + "git.ewintr.nl/gte/internal/process" + "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/pkg/mstore" +) + +type Sync struct { + syncer *process.Sync +} + +func NewSync(conf *configuration.Configuration) (*Sync, error) { + msgStore := mstore.NewIMAP(conf.IMAP()) + remote := storage.NewRemoteRepository(msgStore) + local, err := storage.NewSqlite(conf.Sqlite()) + if err != nil { + return &Sync{}, err + } + syncer := process.NewSync(remote, local) + + return &Sync{ + syncer: syncer, + }, nil +} + +func (s *Sync) Do() string { + result, err := s.syncer.Process() + if err != nil { + return FormatError(err) + } + + return fmt.Sprintf("synced %d tasks\n", result.Count) +} diff --git a/cmd/cli/command/today.go b/cmd/cli/command/today.go new file mode 100644 index 0000000..33fc319 --- /dev/null +++ b/cmd/cli/command/today.go @@ -0,0 +1,47 @@ +package command + +import ( + "fmt" + + "git.ewintr.nl/gte/internal/configuration" + "git.ewintr.nl/gte/internal/process" + "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/internal/task" +) + +type Today struct { + todayer *process.List +} + +func NewToday(conf *configuration.Configuration) (*Today, error) { + local, err := storage.NewSqlite(conf.Sqlite()) + if err != nil { + return &Today{}, err + } + reqs := process.ListReqs{ + Due: task.Today, + IncludeBefore: true, + } + todayer := process.NewList(local, reqs) + + return &Today{ + todayer: todayer, + }, nil +} + +func (t *Today) Do() string { + res, err := t.todayer.Process() + if err != nil { + return FormatError(err) + } + if len(res.Tasks) == 0 { + return "nothing left\n" + } + + var msg string + for _, t := range res.Tasks { + msg += fmt.Sprintf("%s - %s\n", t.Project, t.Action) + } + + return msg +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index b53b8fe..6ae8439 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -25,10 +25,5 @@ func main() { fmt.Println(err, "could not initialize command") os.Exit(1) } - result, err := cmd.Do() - if err != nil { - fmt.Println(err, "could not perform command") - os.Exit(1) - } - fmt.Printf("%s\n", result.Message) + fmt.Printf("%s\n", cmd.Do()) } diff --git a/internal/process/list.go b/internal/process/list.go new file mode 100644 index 0000000..50abc25 --- /dev/null +++ b/internal/process/list.go @@ -0,0 +1,66 @@ +package process + +import ( + "errors" + "fmt" + + "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/internal/task" +) + +var ( + ErrInvalidReqs = errors.New("could not make sense of requirements") + ErrListProcess = errors.New("could not fetch task list") +) + +// ListReqs specifies the requirements in AND fashion for a list of tasks +type ListReqs struct { + Due task.Date + IncludeBefore bool +} + +func (lr ListReqs) Valid() bool { + return !lr.Due.IsZero() +} + +// List finds all tasks that satisfy the given requirements +type List struct { + local storage.LocalRepository + reqs ListReqs +} + +type ListResult struct { + Tasks []*task.Task +} + +func NewList(local storage.LocalRepository, reqs ListReqs) *List { + return &List{ + local: local, + reqs: reqs, + } +} + +func (l *List) Process() (*ListResult, error) { + if !l.reqs.Valid() { + return &ListResult{}, ErrInvalidReqs + } + + potentialTasks, err := l.local.FindAllInFolder(task.FOLDER_PLANNED) + if err != nil { + return &ListResult{}, fmt.Errorf("%w: %v", ErrListProcess, err) + } + + dueTasks := []*task.Task{} + for _, t := range potentialTasks { + switch { + case t.Due.Equal(l.reqs.Due): + dueTasks = append(dueTasks, t) + case l.reqs.IncludeBefore && l.reqs.Due.After(t.Due): + dueTasks = append(dueTasks, t) + } + } + + return &ListResult{ + Tasks: dueTasks, + }, nil +} diff --git a/internal/process/list_test.go b/internal/process/list_test.go new file mode 100644 index 0000000..d1e8fac --- /dev/null +++ b/internal/process/list_test.go @@ -0,0 +1,85 @@ +package process_test + +import ( + "errors" + "testing" + + "git.ewintr.nl/go-kit/test" + "git.ewintr.nl/gte/internal/process" + "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/internal/task" +) + +func TestListProcess(t *testing.T) { + date1 := task.NewDate(2021, 7, 9) + date2 := task.NewDate(2021, 7, 10) + date3 := task.NewDate(2021, 7, 11) + + task1 := &task.Task{ + Id: "id1", + Version: 1, + Action: "action1", + Folder: task.FOLDER_NEW, + } + task2 := &task.Task{ + Id: "id2", + Version: 1, + Action: "action2", + Due: date1, + Folder: task.FOLDER_PLANNED, + } + task3 := &task.Task{ + Id: "id3", + Version: 1, + Action: "action3", + Due: date2, + Folder: task.FOLDER_PLANNED, + } + task4 := &task.Task{ + Id: "id4", + Version: 1, + Action: "action4", + Due: date3, + Folder: task.FOLDER_PLANNED, + } + allTasks := []*task.Task{task1, task2, task3, task4} + + local := storage.NewMemory() + test.OK(t, local.SetTasks(allTasks)) + + t.Run("invalid reqs", func(t *testing.T) { + list := process.NewList(local, process.ListReqs{}) + _, actErr := list.Process() + test.Assert(t, errors.Is(actErr, process.ErrInvalidReqs), "expected invalid reqs err") + }) + + for _, tc := range []struct { + name string + reqs process.ListReqs + exp []*task.Task + }{ + { + name: "due", + reqs: process.ListReqs{ + Due: date2, + }, + exp: []*task.Task{task3}, + }, + { + name: "due and before", + reqs: process.ListReqs{ + Due: date2, + IncludeBefore: true, + }, + exp: []*task.Task{task2, task3}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + list := process.NewList(local, tc.reqs) + + act, err := list.Process() + test.OK(t, err) + test.Equals(t, tc.exp, act.Tasks) + }) + } +}