refactor args and cmds
This commit is contained in:
parent
2a62e6c335
commit
c209ba6197
|
@ -3,109 +3,107 @@ package command
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Add struct {
|
type AddArgs struct {
|
||||||
localIDRepo storage.LocalID
|
fieldTPL map[string][]string
|
||||||
taskRepo storage.Task
|
task item.Task
|
||||||
syncRepo storage.Sync
|
|
||||||
argSet *ArgSet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAdd(localRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
|
func NewAddArgs() AddArgs {
|
||||||
return &Add{
|
return AddArgs{
|
||||||
localIDRepo: localRepo,
|
fieldTPL: map[string][]string{
|
||||||
taskRepo: taskRepo,
|
"date": []string{"d", "date", "on"},
|
||||||
syncRepo: syncRepo,
|
"time": []string{"t", "time", "at"},
|
||||||
argSet: &ArgSet{
|
"duration": []string{"dur", "duration", "for"},
|
||||||
Flags: map[string]Flag{
|
"recurrer": []string{"rec", "recurrer"},
|
||||||
FlagOn: &FlagDate{},
|
|
||||||
FlagAt: &FlagTime{},
|
|
||||||
FlagFor: &FlagDuration{},
|
|
||||||
FlagRec: &FlagRecurrer{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (add *Add) Execute(main []string, flags map[string]string) error {
|
func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
||||||
if len(main) == 0 || main[0] != "add" {
|
if len(main) == 0 || main[0] != "add" {
|
||||||
return ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
as := add.argSet
|
main = main[1:]
|
||||||
if len(main) > 1 {
|
if len(main) == 0 {
|
||||||
as.Main = strings.Join(main[1:], " ")
|
return nil, fmt.Errorf("%w: title is required for add", ErrInvalidArg)
|
||||||
}
|
|
||||||
for k := range as.Flags {
|
|
||||||
v, ok := flags[k]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := as.Set(k, v); err != nil {
|
|
||||||
return fmt.Errorf("could not set %s: %v", k, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if as.Main == "" {
|
|
||||||
return fmt.Errorf("%w: title is required", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if !as.IsSet(FlagOn) {
|
|
||||||
return fmt.Errorf("%w: date is required", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if !as.IsSet(FlagAt) && as.IsSet(FlagFor) {
|
|
||||||
return fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if as.IsSet(FlagAt) && !as.IsSet(FlagFor) {
|
|
||||||
if err := as.Flags[FlagFor].Set("1h"); err != nil {
|
|
||||||
return fmt.Errorf("could not set duration to one hour")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !as.IsSet(FlagAt) && !as.IsSet(FlagFor) {
|
|
||||||
if err := as.Flags[FlagFor].Set("24h"); err != nil {
|
|
||||||
return fmt.Errorf("could not set duration to 24 hours")
|
|
||||||
}
|
}
|
||||||
|
fields, err := ResolveFields(fields, aa.fieldTPL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return add.do()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (add *Add) do() error {
|
|
||||||
as := add.argSet
|
|
||||||
rec := as.GetRecurrer(FlagRec)
|
|
||||||
tsk := item.Task{
|
tsk := item.Task{
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Date: as.GetDate(FlagOn),
|
|
||||||
Recurrer: rec,
|
|
||||||
TaskBody: item.TaskBody{
|
TaskBody: item.TaskBody{
|
||||||
Title: as.Main,
|
Title: strings.Join(main, ","),
|
||||||
Time: as.GetTime(FlagAt),
|
|
||||||
Duration: as.GetDuration(FlagFor),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if rec != nil {
|
|
||||||
tsk.RecurNext = rec.First()
|
if val, ok := fields["date"]; ok {
|
||||||
|
d := item.NewDateFromString(val)
|
||||||
|
if d.IsZero() {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
tsk.Date = d
|
||||||
|
}
|
||||||
|
if val, ok := fields["time"]; ok {
|
||||||
|
t := item.NewTimeFromString(val)
|
||||||
|
if t.IsZero() {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
tsk.Time = t
|
||||||
|
}
|
||||||
|
if val, ok := fields["duration"]; ok {
|
||||||
|
d, err := time.ParseDuration(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
tsk.Duration = d
|
||||||
|
}
|
||||||
|
if val, ok := fields["recurrer"]; ok {
|
||||||
|
rec := item.NewRecurrer(val)
|
||||||
|
if rec == nil {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
tsk.Recurrer = rec
|
||||||
|
tsk.RecurNext = tsk.Recurrer.First()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := add.taskRepo.Store(tsk); err != nil {
|
return &Add{
|
||||||
|
args: AddArgs{
|
||||||
|
task: tsk,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Add struct {
|
||||||
|
args AddArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Add) Do(deps Dependencies) error {
|
||||||
|
if err := deps.TaskRepo.Store(a.args.task); err != nil {
|
||||||
return fmt.Errorf("could not store event: %v", err)
|
return fmt.Errorf("could not store event: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localID, err := add.localIDRepo.Next()
|
localID, err := deps.LocalIDRepo.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create next local id: %v", err)
|
return fmt.Errorf("could not create next local id: %v", err)
|
||||||
}
|
}
|
||||||
if err := add.localIDRepo.Store(tsk.ID, localID); err != nil {
|
if err := deps.LocalIDRepo.Store(a.args.task.ID, localID); err != nil {
|
||||||
return fmt.Errorf("could not store local id: %v", err)
|
return fmt.Errorf("could not store local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
it, err := tsk.Item()
|
it, err := a.args.task.Item()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 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 := add.syncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,14 +14,13 @@ func TestAdd(t *testing.T) {
|
||||||
|
|
||||||
aDate := item.NewDate(2024, 11, 2)
|
aDate := item.NewDate(2024, 11, 2)
|
||||||
aTime := item.NewTime(12, 0)
|
aTime := item.NewTime(12, 0)
|
||||||
aDay := time.Duration(24) * time.Hour
|
|
||||||
anHourStr := "1h"
|
anHourStr := "1h"
|
||||||
anHour := time.Hour
|
anHour := time.Hour
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
main []string
|
main []string
|
||||||
flags map[string]string
|
fields map[string]string
|
||||||
expErr bool
|
expErr bool
|
||||||
expTask item.Task
|
expTask item.Task
|
||||||
}{
|
}{
|
||||||
|
@ -32,38 +31,18 @@ func TestAdd(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "title missing",
|
name: "title missing",
|
||||||
main: []string{"add"},
|
main: []string{"add"},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
command.FlagOn: aDate.String(),
|
"date": aDate.String(),
|
||||||
},
|
},
|
||||||
expErr: true,
|
expErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "date missing",
|
name: "date time duration",
|
||||||
main: []string{"add", "some", "title"},
|
|
||||||
expErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only date",
|
|
||||||
main: []string{"add", "title"},
|
main: []string{"add", "title"},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
command.FlagOn: aDate.String(),
|
"date": aDate.String(),
|
||||||
},
|
"time": aTime.String(),
|
||||||
expTask: item.Task{
|
"duration": anHourStr,
|
||||||
ID: "title",
|
|
||||||
Date: aDate,
|
|
||||||
TaskBody: item.TaskBody{
|
|
||||||
Title: "title",
|
|
||||||
Duration: aDay,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "date, time and duration",
|
|
||||||
main: []string{"add", "title"},
|
|
||||||
flags: map[string]string{
|
|
||||||
command.FlagOn: aDate.String(),
|
|
||||||
command.FlagAt: aTime.String(),
|
|
||||||
command.FlagFor: anHourStr,
|
|
||||||
},
|
},
|
||||||
expTask: item.Task{
|
expTask: item.Task{
|
||||||
ID: "title",
|
ID: "title",
|
||||||
|
@ -75,28 +54,25 @@ func TestAdd(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "date and duration",
|
|
||||||
main: []string{"add", "title"},
|
|
||||||
flags: map[string]string{
|
|
||||||
command.FlagOn: aDate.String(),
|
|
||||||
command.FlagFor: anHourStr,
|
|
||||||
},
|
|
||||||
expErr: true,
|
|
||||||
},
|
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
taskRepo := memory.NewTask()
|
taskRepo := memory.NewTask()
|
||||||
localRepo := memory.NewLocalID()
|
localIDRepo := memory.NewLocalID()
|
||||||
syncRepo := memory.NewSync()
|
syncRepo := memory.NewSync()
|
||||||
cmd := command.NewAdd(localRepo, taskRepo, syncRepo)
|
cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields)
|
||||||
actParseErr := cmd.Execute(tc.main, tc.flags) != nil
|
if tc.expErr != (actParseErr != nil) {
|
||||||
if tc.expErr != actParseErr {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
||||||
}
|
}
|
||||||
if tc.expErr {
|
if tc.expErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localIDRepo,
|
||||||
|
SyncRepo: syncRepo,
|
||||||
|
}); err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actTasks, err := taskRepo.FindAll()
|
actTasks, err := taskRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -106,7 +82,7 @@ func TestAdd(t *testing.T) {
|
||||||
t.Errorf("exp 1, got %d", len(actTasks))
|
t.Errorf("exp 1, got %d", len(actTasks))
|
||||||
}
|
}
|
||||||
|
|
||||||
actLocalIDs, err := localRepo.FindAll()
|
actLocalIDs, err := localIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ArgSet struct {
|
|
||||||
Main string
|
|
||||||
Flags map[string]Flag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) Set(name, val string) error {
|
|
||||||
f, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown flag %s", name)
|
|
||||||
}
|
|
||||||
return f.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) IsSet(name string) bool {
|
|
||||||
f, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return f.IsSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetString(name string) string {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(string)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetDate(name string) item.Date {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return item.Date{}
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(item.Date)
|
|
||||||
if !ok {
|
|
||||||
return item.Date{}
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetTime(name string) item.Time {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return item.Time{}
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(item.Time)
|
|
||||||
if !ok {
|
|
||||||
return item.Time{}
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetDuration(name string) time.Duration {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return time.Duration(0)
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(time.Duration)
|
|
||||||
if !ok {
|
|
||||||
return time.Duration(0)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetRecurrer(name string) item.Recurrer {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(item.Recurrer)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *ArgSet) GetInt(name string) int {
|
|
||||||
flag, ok := as.Flags[name]
|
|
||||||
if !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
val, ok := flag.Get().(int)
|
|
||||||
if !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
package command_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArgSet(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
flags map[string]command.Flag
|
|
||||||
flagName string
|
|
||||||
setValue string
|
|
||||||
exp interface{}
|
|
||||||
expErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "string flag success",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"title": &command.FlagString{Name: "title"},
|
|
||||||
},
|
|
||||||
flagName: "title",
|
|
||||||
setValue: "test title",
|
|
||||||
exp: "test title",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "date flag success",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"date": &command.FlagDate{Name: "date"},
|
|
||||||
},
|
|
||||||
flagName: "date",
|
|
||||||
setValue: "2024-01-02",
|
|
||||||
exp: time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "time flag success",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"time": &command.FlagTime{Name: "time"},
|
|
||||||
},
|
|
||||||
flagName: "time",
|
|
||||||
setValue: "15:04",
|
|
||||||
exp: time.Date(0, 1, 1, 15, 4, 0, 0, time.UTC),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "duration flag success",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"duration": &command.FlagDuration{Name: "duration"},
|
|
||||||
},
|
|
||||||
flagName: "duration",
|
|
||||||
setValue: "2h30m",
|
|
||||||
exp: 2*time.Hour + 30*time.Minute,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "recur period flag success",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"recur": &command.FlagRecurrer{Name: "recur"},
|
|
||||||
},
|
|
||||||
flagName: "recur",
|
|
||||||
setValue: "2024-12-23, daily",
|
|
||||||
exp: item.NewRecurrer("2024-12-23, daily"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown flag error",
|
|
||||||
flags: map[string]command.Flag{},
|
|
||||||
flagName: "unknown",
|
|
||||||
setValue: "value",
|
|
||||||
expErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid date format error",
|
|
||||||
flags: map[string]command.Flag{
|
|
||||||
"date": &command.FlagDate{Name: "date"},
|
|
||||||
},
|
|
||||||
flagName: "date",
|
|
||||||
setValue: "invalid",
|
|
||||||
expErr: true,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
as := &command.ArgSet{
|
|
||||||
Main: "test",
|
|
||||||
Flags: tt.flags,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := as.Set(tt.flagName, tt.setValue)
|
|
||||||
if (err != nil) != tt.expErr {
|
|
||||||
t.Errorf("ArgSet.Set() error = %v, expErr %v", err, tt.expErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.expErr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !as.IsSet(tt.flagName) {
|
|
||||||
t.Errorf("ArgSet.IsSet() = false, want true for flag %s", tt.flagName)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,66 +3,104 @@ package command
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FlagTitle = "title"
|
DateFormat = "2006-01-02"
|
||||||
FlagOn = "on"
|
TimeFormat = "15:04"
|
||||||
FlagAt = "at"
|
|
||||||
FlagFor = "for"
|
|
||||||
FlagRec = "rec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrWrongCommand = errors.New("wrong command")
|
||||||
|
ErrInvalidArg = errors.New("invalid argument")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dependencies struct {
|
||||||
|
LocalIDRepo storage.LocalID
|
||||||
|
TaskRepo storage.Task
|
||||||
|
SyncRepo storage.Sync
|
||||||
|
SyncClient client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandArgs interface {
|
||||||
|
Parse(main []string, fields map[string]string) (Command, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Command interface {
|
type Command interface {
|
||||||
Execute([]string, map[string]string) error
|
Do(deps Dependencies) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
Commands []Command
|
deps Dependencies
|
||||||
|
cmdArgs []CommandArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCLI(deps Dependencies) *CLI {
|
||||||
|
return &CLI{
|
||||||
|
deps: deps,
|
||||||
|
cmdArgs: []CommandArgs{
|
||||||
|
NewAddArgs(), NewDeleteArgs(), NewListArgs(),
|
||||||
|
NewSyncArgs(), NewUpdateArgs(),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) Run(args []string) error {
|
func (cli *CLI) Run(args []string) error {
|
||||||
main, flags, err := ParseFlags(args)
|
main, flags := FindFields(args)
|
||||||
if err != nil {
|
for _, ca := range cli.cmdArgs {
|
||||||
return err
|
cmd, err := ca.Parse(main, flags)
|
||||||
}
|
|
||||||
for _, c := range cli.Commands {
|
|
||||||
err := c.Execute(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:
|
default:
|
||||||
return nil
|
return cmd.Do(cli.deps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("could not find matching command")
|
return fmt.Errorf("could not find matching command")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseFlags(args []string) ([]string, map[string]string, error) {
|
func FindFields(args []string) ([]string, map[string]string) {
|
||||||
flags := make(map[string]string)
|
fields := make(map[string]string)
|
||||||
main := make([]string, 0)
|
main := make([]string, 0)
|
||||||
var inMain bool
|
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
if strings.HasPrefix(args[i], "-") {
|
if k, v, ok := strings.Cut(args[i], ":"); ok && !strings.Contains(k, " ") {
|
||||||
inMain = false
|
fields[k] = v
|
||||||
if i+1 >= len(args) {
|
|
||||||
return nil, nil, fmt.Errorf("flag wihout value")
|
|
||||||
}
|
|
||||||
flags[strings.TrimPrefix(args[i], "-")] = args[i+1]
|
|
||||||
i++
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !inMain && len(main) > 0 {
|
|
||||||
return nil, nil, fmt.Errorf("two mains")
|
|
||||||
}
|
|
||||||
inMain = true
|
|
||||||
main = append(main, args[i])
|
main = append(main, args[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
return main, flags, nil
|
return main, fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveFields(fields map[string]string, tmpl map[string][]string) (map[string]string, error) {
|
||||||
|
res := make(map[string]string)
|
||||||
|
for k, v := range fields {
|
||||||
|
for tk, tv := range tmpl {
|
||||||
|
if slices.Contains(tv, k) {
|
||||||
|
if _, ok := res[tk]; ok {
|
||||||
|
return nil, fmt.Errorf("%w: duplicate field: %v", ErrInvalidArg, tk)
|
||||||
|
}
|
||||||
|
res[tk] = v
|
||||||
|
delete(fields, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(fields) > 0 {
|
||||||
|
ks := make([]string, 0, len(fields))
|
||||||
|
for k := range fields {
|
||||||
|
ks = append(ks, k)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%w: unknown field(s): %v", ErrInvalidArg, strings.Join(ks, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,32 +7,31 @@ import (
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseArgs(t *testing.T) {
|
func TestFindFields(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
expMain []string
|
expMain []string
|
||||||
expFlags map[string]string
|
expFields map[string]string
|
||||||
expErr bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
expMain: []string{},
|
expMain: []string{},
|
||||||
expFlags: map[string]string{},
|
expFields: map[string]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "just main",
|
name: "just main",
|
||||||
args: []string{"one", "two three", "four"},
|
args: []string{"one", "two three", "four"},
|
||||||
expMain: []string{"one", "two three", "four"},
|
expMain: []string{"one", "two three", "four"},
|
||||||
expFlags: map[string]string{},
|
expFields: map[string]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with flags",
|
name: "with flags",
|
||||||
args: []string{"-flag1", "value1", "one", "two", "-flag2", "value2", "-flag3", "value3"},
|
args: []string{"flag1:value1", "one", "two", "flag2:value2", "flag3:value3"},
|
||||||
expMain: []string{"one", "two"},
|
expMain: []string{"one", "two"},
|
||||||
expFlags: map[string]string{
|
expFields: map[string]string{
|
||||||
"flag1": "value1",
|
"flag1": "value1",
|
||||||
"flag2": "value2",
|
"flag2": "value2",
|
||||||
"flag3": "value3",
|
"flag3": "value3",
|
||||||
|
@ -40,29 +39,88 @@ func TestParseArgs(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "flag without value",
|
name: "flag without value",
|
||||||
args: []string{"one", "two", "-flag1"},
|
args: []string{"one", "two", "flag1:"},
|
||||||
expErr: true,
|
expMain: []string{"one", "two"},
|
||||||
|
expFields: map[string]string{
|
||||||
|
"flag1": "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "split main",
|
name: "split main",
|
||||||
args: []string{"one", "-flag1", "value1", "two"},
|
args: []string{"one", "flag1:value1", "two"},
|
||||||
expErr: true,
|
expMain: []string{"one", "two"},
|
||||||
|
expFields: map[string]string{
|
||||||
|
"flag1": "value1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
actMain, actFlags, actErr := command.ParseFlags(tc.args)
|
actMain, actFields := command.FindFields(tc.args)
|
||||||
if tc.expErr != (actErr != nil) {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
|
||||||
}
|
|
||||||
if tc.expErr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(tc.expMain, actMain); diff != "" {
|
if diff := cmp.Diff(tc.expMain, actMain); diff != "" {
|
||||||
t.Errorf("(exp +, got -)\n%s", diff)
|
t.Errorf("(exp +, got -)\n%s", diff)
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(tc.expFlags, actFlags); diff != "" {
|
if diff := cmp.Diff(tc.expFields, actFields); diff != "" {
|
||||||
t.Errorf("(exp +, got -)\n%s", diff)
|
t.Errorf("(exp +, got -)\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveFields(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tmpl := map[string][]string{
|
||||||
|
"one": []string{"a", "b"},
|
||||||
|
"two": []string{"c", "d"},
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
fields map[string]string
|
||||||
|
expRes map[string]string
|
||||||
|
expErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
expRes: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
fields: map[string]string{
|
||||||
|
"unknown": "value",
|
||||||
|
},
|
||||||
|
expErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate",
|
||||||
|
fields: map[string]string{
|
||||||
|
"a": "val1",
|
||||||
|
"b": "val2",
|
||||||
|
},
|
||||||
|
expErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
fields: map[string]string{
|
||||||
|
"a": "val1",
|
||||||
|
"d": "val2",
|
||||||
|
},
|
||||||
|
expRes: map[string]string{
|
||||||
|
"one": "val1",
|
||||||
|
"two": "val2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actRes, actErr := command.ResolveFields(tc.fields, tmpl)
|
||||||
|
if tc.expErr != (actErr != nil) {
|
||||||
|
t.Errorf("exp %v, got %v", tc.expErr, actErr != nil)
|
||||||
|
}
|
||||||
|
if tc.expErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.expRes, actRes); diff != "" {
|
||||||
|
t.Errorf("(+exp, -got)%s\n", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,46 +3,45 @@ package command
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Delete struct {
|
type DeleteArgs struct {
|
||||||
localIDRepo storage.LocalID
|
LocalID int
|
||||||
taskRepo storage.Task
|
|
||||||
syncRepo storage.Sync
|
|
||||||
localID int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDelete(localIDRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
|
func NewDeleteArgs() DeleteArgs {
|
||||||
return &Delete{
|
return DeleteArgs{}
|
||||||
localIDRepo: localIDRepo,
|
|
||||||
taskRepo: taskRepo,
|
|
||||||
syncRepo: syncRepo,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (del *Delete) Execute(main []string, flags map[string]string) error {
|
func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
||||||
if len(main) < 2 || main[0] != "delete" {
|
if len(main) < 2 || main[0] != "delete" {
|
||||||
return ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
localID, err := strconv.Atoi(main[1])
|
localID, err := strconv.Atoi(main[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("not a local id: %v", main[1])
|
return nil, fmt.Errorf("not a local id: %v", main[1])
|
||||||
}
|
|
||||||
del.localID = localID
|
|
||||||
|
|
||||||
return del.do()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (del *Delete) do() error {
|
return &Delete{
|
||||||
|
args: DeleteArgs{
|
||||||
|
LocalID: localID,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Delete struct {
|
||||||
|
args DeleteArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (del *Delete) Do(deps Dependencies) error {
|
||||||
var id string
|
var id string
|
||||||
idMap, err := del.localIDRepo.FindAll()
|
idMap, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 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.localID == lid {
|
if del.args.LocalID == lid {
|
||||||
id = tskID
|
id = tskID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +49,7 @@ func (del *Delete) do() error {
|
||||||
return fmt.Errorf("could not find local id")
|
return fmt.Errorf("could not find local id")
|
||||||
}
|
}
|
||||||
|
|
||||||
tsk, err := del.taskRepo.Find(id)
|
tsk, err := deps.TaskRepo.Find(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get task: %v", err)
|
return fmt.Errorf("could not get task: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -60,15 +59,15 @@ func (del *Delete) do() error {
|
||||||
return 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 := del.syncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := del.localIDRepo.Delete(id); err != nil {
|
if err := deps.LocalIDRepo.Delete(id); err != nil {
|
||||||
return fmt.Errorf("could not delete local id: %v", err)
|
return fmt.Errorf("could not delete local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := del.taskRepo.Delete(id); err != nil {
|
if err := deps.TaskRepo.Delete(id); err != nil {
|
||||||
return fmt.Errorf("could not delete task: %v", err)
|
return fmt.Errorf("could not delete task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,18 @@ func TestDelete(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
main []string
|
main []string
|
||||||
flags map[string]string
|
flags map[string]string
|
||||||
expErr bool
|
expParseErr bool
|
||||||
|
expDoErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "invalid",
|
name: "invalid",
|
||||||
main: []string{"update"},
|
main: []string{"update"},
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not found",
|
name: "not found",
|
||||||
main: []string{"delete", "5"},
|
main: []string{"delete", "5"},
|
||||||
expErr: true,
|
expDoErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
|
@ -48,26 +49,35 @@ func TestDelete(t *testing.T) {
|
||||||
if err := taskRepo.Store(e); err != nil {
|
if err := taskRepo.Store(e); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
localRepo := memory.NewLocalID()
|
localIDRepo := memory.NewLocalID()
|
||||||
if err := localRepo.Store(e.ID, 1); err != nil {
|
if err := localIDRepo.Store(e.ID, 1); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := command.NewDelete(localRepo, taskRepo, syncRepo)
|
cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags)
|
||||||
|
if tc.expParseErr != (actParseErr != nil) {
|
||||||
actErr := cmd.Execute(tc.main, tc.flags) != nil
|
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
|
||||||
if tc.expErr != actErr {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
|
||||||
}
|
}
|
||||||
if tc.expErr {
|
if tc.expParseErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
actDoErr := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localIDRepo,
|
||||||
|
SyncRepo: syncRepo,
|
||||||
|
}) != nil
|
||||||
|
if tc.expDoErr != actDoErr {
|
||||||
|
t.Errorf("exp false, got %v", actDoErr)
|
||||||
|
}
|
||||||
|
if tc.expDoErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, repoErr := taskRepo.Find(e.ID)
|
_, repoErr := taskRepo.Find(e.ID)
|
||||||
if !errors.Is(repoErr, storage.ErrNotFound) {
|
if !errors.Is(repoErr, storage.ErrNotFound) {
|
||||||
t.Errorf("exp %v, got %v", storage.ErrNotFound, actErr)
|
t.Errorf("exp %v, got %v", storage.ErrNotFound, repoErr)
|
||||||
}
|
}
|
||||||
idMap, idErr := localRepo.FindAll()
|
idMap, idErr := localIDRepo.FindAll()
|
||||||
if idErr != nil {
|
if idErr != nil {
|
||||||
t.Errorf("exp nil, got %v", idErr)
|
t.Errorf("exp nil, got %v", idErr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DateFormat = "2006-01-02"
|
|
||||||
TimeFormat = "15:04"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrWrongCommand = errors.New("wrong command")
|
|
||||||
ErrInvalidArg = errors.New("invalid argument")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Flag interface {
|
|
||||||
Set(val string) error
|
|
||||||
IsSet() bool
|
|
||||||
Get() any
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagString struct {
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FlagString) Set(val string) error {
|
|
||||||
fs.Value = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FlagString) IsSet() bool {
|
|
||||||
return fs.Value != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FlagString) Get() any {
|
|
||||||
return fs.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagDate struct {
|
|
||||||
Name string
|
|
||||||
Value item.Date
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagDate) Set(val string) error {
|
|
||||||
d := item.NewDateFromString(val)
|
|
||||||
if d.IsZero() {
|
|
||||||
return fmt.Errorf("could not parse date: %v", d)
|
|
||||||
}
|
|
||||||
fd.Value = d
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagDate) IsSet() bool {
|
|
||||||
return !fd.Value.IsZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagDate) Get() any {
|
|
||||||
return fd.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagTime struct {
|
|
||||||
Name string
|
|
||||||
Value item.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ft *FlagTime) Set(val string) error {
|
|
||||||
d := item.NewTimeFromString(val)
|
|
||||||
if d.IsZero() {
|
|
||||||
return fmt.Errorf("could not parse date: %v", d)
|
|
||||||
}
|
|
||||||
ft.Value = d
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagTime) IsSet() bool {
|
|
||||||
return !fd.Value.IsZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FlagTime) Get() any {
|
|
||||||
return fs.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagDuration struct {
|
|
||||||
Name string
|
|
||||||
Value time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagDuration) Set(val string) error {
|
|
||||||
dur, err := time.ParseDuration(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse duration: %v", err)
|
|
||||||
}
|
|
||||||
fd.Value = dur
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fd *FlagDuration) IsSet() bool {
|
|
||||||
return fd.Value.String() != "0s"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FlagDuration) Get() any {
|
|
||||||
return fs.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagRecurrer struct {
|
|
||||||
Name string
|
|
||||||
Value item.Recurrer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *FlagRecurrer) Set(val string) error {
|
|
||||||
fr.Value = item.NewRecurrer(val)
|
|
||||||
if fr.Value == nil {
|
|
||||||
return fmt.Errorf("not a valid recurrer: %v", val)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *FlagRecurrer) IsSet() bool {
|
|
||||||
return fr.Value != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *FlagRecurrer) Get() any {
|
|
||||||
return fr.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagInt struct {
|
|
||||||
Name string
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FlagInt) Set(val string) error {
|
|
||||||
i, err := strconv.Atoi(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("not a valid integer: %v", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
fi.Value = i
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FlagInt) IsSet() bool {
|
|
||||||
return fi.Value != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FlagInt) Get() any {
|
|
||||||
return fi.Value
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
package command_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFlagString(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
valid := "test"
|
|
||||||
f := command.FlagString{}
|
|
||||||
if f.IsSet() {
|
|
||||||
t.Errorf("exp false, got true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Set(valid); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsSet() {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
act, ok := f.Get().(string)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
if act != valid {
|
|
||||||
t.Errorf("exp %v, got %v", valid, act)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagDate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
valid := item.NewDate(2024, 11, 20)
|
|
||||||
f := command.FlagDate{}
|
|
||||||
if f.IsSet() {
|
|
||||||
t.Errorf("exp false, got true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Set(valid.String()); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsSet() {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
act, ok := f.Get().(item.Date)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
if act.String() != valid.String() {
|
|
||||||
t.Errorf("exp %v, got %v", valid.String(), act.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagTime(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
valid := item.NewTime(12, 30)
|
|
||||||
f := command.FlagTime{}
|
|
||||||
if f.IsSet() {
|
|
||||||
t.Errorf("exp false, got true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Set(valid.String()); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsSet() {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
act, ok := f.Get().(item.Time)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
if act.String() != valid.String() {
|
|
||||||
t.Errorf("exp %v, got %v", valid.String(), act.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagDurationTime(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
valid := time.Hour
|
|
||||||
validStr := "1h"
|
|
||||||
f := command.FlagDuration{}
|
|
||||||
if f.IsSet() {
|
|
||||||
t.Errorf("exp false, got true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Set(validStr); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsSet() {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
act, ok := f.Get().(time.Duration)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
if act != valid {
|
|
||||||
t.Errorf("exp %v, got %v", valid, act)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagRecurrer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
validStr := "2024-12-23, daily"
|
|
||||||
valid := item.NewRecurrer(validStr)
|
|
||||||
f := command.FlagRecurrer{}
|
|
||||||
if f.IsSet() {
|
|
||||||
t.Errorf("exp false, got true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Set(validStr); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsSet() {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
act, ok := f.Get().(item.Recurrer)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp true, got false")
|
|
||||||
}
|
|
||||||
if act != valid {
|
|
||||||
t.Errorf("exp %v, got %v", valid, act)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,36 +2,32 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type List struct {
|
type ListArgs struct {
|
||||||
localIDRepo storage.LocalID
|
|
||||||
taskRepo storage.Task
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewList(localIDRepo storage.LocalID, taskRepo storage.Task) Command {
|
func NewListArgs() ListArgs {
|
||||||
return &List{
|
return ListArgs{}
|
||||||
localIDRepo: localIDRepo,
|
|
||||||
taskRepo: taskRepo,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list *List) Execute(main []string, flags map[string]string) error {
|
func (la ListArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
||||||
if len(main) > 0 && main[0] != "list" {
|
if len(main) > 0 && main[0] != "list" {
|
||||||
return ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.do()
|
return &List{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list *List) do() error {
|
type List struct {
|
||||||
localIDs, err := list.localIDRepo.FindAll()
|
}
|
||||||
|
|
||||||
|
func (list *List) Do(deps Dependencies) error {
|
||||||
|
localIDs, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
all, err := list.taskRepo.FindAll()
|
all, err := deps.TaskRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,11 +47,19 @@ func TestList(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
cmd := command.NewList(localRepo, taskRepo)
|
cmd, actErr := command.NewListArgs().Parse(tc.main, nil)
|
||||||
actErr := cmd.Execute(tc.main, nil) != nil
|
if tc.expErr != (actErr != nil) {
|
||||||
if tc.expErr != actErr {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
||||||
}
|
}
|
||||||
|
if tc.expErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localRepo,
|
||||||
|
}); err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,52 +7,43 @@ import (
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Sync struct {
|
type SyncArgs struct{}
|
||||||
client client.Client
|
|
||||||
syncRepo storage.Sync
|
func NewSyncArgs() SyncArgs {
|
||||||
localIDRepo storage.LocalID
|
return SyncArgs{}
|
||||||
taskRepo storage.Task
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSync(client client.Client, syncRepo storage.Sync, localIDRepo storage.LocalID, taskRepo storage.Task) Command {
|
func (sa SyncArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
||||||
return &Sync{
|
|
||||||
client: client,
|
|
||||||
syncRepo: syncRepo,
|
|
||||||
localIDRepo: localIDRepo,
|
|
||||||
taskRepo: taskRepo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sync *Sync) Execute(main []string, flags map[string]string) error {
|
|
||||||
if len(main) == 0 || main[0] != "sync" {
|
if len(main) == 0 || main[0] != "sync" {
|
||||||
return ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return sync.do()
|
return &Sync{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sync *Sync) do() error {
|
type Sync struct{}
|
||||||
|
|
||||||
|
func (s *Sync) Do(deps Dependencies) error {
|
||||||
// local new and updated
|
// local new and updated
|
||||||
sendItems, err := sync.syncRepo.FindAll()
|
sendItems, err := deps.SyncRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get updated items: %v", err)
|
return fmt.Errorf("could not get updated items: %v", err)
|
||||||
}
|
}
|
||||||
if err := sync.client.Update(sendItems); err != nil {
|
if err := deps.SyncClient.Update(sendItems); err != nil {
|
||||||
return fmt.Errorf("could not send updated items: %v", err)
|
return fmt.Errorf("could not send updated items: %v", err)
|
||||||
}
|
}
|
||||||
if err := sync.syncRepo.DeleteAll(); err != nil {
|
if err := deps.SyncRepo.DeleteAll(); err != nil {
|
||||||
return 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 := sync.syncRepo.LastUpdate()
|
ts, err := deps.SyncRepo.LastUpdate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 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 := sync.client.Updated([]item.Kind{item.KindTask}, ts)
|
recItems, err := deps.SyncClient.Updated([]item.Kind{item.KindTask}, ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not receive updates: %v", err)
|
return fmt.Errorf("could not receive updates: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -60,10 +51,10 @@ func (sync *Sync) do() error {
|
||||||
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 := sync.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 fmt.Errorf("could not delete local id: %v", err)
|
return fmt.Errorf("could not delete local id: %v", err)
|
||||||
}
|
}
|
||||||
if err := sync.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 fmt.Errorf("could not delete task: %v", err)
|
return fmt.Errorf("could not delete task: %v", err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -71,7 +62,7 @@ func (sync *Sync) do() error {
|
||||||
updated = append(updated, ri)
|
updated = append(updated, ri)
|
||||||
}
|
}
|
||||||
|
|
||||||
lidMap, err := sync.localIDRepo.FindAll()
|
lidMap, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -87,17 +78,17 @@ func (sync *Sync) do() error {
|
||||||
RecurNext: u.RecurNext,
|
RecurNext: u.RecurNext,
|
||||||
TaskBody: tskBody,
|
TaskBody: tskBody,
|
||||||
}
|
}
|
||||||
if err := sync.taskRepo.Store(tsk); err != nil {
|
if err := deps.TaskRepo.Store(tsk); err != nil {
|
||||||
return 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 = sync.localIDRepo.Next()
|
lid, err = deps.LocalIDRepo.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get next local id: %v", err)
|
return fmt.Errorf("could not get next local id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sync.localIDRepo.Store(u.ID, lid); err != nil {
|
if err := deps.LocalIDRepo.Store(u.ID, lid); err != nil {
|
||||||
return fmt.Errorf("could not store local id: %v", err)
|
return fmt.Errorf("could not store local id: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,6 @@ import (
|
||||||
func TestSyncParse(t *testing.T) {
|
func TestSyncParse(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
syncClient := client.NewMemory()
|
|
||||||
syncRepo := memory.NewSync()
|
|
||||||
localIDRepo := memory.NewLocalID()
|
|
||||||
taskRepo := memory.NewTask()
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
main []string
|
main []string
|
||||||
|
@ -39,9 +34,8 @@ func TestSyncParse(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
|
_, actErr := command.SyncArgs{}.Parse(tc.main, nil)
|
||||||
actErr := cmd.Execute(tc.main, nil) != nil
|
if tc.expErr != (actErr != nil) {
|
||||||
if tc.expErr != actErr {
|
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -82,8 +76,16 @@ func TestSyncSend(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
|
cmd, err := command.SyncArgs{}.Parse([]string{"sync"}, nil)
|
||||||
if err := cmd.Execute([]string{"sync"}, nil); err != nil {
|
if err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localIDRepo,
|
||||||
|
SyncRepo: syncRepo,
|
||||||
|
SyncClient: syncClient,
|
||||||
|
}); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
actItems, actErr := syncClient.Updated(tc.ks, tc.ts)
|
actItems, actErr := syncClient.Updated(tc.ks, tc.ts)
|
||||||
|
@ -181,12 +183,12 @@ func TestSyncReceive(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// setup
|
|
||||||
syncClient := client.NewMemory()
|
syncClient := client.NewMemory()
|
||||||
syncRepo := memory.NewSync()
|
syncRepo := memory.NewSync()
|
||||||
localIDRepo := memory.NewLocalID()
|
localIDRepo := memory.NewLocalID()
|
||||||
taskRepo := memory.NewTask()
|
taskRepo := memory.NewTask()
|
||||||
|
|
||||||
|
// setup
|
||||||
for i, p := range tc.present {
|
for i, p := range tc.present {
|
||||||
if err := taskRepo.Store(p); err != nil {
|
if err := taskRepo.Store(p); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
@ -200,8 +202,16 @@ func TestSyncReceive(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync
|
// sync
|
||||||
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
|
cmd, err := command.NewSyncArgs().Parse([]string{"sync"}, nil)
|
||||||
if err := cmd.Execute([]string{"sync"}, nil); err != nil {
|
if err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
|
if err := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localIDRepo,
|
||||||
|
SyncRepo: syncRepo,
|
||||||
|
SyncClient: syncClient,
|
||||||
|
}); err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,71 +4,93 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Update struct {
|
type UpdateArgs struct {
|
||||||
localIDRepo storage.LocalID
|
fieldTPL map[string][]string
|
||||||
taskRepo storage.Task
|
LocalID int
|
||||||
syncRepo storage.Sync
|
Title string
|
||||||
argSet *ArgSet
|
Date item.Date
|
||||||
localID int
|
Time item.Time
|
||||||
|
Duration time.Duration
|
||||||
|
Recurrer item.Recurrer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUpdate(localIDRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
|
func NewUpdateArgs() UpdateArgs {
|
||||||
return &Update{
|
return UpdateArgs{
|
||||||
localIDRepo: localIDRepo,
|
fieldTPL: map[string][]string{
|
||||||
taskRepo: taskRepo,
|
"date": []string{"d", "date", "on"},
|
||||||
syncRepo: syncRepo,
|
"time": []string{"t", "time", "at"},
|
||||||
argSet: &ArgSet{
|
"duration": []string{"dur", "duration", "for"},
|
||||||
Flags: map[string]Flag{
|
"recurrer": []string{"rec", "recurrer"},
|
||||||
FlagTitle: &FlagString{},
|
|
||||||
FlagOn: &FlagDate{},
|
|
||||||
FlagAt: &FlagTime{},
|
|
||||||
FlagFor: &FlagDuration{},
|
|
||||||
FlagRec: &FlagRecurrer{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (update *Update) Execute(main []string, flags map[string]string) error {
|
func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
||||||
if len(main) < 2 || main[0] != "update" {
|
if len(main) < 2 || main[0] != "update" {
|
||||||
return ErrWrongCommand
|
return nil, ErrWrongCommand
|
||||||
}
|
}
|
||||||
localID, err := strconv.Atoi(main[1])
|
localID, err := strconv.Atoi(main[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("not a local id: %v", main[1])
|
return nil, fmt.Errorf("not a local id: %v", main[1])
|
||||||
}
|
}
|
||||||
update.localID = localID
|
fields, err = ResolveFields(fields, ua.fieldTPL)
|
||||||
main = main[2:]
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
as := update.argSet
|
|
||||||
as.Main = strings.Join(main, " ")
|
|
||||||
for k := range as.Flags {
|
|
||||||
v, ok := flags[k]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if err := as.Set(k, v); err != nil {
|
args := UpdateArgs{
|
||||||
return fmt.Errorf("could not set %s: %v", k, err)
|
LocalID: localID,
|
||||||
}
|
Title: strings.Join(main[2:], " "),
|
||||||
}
|
|
||||||
update.argSet = as
|
|
||||||
|
|
||||||
return update.do()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (update *Update) do() error {
|
if val, ok := fields["date"]; ok {
|
||||||
as := update.argSet
|
d := item.NewDateFromString(val)
|
||||||
|
if d.IsZero() {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
args.Date = d
|
||||||
|
}
|
||||||
|
if val, ok := fields["time"]; ok {
|
||||||
|
t := item.NewTimeFromString(val)
|
||||||
|
if t.IsZero() {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
args.Time = t
|
||||||
|
}
|
||||||
|
if val, ok := fields["duration"]; ok {
|
||||||
|
d, err := time.ParseDuration(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
args.Duration = d
|
||||||
|
}
|
||||||
|
if val, ok := fields["recurrer"]; ok {
|
||||||
|
rec := item.NewRecurrer(val)
|
||||||
|
if rec == nil {
|
||||||
|
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
args.Recurrer = rec
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Update{args}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Update struct {
|
||||||
|
args UpdateArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Update) Do(deps Dependencies) error {
|
||||||
var id string
|
var id string
|
||||||
idMap, err := update.localIDRepo.FindAll()
|
idMap, err := deps.LocalIDRepo.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get local ids: %v", err)
|
return fmt.Errorf("could not get local ids: %v", err)
|
||||||
}
|
}
|
||||||
for tid, lid := range idMap {
|
for tid, lid := range idMap {
|
||||||
if update.localID == lid {
|
if u.args.LocalID == lid {
|
||||||
id = tid
|
id = tid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,32 +98,33 @@ func (update *Update) do() error {
|
||||||
return fmt.Errorf("could not find local id")
|
return fmt.Errorf("could not find local id")
|
||||||
}
|
}
|
||||||
|
|
||||||
tsk, err := update.taskRepo.Find(id)
|
tsk, err := deps.TaskRepo.Find(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not find task")
|
return fmt.Errorf("could not find task")
|
||||||
}
|
}
|
||||||
|
|
||||||
if as.Main != "" {
|
if u.args.Title != "" {
|
||||||
tsk.Title = as.Main
|
tsk.Title = u.args.Title
|
||||||
}
|
}
|
||||||
if as.IsSet(FlagOn) {
|
if !u.args.Date.IsZero() {
|
||||||
tsk.Date = as.GetDate(FlagOn)
|
tsk.Date = u.args.Date
|
||||||
}
|
}
|
||||||
if as.IsSet(FlagAt) {
|
if !u.args.Time.IsZero() {
|
||||||
tsk.Time = as.GetTime(FlagAt)
|
tsk.Time = u.args.Time
|
||||||
}
|
}
|
||||||
if as.IsSet(FlagFor) {
|
if u.args.Duration != 0 {
|
||||||
tsk.Duration = as.GetDuration(FlagFor)
|
tsk.Duration = u.args.Duration
|
||||||
}
|
}
|
||||||
if as.IsSet(FlagRec) {
|
if u.args.Recurrer != nil {
|
||||||
tsk.Recurrer = as.GetRecurrer(FlagRec)
|
tsk.Recurrer = u.args.Recurrer
|
||||||
|
tsk.RecurNext = tsk.Recurrer.First()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tsk.Valid() {
|
if !tsk.Valid() {
|
||||||
return fmt.Errorf("task is unvalid")
|
return fmt.Errorf("task is unvalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := update.taskRepo.Store(tsk); err != nil {
|
if err := deps.TaskRepo.Store(tsk); err != nil {
|
||||||
return fmt.Errorf("could not store task: %v", err)
|
return fmt.Errorf("could not store task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +132,7 @@ func (update *Update) do() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 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 := update.syncRepo.Store(it); err != nil {
|
if err := deps.SyncRepo.Store(it); err != nil {
|
||||||
return fmt.Errorf("could not store sync item: %v", err)
|
return fmt.Errorf("could not store sync item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,18 +31,19 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
localID int
|
localID int
|
||||||
main []string
|
main []string
|
||||||
flags map[string]string
|
fields map[string]string
|
||||||
expTask item.Task
|
expTask item.Task
|
||||||
expErr bool
|
expParseErr bool
|
||||||
|
expDoErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no args",
|
name: "no args",
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not found",
|
name: "not found",
|
||||||
localID: 1,
|
main: []string{"update", "1"},
|
||||||
expErr: true,
|
expDoErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "name",
|
name: "name",
|
||||||
|
@ -59,19 +60,19 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid on",
|
name: "invalid date",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"on": "invalid",
|
"on": "invalid",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "on",
|
name: "date",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"on": "2024-10-02",
|
"on": "2024-10-02",
|
||||||
},
|
},
|
||||||
expTask: item.Task{
|
expTask: item.Task{
|
||||||
|
@ -85,20 +86,20 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid at",
|
name: "invalid time",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"at": "invalid",
|
"at": "invalid",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "at",
|
name: "time",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"at": "11:00",
|
"time": "11:00",
|
||||||
},
|
},
|
||||||
expTask: item.Task{
|
expTask: item.Task{
|
||||||
ID: tskID,
|
ID: tskID,
|
||||||
|
@ -111,37 +112,19 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "on and at",
|
name: "invalid duration",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"on": "2024-10-02",
|
|
||||||
"at": "11:00",
|
|
||||||
},
|
|
||||||
expTask: item.Task{
|
|
||||||
ID: tskID,
|
|
||||||
Date: item.NewDate(2024, 10, 2),
|
|
||||||
TaskBody: item.TaskBody{
|
|
||||||
Title: title,
|
|
||||||
Time: item.NewTime(11, 0),
|
|
||||||
Duration: oneHour,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid for",
|
|
||||||
localID: lid,
|
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
|
||||||
flags: map[string]string{
|
|
||||||
"for": "invalid",
|
"for": "invalid",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "for",
|
name: "duration",
|
||||||
localID: lid,
|
localID: lid,
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"for": "2h",
|
"for": "2h",
|
||||||
},
|
},
|
||||||
expTask: item.Task{
|
expTask: item.Task{
|
||||||
|
@ -155,17 +138,17 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid rec",
|
name: "invalid recurrer",
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"rec": "invalud",
|
"rec": "invalud",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expParseErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid rec",
|
name: "valid recurrer",
|
||||||
main: []string{"update", fmt.Sprintf("%d", lid)},
|
main: []string{"update", fmt.Sprintf("%d", lid)},
|
||||||
flags: map[string]string{
|
fields: map[string]string{
|
||||||
"rec": "2024-12-08, daily",
|
"rec": "2024-12-08, daily",
|
||||||
},
|
},
|
||||||
expTask: item.Task{
|
expTask: item.Task{
|
||||||
|
@ -199,12 +182,22 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
t.Errorf("exp nil, ,got %v", err)
|
t.Errorf("exp nil, ,got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := command.NewUpdate(localIDRepo, taskRepo, syncRepo)
|
cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields)
|
||||||
actParseErr := cmd.Execute(tc.main, tc.flags) != nil
|
if tc.expParseErr != (actErr != nil) {
|
||||||
if tc.expErr != actParseErr {
|
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
|
||||||
}
|
}
|
||||||
if tc.expErr {
|
if tc.expParseErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
actDoErr := cmd.Do(command.Dependencies{
|
||||||
|
TaskRepo: taskRepo,
|
||||||
|
LocalIDRepo: localIDRepo,
|
||||||
|
SyncRepo: syncRepo,
|
||||||
|
}) != nil
|
||||||
|
if tc.expDoErr != actDoErr {
|
||||||
|
t.Errorf("exp %v, got %v", tc.expDoErr, actDoErr)
|
||||||
|
}
|
||||||
|
if tc.expDoErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
plan/main.go
16
plan/main.go
|
@ -35,16 +35,12 @@ func main() {
|
||||||
|
|
||||||
syncClient := client.New(conf.SyncURL, conf.ApiKey)
|
syncClient := client.New(conf.SyncURL, conf.ApiKey)
|
||||||
|
|
||||||
cli := command.CLI{
|
cli := command.NewCLI(command.Dependencies{
|
||||||
Commands: []command.Command{
|
LocalIDRepo: localIDRepo,
|
||||||
command.NewAdd(localIDRepo, taskRepo, syncRepo),
|
TaskRepo: taskRepo,
|
||||||
command.NewList(localIDRepo, taskRepo),
|
SyncRepo: syncRepo,
|
||||||
command.NewUpdate(localIDRepo, taskRepo, syncRepo),
|
SyncClient: syncClient,
|
||||||
command.NewDelete(localIDRepo, taskRepo, syncRepo),
|
})
|
||||||
command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cli.Run(os.Args[1:]); err != nil {
|
if err := cli.Run(os.Args[1:]); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
Loading…
Reference in New Issue