Compare commits
No commits in common. "23a3f59f48a6bdc0f301a43323fc32102ed2c1f7" and "c209ba6197c0cc86d970955a1317b6e62836069d" have entirely different histories.
23a3f59f48
...
c209ba6197
|
@ -91,6 +91,12 @@ func (t Task) Valid() bool {
|
||||||
if t.Title == "" {
|
if t.Title == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if t.Date.IsZero() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.Duration.Seconds() < 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,28 @@ 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",
|
name: "valid",
|
||||||
tsk: item.Task{
|
tsk: item.Task{
|
||||||
|
|
|
@ -11,16 +11,16 @@ import (
|
||||||
|
|
||||||
type AddArgs struct {
|
type AddArgs struct {
|
||||||
fieldTPL map[string][]string
|
fieldTPL map[string][]string
|
||||||
Task item.Task
|
task item.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddArgs() AddArgs {
|
func NewAddArgs() AddArgs {
|
||||||
return AddArgs{
|
return AddArgs{
|
||||||
fieldTPL: map[string][]string{
|
fieldTPL: map[string][]string{
|
||||||
"date": {"d", "date", "on"},
|
"date": []string{"d", "date", "on"},
|
||||||
"time": {"t", "time", "at"},
|
"time": []string{"t", "time", "at"},
|
||||||
"duration": {"dur", "duration", "for"},
|
"duration": []string{"dur", "duration", "for"},
|
||||||
"recurrer": {"rec", "recurrer"},
|
"recurrer": []string{"rec", "recurrer"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error
|
||||||
tsk := item.Task{
|
tsk := item.Task{
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
TaskBody: item.TaskBody{
|
TaskBody: item.TaskBody{
|
||||||
Title: strings.Join(main, " "),
|
Title: strings.Join(main, ","),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,43 +76,36 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Add{
|
return &Add{
|
||||||
Args: AddArgs{
|
args: AddArgs{
|
||||||
Task: tsk,
|
task: tsk,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Add struct {
|
type Add struct {
|
||||||
Args AddArgs
|
args AddArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Add) Do(deps Dependencies) (CommandResult, error) {
|
func (a *Add) Do(deps Dependencies) error {
|
||||||
if err := deps.TaskRepo.Store(a.Args.Task); err != nil {
|
if err := deps.TaskRepo.Store(a.args.task); err != nil {
|
||||||
return nil, fmt.Errorf("could not store event: %v", err)
|
return fmt.Errorf("could not store event: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localID, err := deps.LocalIDRepo.Next()
|
localID, err := deps.LocalIDRepo.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create next local id: %v", err)
|
return 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)
|
return fmt.Errorf("could not store local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
it, err := a.Args.Task.Item()
|
it, err := a.args.task.Item()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not convert event to sync item: %v", err)
|
return fmt.Errorf("could not convert event to sync item: %v", err)
|
||||||
}
|
}
|
||||||
if err := deps.SyncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return nil, fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
type AddRender struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ar AddRender) Render() string {
|
|
||||||
return "stored task"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"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/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,12 +56,9 @@ func TestAdd(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// setup
|
|
||||||
taskRepo := memory.NewTask()
|
taskRepo := memory.NewTask()
|
||||||
localIDRepo := memory.NewLocalID()
|
localIDRepo := memory.NewLocalID()
|
||||||
syncRepo := memory.NewSync()
|
syncRepo := memory.NewSync()
|
||||||
|
|
||||||
// parse
|
|
||||||
cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields)
|
cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields)
|
||||||
if tc.expErr != (actParseErr != nil) {
|
if tc.expErr != (actParseErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
||||||
|
@ -70,9 +66,7 @@ func TestAdd(t *testing.T) {
|
||||||
if tc.expErr {
|
if tc.expErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
// do
|
|
||||||
if _, err := cmd.Do(command.Dependencies{
|
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localIDRepo,
|
LocalIDRepo: localIDRepo,
|
||||||
SyncRepo: syncRepo,
|
SyncRepo: syncRepo,
|
||||||
|
@ -80,8 +74,7 @@ func TestAdd(t *testing.T) {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check
|
actTasks, err := taskRepo.FindAll()
|
||||||
actTasks, err := taskRepo.FindMany(storage.TaskListParams{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,7 @@ type CommandArgs interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Command interface {
|
type Command interface {
|
||||||
Do(deps Dependencies) (CommandResult, error)
|
Do(deps Dependencies) error
|
||||||
}
|
|
||||||
|
|
||||||
type CommandResult interface {
|
|
||||||
Render() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
|
@ -48,7 +44,6 @@ func NewCLI(deps Dependencies) *CLI {
|
||||||
return &CLI{
|
return &CLI{
|
||||||
deps: deps,
|
deps: deps,
|
||||||
cmdArgs: []CommandArgs{
|
cmdArgs: []CommandArgs{
|
||||||
NewShowArgs(),
|
|
||||||
NewAddArgs(), NewDeleteArgs(), NewListArgs(),
|
NewAddArgs(), NewDeleteArgs(), NewListArgs(),
|
||||||
NewSyncArgs(), NewUpdateArgs(),
|
NewSyncArgs(), NewUpdateArgs(),
|
||||||
},
|
},
|
||||||
|
@ -56,23 +51,17 @@ func NewCLI(deps Dependencies) *CLI {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) Run(args []string) error {
|
func (cli *CLI) Run(args []string) error {
|
||||||
main, fields := FindFields(args)
|
main, flags := FindFields(args)
|
||||||
for _, ca := range cli.cmdArgs {
|
for _, ca := range cli.cmdArgs {
|
||||||
cmd, err := ca.Parse(main, fields)
|
cmd, err := ca.Parse(main, flags)
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, ErrWrongCommand):
|
case errors.Is(err, ErrWrongCommand):
|
||||||
continue
|
continue
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
|
default:
|
||||||
|
return cmd.Do(cli.deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := cmd.Do(cli.deps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(result.Render())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("could not find matching command")
|
return fmt.Errorf("could not find matching command")
|
||||||
|
|
|
@ -24,58 +24,52 @@ func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Delete{
|
return &Delete{
|
||||||
Args: DeleteArgs{
|
args: DeleteArgs{
|
||||||
LocalID: localID,
|
LocalID: localID,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Delete struct {
|
type Delete struct {
|
||||||
Args DeleteArgs
|
args DeleteArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (del *Delete) Do(deps Dependencies) (CommandResult, error) {
|
func (del *Delete) Do(deps Dependencies) error {
|
||||||
var id string
|
var id string
|
||||||
idMap, err := deps.LocalIDRepo.FindAll()
|
idMap, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
for tskID, lid := range idMap {
|
for tskID, lid := range idMap {
|
||||||
if del.Args.LocalID == lid {
|
if del.args.LocalID == lid {
|
||||||
id = tskID
|
id = tskID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return nil, fmt.Errorf("could not find local id")
|
return fmt.Errorf("could not find local id")
|
||||||
}
|
}
|
||||||
|
|
||||||
tsk, err := deps.TaskRepo.FindOne(id)
|
tsk, err := deps.TaskRepo.Find(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get task: %v", err)
|
return fmt.Errorf("could not get task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
it, err := tsk.Item()
|
it, err := tsk.Item()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not convert task to sync item: %v", err)
|
return fmt.Errorf("could not convert task to sync item: %v", err)
|
||||||
}
|
}
|
||||||
it.Deleted = true
|
it.Deleted = true
|
||||||
if err := deps.SyncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return nil, fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deps.LocalIDRepo.Delete(id); err != nil {
|
if err := deps.LocalIDRepo.Delete(id); err != nil {
|
||||||
return nil, fmt.Errorf("could not delete local id: %v", err)
|
return fmt.Errorf("could not delete local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deps.TaskRepo.Delete(id); err != nil {
|
if err := deps.TaskRepo.Delete(id); err != nil {
|
||||||
return nil, fmt.Errorf("could not delete task: %v", err)
|
return fmt.Errorf("could not delete task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteResult struct{}
|
|
||||||
|
|
||||||
func (dr DeleteResult) Render() string {
|
|
||||||
return "task deleted"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ func TestDelete(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// setup
|
|
||||||
taskRepo := memory.NewTask()
|
taskRepo := memory.NewTask()
|
||||||
syncRepo := memory.NewSync()
|
syncRepo := memory.NewSync()
|
||||||
if err := taskRepo.Store(e); err != nil {
|
if err := taskRepo.Store(e); err != nil {
|
||||||
|
@ -55,7 +54,6 @@ func TestDelete(t *testing.T) {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse
|
|
||||||
cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags)
|
cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags)
|
||||||
if tc.expParseErr != (actParseErr != nil) {
|
if tc.expParseErr != (actParseErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
|
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
|
||||||
|
@ -63,22 +61,19 @@ func TestDelete(t *testing.T) {
|
||||||
if tc.expParseErr {
|
if tc.expParseErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
actDoErr := cmd.Do(command.Dependencies{
|
||||||
// do
|
|
||||||
_, actDoErr := cmd.Do(command.Dependencies{
|
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localIDRepo,
|
LocalIDRepo: localIDRepo,
|
||||||
SyncRepo: syncRepo,
|
SyncRepo: syncRepo,
|
||||||
})
|
}) != nil
|
||||||
if tc.expDoErr != (actDoErr != nil) {
|
if tc.expDoErr != actDoErr {
|
||||||
t.Errorf("exp false, got %v", actDoErr)
|
t.Errorf("exp false, got %v", actDoErr)
|
||||||
}
|
}
|
||||||
if tc.expDoErr {
|
if tc.expDoErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check
|
_, repoErr := taskRepo.Find(e.ID)
|
||||||
_, repoErr := taskRepo.FindOne(e.ID)
|
|
||||||
if !errors.Is(repoErr, storage.ErrNotFound) {
|
if !errors.Is(repoErr, storage.ErrNotFound) {
|
||||||
t.Errorf("exp %v, got %v", storage.ErrNotFound, repoErr)
|
t.Errorf("exp %v, got %v", storage.ErrNotFound, repoErr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,9 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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 {
|
type ListArgs struct {
|
||||||
params storage.TaskListParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListArgs() ListArgs {
|
func NewListArgs() ListArgs {
|
||||||
|
@ -19,96 +12,32 @@ func NewListArgs() ListArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (la ListArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
func (la ListArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
||||||
if len(main) > 2 {
|
if len(main) > 0 && main[0] != "list" {
|
||||||
return nil, ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
return &List{}, nil
|
||||||
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 {
|
type List struct {
|
||||||
args ListArgs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list *List) Do(deps Dependencies) (CommandResult, error) {
|
func (list *List) Do(deps Dependencies) error {
|
||||||
localIDs, err := deps.LocalIDRepo.FindAll()
|
localIDs, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
all, err := deps.TaskRepo.FindMany(list.args.params)
|
all, err := deps.TaskRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, e := range all {
|
||||||
res := make([]TaskWithLID, 0, len(all))
|
lid, ok := localIDs[e.ID]
|
||||||
for _, tsk := range all {
|
|
||||||
lid, ok := localIDs[tsk.ID]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("could not find local id for %s", tsk.ID)
|
return fmt.Errorf("could not find local id for %s", e.ID)
|
||||||
}
|
}
|
||||||
res = append(res, TaskWithLID{
|
fmt.Printf("%s\t%d\t%s\t%s\t%s\n", e.ID, lid, e.Title, e.Date.String(), e.Duration.String())
|
||||||
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 fmt.Sprintf("\n%s\n", format.Table(data))
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,22 +30,15 @@ func TestList(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
main []string
|
main []string
|
||||||
expRes bool
|
|
||||||
expErr bool
|
expErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
main: []string{},
|
main: []string{},
|
||||||
expRes: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list",
|
name: "list",
|
||||||
main: []string{"list"},
|
main: []string{"list"},
|
||||||
expRes: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty list",
|
|
||||||
main: []string{"list", "recur"},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong",
|
name: "wrong",
|
||||||
|
@ -54,7 +47,6 @@ func TestList(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// parse
|
|
||||||
cmd, actErr := command.NewListArgs().Parse(tc.main, nil)
|
cmd, actErr := command.NewListArgs().Parse(tc.main, nil)
|
||||||
if tc.expErr != (actErr != nil) {
|
if tc.expErr != (actErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
||||||
|
@ -62,22 +54,12 @@ func TestList(t *testing.T) {
|
||||||
if tc.expErr {
|
if tc.expErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
// do
|
|
||||||
res, err := cmd.Do(command.Dependencies{
|
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localRepo,
|
LocalIDRepo: localRepo,
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
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)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/format"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ShowArgs struct {
|
|
||||||
localID int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewShowArgs() ShowArgs {
|
|
||||||
return ShowArgs{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sa ShowArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
|
||||||
if len(main) != 1 {
|
|
||||||
return nil, ErrWrongCommand
|
|
||||||
}
|
|
||||||
lid, err := strconv.Atoi(main[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrWrongCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Show{
|
|
||||||
args: ShowArgs{
|
|
||||||
localID: lid,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Show struct {
|
|
||||||
args ShowArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Show) Do(deps Dependencies) (CommandResult, error) {
|
|
||||||
id, err := deps.LocalIDRepo.FindOne(s.args.localID)
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, storage.ErrNotFound):
|
|
||||||
return nil, fmt.Errorf("could not find local id")
|
|
||||||
case err != nil:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 sr.Task.Recurrer != nil {
|
|
||||||
recurStr = sr.Task.Recurrer.String()
|
|
||||||
}
|
|
||||||
data := [][]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", s.Task.ID},
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("\n%s\n", format.Table(data))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
package command_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestShow(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
taskRepo := memory.NewTask()
|
|
||||||
localRepo := memory.NewLocalID()
|
|
||||||
tsk := item.Task{
|
|
||||||
ID: "id",
|
|
||||||
Date: item.NewDate(2024, 10, 7),
|
|
||||||
TaskBody: item.TaskBody{
|
|
||||||
Title: "name",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := taskRepo.Store(tsk); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
if err := localRepo.Store(tsk.ID, 1); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
main []string
|
|
||||||
expData [][]string
|
|
||||||
expParseErr bool
|
|
||||||
expDoErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
main: []string{},
|
|
||||||
expParseErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "wrong",
|
|
||||||
main: []string{"delete"},
|
|
||||||
expParseErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "local id",
|
|
||||||
main: []string{"1"},
|
|
||||||
expData: [][]string{
|
|
||||||
{"title", tsk.Title},
|
|
||||||
{"local id", fmt.Sprintf("%d", 1)},
|
|
||||||
{"date", tsk.Date.String()},
|
|
||||||
{"time", tsk.Time.String()},
|
|
||||||
{"duration", tsk.Duration.String()},
|
|
||||||
{"recur", ""},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
if tc.expParseErr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// do
|
|
||||||
_, actDoErr := cmd.Do(command.Dependencies{
|
|
||||||
TaskRepo: taskRepo,
|
|
||||||
LocalIDRepo: localRepo,
|
|
||||||
})
|
|
||||||
if tc.expDoErr != (actDoErr != nil) {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expDoErr, actDoErr != nil)
|
|
||||||
}
|
|
||||||
if tc.expDoErr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// if diff := cmp.Diff(tc.expData, actData); diff != "" {
|
|
||||||
// t.Errorf("(+exp, -got)%s\n", diff)
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -25,37 +25,37 @@ func (sa SyncArgs) Parse(main []string, flags map[string]string) (Command, error
|
||||||
|
|
||||||
type Sync struct{}
|
type Sync struct{}
|
||||||
|
|
||||||
func (s *Sync) Do(deps Dependencies) (CommandResult, error) {
|
func (s *Sync) Do(deps Dependencies) error {
|
||||||
// local new and updated
|
// local new and updated
|
||||||
sendItems, err := deps.SyncRepo.FindAll()
|
sendItems, err := deps.SyncRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get updated items: %v", err)
|
return fmt.Errorf("could not get updated items: %v", err)
|
||||||
}
|
}
|
||||||
if err := deps.SyncClient.Update(sendItems); err != nil {
|
if err := deps.SyncClient.Update(sendItems); err != nil {
|
||||||
return nil, fmt.Errorf("could not send updated items: %v", err)
|
return fmt.Errorf("could not send updated items: %v", err)
|
||||||
}
|
}
|
||||||
if err := deps.SyncRepo.DeleteAll(); err != nil {
|
if err := deps.SyncRepo.DeleteAll(); err != nil {
|
||||||
return nil, fmt.Errorf("could not clear updated items: %v", err)
|
return fmt.Errorf("could not clear updated items: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get new/updated items
|
// get new/updated items
|
||||||
ts, err := deps.SyncRepo.LastUpdate()
|
ts, err := deps.SyncRepo.LastUpdate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find timestamp of last update: %v", err)
|
return fmt.Errorf("could not find timestamp of last update: %v", err)
|
||||||
}
|
}
|
||||||
recItems, err := deps.SyncClient.Updated([]item.Kind{item.KindTask}, ts)
|
recItems, err := deps.SyncClient.Updated([]item.Kind{item.KindTask}, ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not receive updates: %v", err)
|
return fmt.Errorf("could not receive updates: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
updated := make([]item.Item, 0)
|
updated := make([]item.Item, 0)
|
||||||
for _, ri := range recItems {
|
for _, ri := range recItems {
|
||||||
if ri.Deleted {
|
if ri.Deleted {
|
||||||
if err := deps.LocalIDRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
if err := deps.LocalIDRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||||
return nil, fmt.Errorf("could not delete local id: %v", err)
|
return fmt.Errorf("could not delete local id: %v", err)
|
||||||
}
|
}
|
||||||
if err := deps.TaskRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
if err := deps.TaskRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||||
return nil, fmt.Errorf("could not delete task: %v", err)
|
return fmt.Errorf("could not delete task: %v", err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -64,12 +64,12 @@ func (s *Sync) Do(deps Dependencies) (CommandResult, error) {
|
||||||
|
|
||||||
lidMap, err := deps.LocalIDRepo.FindAll()
|
lidMap, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
for _, u := range updated {
|
for _, u := range updated {
|
||||||
var tskBody item.TaskBody
|
var tskBody item.TaskBody
|
||||||
if err := json.Unmarshal([]byte(u.Body), &tskBody); err != nil {
|
if err := json.Unmarshal([]byte(u.Body), &tskBody); err != nil {
|
||||||
return nil, fmt.Errorf("could not unmarshal task body: %v", err)
|
return fmt.Errorf("could not unmarshal task body: %v", err)
|
||||||
}
|
}
|
||||||
tsk := item.Task{
|
tsk := item.Task{
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
|
@ -79,24 +79,20 @@ func (s *Sync) Do(deps Dependencies) (CommandResult, error) {
|
||||||
TaskBody: tskBody,
|
TaskBody: tskBody,
|
||||||
}
|
}
|
||||||
if err := deps.TaskRepo.Store(tsk); err != nil {
|
if err := deps.TaskRepo.Store(tsk); err != nil {
|
||||||
return nil, fmt.Errorf("could not store task: %v", err)
|
return fmt.Errorf("could not store task: %v", err)
|
||||||
}
|
}
|
||||||
lid, ok := lidMap[u.ID]
|
lid, ok := lidMap[u.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
lid, err = deps.LocalIDRepo.Next()
|
lid, err = deps.LocalIDRepo.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get next local id: %v", err)
|
return fmt.Errorf("could not get next local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deps.LocalIDRepo.Store(u.ID, lid); err != nil {
|
if err := deps.LocalIDRepo.Store(u.ID, lid); err != nil {
|
||||||
return nil, fmt.Errorf("could not store local id: %v", err)
|
return fmt.Errorf("could not store local id: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SyncResult{}, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncResult struct{}
|
|
||||||
|
|
||||||
func (sr SyncResult) Render() string { return "tasks synced" }
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"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/plan/storage/memory"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
)
|
)
|
||||||
|
@ -81,7 +80,7 @@ func TestSyncSend(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
if _, err := cmd.Do(command.Dependencies{
|
if err := cmd.Do(command.Dependencies{
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localIDRepo,
|
LocalIDRepo: localIDRepo,
|
||||||
SyncRepo: syncRepo,
|
SyncRepo: syncRepo,
|
||||||
|
@ -207,7 +206,7 @@ func TestSyncReceive(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
if _, err := cmd.Do(command.Dependencies{
|
if err := cmd.Do(command.Dependencies{
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localIDRepo,
|
LocalIDRepo: localIDRepo,
|
||||||
SyncRepo: syncRepo,
|
SyncRepo: syncRepo,
|
||||||
|
@ -217,7 +216,7 @@ func TestSyncReceive(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check result
|
// check result
|
||||||
actTasks, err := taskRepo.FindMany(storage.TaskListParams{})
|
actTasks, err := taskRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UpdateArgs struct {
|
type UpdateArgs struct {
|
||||||
|
@ -24,10 +22,10 @@ type UpdateArgs struct {
|
||||||
func NewUpdateArgs() UpdateArgs {
|
func NewUpdateArgs() UpdateArgs {
|
||||||
return UpdateArgs{
|
return UpdateArgs{
|
||||||
fieldTPL: map[string][]string{
|
fieldTPL: map[string][]string{
|
||||||
"date": {"d", "date", "on"},
|
"date": []string{"d", "date", "on"},
|
||||||
"time": {"t", "time", "at"},
|
"time": []string{"t", "time", "at"},
|
||||||
"duration": {"dur", "duration", "for"},
|
"duration": []string{"dur", "duration", "for"},
|
||||||
"recurrer": {"rec", "recurrer"},
|
"recurrer": []string{"rec", "recurrer"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,18 +83,24 @@ type Update struct {
|
||||||
args UpdateArgs
|
args UpdateArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) Do(deps Dependencies) (CommandResult, error) {
|
func (u *Update) Do(deps Dependencies) error {
|
||||||
id, err := deps.LocalIDRepo.FindOne(u.args.LocalID)
|
var id string
|
||||||
switch {
|
idMap, err := deps.LocalIDRepo.FindAll()
|
||||||
case errors.Is(err, storage.ErrNotFound):
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find local id")
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
case err != nil:
|
}
|
||||||
return nil, err
|
for tid, lid := range idMap {
|
||||||
|
if u.args.LocalID == lid {
|
||||||
|
id = tid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id == "" {
|
||||||
|
return fmt.Errorf("could not find local id")
|
||||||
}
|
}
|
||||||
|
|
||||||
tsk, err := deps.TaskRepo.FindOne(id)
|
tsk, err := deps.TaskRepo.Find(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find task")
|
return fmt.Errorf("could not find task")
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.args.Title != "" {
|
if u.args.Title != "" {
|
||||||
|
@ -117,26 +121,20 @@ func (u *Update) Do(deps Dependencies) (CommandResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tsk.Valid() {
|
if !tsk.Valid() {
|
||||||
return nil, fmt.Errorf("task is unvalid")
|
return fmt.Errorf("task is unvalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deps.TaskRepo.Store(tsk); err != nil {
|
if err := deps.TaskRepo.Store(tsk); err != nil {
|
||||||
return nil, fmt.Errorf("could not store task: %v", err)
|
return fmt.Errorf("could not store task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
it, err := tsk.Item()
|
it, err := tsk.Item()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not convert task to sync item: %v", err)
|
return fmt.Errorf("could not convert task to sync item: %v", err)
|
||||||
}
|
}
|
||||||
if err := deps.SyncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return nil, fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateResult{}, nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateResult struct{}
|
|
||||||
|
|
||||||
func (ur UpdateResult) Render() string {
|
|
||||||
return "task updated"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,6 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// setup
|
|
||||||
taskRepo := memory.NewTask()
|
taskRepo := memory.NewTask()
|
||||||
localIDRepo := memory.NewLocalID()
|
localIDRepo := memory.NewLocalID()
|
||||||
syncRepo := memory.NewSync()
|
syncRepo := memory.NewSync()
|
||||||
|
@ -183,7 +182,6 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
t.Errorf("exp nil, ,got %v", err)
|
t.Errorf("exp nil, ,got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse
|
|
||||||
cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields)
|
cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields)
|
||||||
if tc.expParseErr != (actErr != nil) {
|
if tc.expParseErr != (actErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
|
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
|
||||||
|
@ -191,22 +189,19 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
if tc.expParseErr {
|
if tc.expParseErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
actDoErr := cmd.Do(command.Dependencies{
|
||||||
// do
|
|
||||||
_, actDoErr := cmd.Do(command.Dependencies{
|
|
||||||
TaskRepo: taskRepo,
|
TaskRepo: taskRepo,
|
||||||
LocalIDRepo: localIDRepo,
|
LocalIDRepo: localIDRepo,
|
||||||
SyncRepo: syncRepo,
|
SyncRepo: syncRepo,
|
||||||
})
|
}) != nil
|
||||||
if tc.expDoErr != (actDoErr != nil) {
|
if tc.expDoErr != actDoErr {
|
||||||
t.Errorf("exp %v, got %v", tc.expDoErr, actDoErr)
|
t.Errorf("exp %v, got %v", tc.expDoErr, actDoErr)
|
||||||
}
|
}
|
||||||
if tc.expDoErr {
|
if tc.expDoErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check
|
actTask, err := taskRepo.Find(tskID)
|
||||||
actTask, err := taskRepo.FindOne(tskID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Table(data [][]string) string {
|
|
||||||
if len(data) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// make all cells in a column the same width
|
|
||||||
max := make([]int, len(data[0]))
|
|
||||||
for _, row := range data {
|
|
||||||
for c, cell := range row {
|
|
||||||
if len(cell) > max[c] {
|
|
||||||
max[c] = len(cell)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for r, row := range data {
|
|
||||||
for c, cell := range row {
|
|
||||||
for s := len(cell); s < max[c]; s++ {
|
|
||||||
data[r][c] += " "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make it smaller if the result is too wide
|
|
||||||
// only by making the widest column smaller for now
|
|
||||||
maxWidth := findTermWidth()
|
|
||||||
if maxWidth != 0 {
|
|
||||||
width := len(max) - 1
|
|
||||||
for _, m := range max {
|
|
||||||
width += m
|
|
||||||
}
|
|
||||||
shortenWith := width - maxWidth
|
|
||||||
widestColNo, widestColLen := 0, 0
|
|
||||||
for i, m := range max {
|
|
||||||
if m > widestColLen {
|
|
||||||
widestColNo, widestColLen = i, m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newTaskColWidth := max[widestColNo] - shortenWith
|
|
||||||
if newTaskColWidth < 0 {
|
|
||||||
return "table is too wide to display\n"
|
|
||||||
}
|
|
||||||
if newTaskColWidth < max[widestColNo] {
|
|
||||||
for r, row := range data {
|
|
||||||
data[r][widestColNo] = row[widestColNo][:newTaskColWidth]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// print the rows
|
|
||||||
var output string
|
|
||||||
for r, row := range data {
|
|
||||||
if r%3 == 0 {
|
|
||||||
output += fmt.Sprintf("%s", "\x1b[48;5;237m")
|
|
||||||
}
|
|
||||||
for c, col := range row {
|
|
||||||
output += col
|
|
||||||
if c != len(row)-1 {
|
|
||||||
output += " "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r%3 == 0 {
|
|
||||||
output += fmt.Sprintf("%s", "\x1b[49m")
|
|
||||||
}
|
|
||||||
output += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func findTermWidth() int {
|
|
||||||
cmd := exec.Command("stty", "size")
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
s := string(out)
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
sArr := strings.Split(s, " ")
|
|
||||||
|
|
||||||
width, err := strconv.Atoi(sArr[1])
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return width
|
|
||||||
}
|
|
|
@ -18,19 +18,6 @@ func NewLocalID() *LocalID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ml *LocalID) FindOne(lid int) (string, error) {
|
|
||||||
ml.mutex.RLock()
|
|
||||||
defer ml.mutex.RUnlock()
|
|
||||||
|
|
||||||
for id, l := range ml.ids {
|
|
||||||
if lid == l {
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", storage.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ml *LocalID) FindAll() (map[string]int, error) {
|
func (ml *LocalID) FindAll() (map[string]int, error) {
|
||||||
ml.mutex.RLock()
|
ml.mutex.RLock()
|
||||||
defer ml.mutex.RUnlock()
|
defer ml.mutex.RUnlock()
|
||||||
|
|
|
@ -54,21 +54,6 @@ func TestLocalID(t *testing.T) {
|
||||||
t.Errorf("exp 2, got %v", actLid)
|
t.Errorf("exp 2, got %v", actLid)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("find by local id")
|
|
||||||
actID, actErr := repo.FindOne(1)
|
|
||||||
if actErr != nil {
|
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
|
||||||
}
|
|
||||||
if actID != "test" {
|
|
||||||
t.Errorf("exp test, got %v", actID)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("unknown local id")
|
|
||||||
actID, actErr = repo.FindOne(2)
|
|
||||||
if !errors.Is(actErr, storage.ErrNotFound) {
|
|
||||||
t.Errorf("exp ErrNotFound, got %v", actErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
actIDs, actErr = repo.FindAll()
|
actIDs, actErr = repo.FindAll()
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
|
|
|
@ -19,7 +19,7 @@ func NewTask() *Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) FindOne(id string) (item.Task, error) {
|
func (t *Task) Find(id string) (item.Task, error) {
|
||||||
t.mutex.RLock()
|
t.mutex.RLock()
|
||||||
defer t.mutex.RUnlock()
|
defer t.mutex.RUnlock()
|
||||||
|
|
||||||
|
@ -30,16 +30,14 @@ func (t *Task) FindOne(id string) (item.Task, error) {
|
||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) FindMany(params storage.TaskListParams) ([]item.Task, error) {
|
func (t *Task) FindAll() ([]item.Task, error) {
|
||||||
t.mutex.RLock()
|
t.mutex.RLock()
|
||||||
defer t.mutex.RUnlock()
|
defer t.mutex.RUnlock()
|
||||||
|
|
||||||
tasks := make([]item.Task, 0, len(t.tasks))
|
tasks := make([]item.Task, 0, len(t.tasks))
|
||||||
for _, tsk := range t.tasks {
|
for _, tsk := range t.tasks {
|
||||||
if storage.Match(tsk, params) {
|
|
||||||
tasks = append(tasks, tsk)
|
tasks = append(tasks, tsk)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
sort.Slice(tasks, func(i, j int) bool {
|
sort.Slice(tasks, func(i, j int) bool {
|
||||||
return tasks[i].ID < tasks[j].ID
|
return tasks[i].ID < tasks[j].ID
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTask(t *testing.T) {
|
func TestTask(t *testing.T) {
|
||||||
|
@ -13,7 +12,7 @@ func TestTask(t *testing.T) {
|
||||||
mem := NewTask()
|
mem := NewTask()
|
||||||
|
|
||||||
t.Log("empty")
|
t.Log("empty")
|
||||||
actTasks, actErr := mem.FindMany(storage.TaskListParams{})
|
actTasks, actErr := mem.FindAll()
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +23,6 @@ func TestTask(t *testing.T) {
|
||||||
t.Log("store")
|
t.Log("store")
|
||||||
tsk1 := item.Task{
|
tsk1 := item.Task{
|
||||||
ID: "id-1",
|
ID: "id-1",
|
||||||
Date: item.NewDate(2024, 12, 29),
|
|
||||||
}
|
}
|
||||||
if err := mem.Store(tsk1); err != nil {
|
if err := mem.Store(tsk1); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
@ -38,7 +36,7 @@ func TestTask(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("find one")
|
t.Log("find one")
|
||||||
actTask, actErr := mem.FindOne(tsk1.ID)
|
actTask, actErr := mem.Find(tsk1.ID)
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -47,23 +45,11 @@ func TestTask(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("find all")
|
t.Log("find all")
|
||||||
actTasks, actErr = mem.FindMany(storage.TaskListParams{})
|
actTasks, actErr = mem.FindAll()
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
if diff := item.TaskDiffs([]item.Task{tsk1, tsk2}, actTasks); diff != "" {
|
if diff := item.TaskDiffs([]item.Task{tsk1, tsk2}, actTasks); diff != "" {
|
||||||
t.Errorf("(exp -, got +)\n%s", 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
|
@ -12,23 +11,6 @@ type LocalID struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LocalID) FindOne(lid int) (string, error) {
|
|
||||||
var id string
|
|
||||||
err := l.db.QueryRow(`
|
|
||||||
SELECT id
|
|
||||||
FROM localids
|
|
||||||
WHERE local_id = ?
|
|
||||||
`, lid).Scan(&id)
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return "", storage.ErrNotFound
|
|
||||||
case err != nil:
|
|
||||||
return "", fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LocalID) FindAll() (map[string]int, error) {
|
func (l *LocalID) FindAll() (map[string]int, error) {
|
||||||
rows, err := l.db.Query(`
|
rows, err := l.db.Query(`
|
||||||
SELECT id, local_id
|
SELECT id, local_id
|
||||||
|
|
|
@ -38,7 +38,7 @@ recurrer=?
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *SqliteTask) FindOne(id string) (item.Task, error) {
|
func (t *SqliteTask) Find(id string) (item.Task, error) {
|
||||||
var tsk item.Task
|
var tsk item.Task
|
||||||
var dateStr, timeStr, recurStr, durStr string
|
var dateStr, timeStr, recurStr, durStr string
|
||||||
err := t.db.QueryRow(`
|
err := t.db.QueryRow(`
|
||||||
|
@ -63,35 +63,14 @@ WHERE id = ?`, id).Scan(&tsk.ID, &tsk.Title, &dateStr, &timeStr, &durStr, &recur
|
||||||
return tsk, nil
|
return tsk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *SqliteTask) FindMany(params storage.TaskListParams) ([]item.Task, error) {
|
func (t *SqliteTask) FindAll() ([]item.Task, error) {
|
||||||
query := `SELECT id, title, date, time, duration, recurrer FROM tasks`
|
rows, err := t.db.Query(`
|
||||||
args := []interface{}{}
|
SELECT id, title, date, time, duration, recurrer
|
||||||
where := []string{}
|
FROM tasks`)
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
}
|
}
|
||||||
tasks := make([]item.Task, 0)
|
result := make([]item.Task, 0)
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var tsk item.Task
|
var tsk item.Task
|
||||||
|
@ -108,10 +87,10 @@ func (t *SqliteTask) FindMany(params storage.TaskListParams) ([]item.Task, error
|
||||||
tsk.Duration = dur
|
tsk.Duration = dur
|
||||||
tsk.Recurrer = item.NewRecurrer(recurStr)
|
tsk.Recurrer = item.NewRecurrer(recurStr)
|
||||||
|
|
||||||
tasks = append(tasks, tsk)
|
result = append(result, tsk)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasks, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqliteTask) Delete(id string) error {
|
func (s *SqliteTask) Delete(id string) error {
|
||||||
|
|
|
@ -13,7 +13,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LocalID interface {
|
type LocalID interface {
|
||||||
FindOne(lid int) (string, error)
|
|
||||||
FindAll() (map[string]int, error)
|
FindAll() (map[string]int, error)
|
||||||
FindOrNext(id string) (int, error)
|
FindOrNext(id string) (int, error)
|
||||||
Next() (int, error)
|
Next() (int, error)
|
||||||
|
@ -28,35 +27,13 @@ type Sync interface {
|
||||||
LastUpdate() (time.Time, error)
|
LastUpdate() (time.Time, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskListParams struct {
|
|
||||||
Recurrer bool
|
|
||||||
Date item.Date
|
|
||||||
IncludeBefore bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Task interface {
|
type Task interface {
|
||||||
Store(task item.Task) error
|
Store(task item.Task) error
|
||||||
FindOne(id string) (item.Task, error)
|
Find(id string) (item.Task, error)
|
||||||
FindMany(params TaskListParams) ([]item.Task, error)
|
FindAll() ([]item.Task, error)
|
||||||
Delete(id string) 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 {
|
func NextLocalID(used []int) int {
|
||||||
if len(used) == 0 {
|
if len(used) == 0 {
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -3,60 +3,10 @@ package storage_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"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) {
|
func TestNextLocalId(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
used []int
|
used []int
|
||||||
|
|
Loading…
Reference in New Issue