From 248edf6379aa998224f53b71d75b55cbd0e9e5c2 Mon Sep 17 00:00:00 2001 From: Erik Winter Date: Wed, 28 Sep 2022 08:49:25 +0200 Subject: [PATCH] improve cli field args parser --- cmd/cli/command/command.go | 69 +++++++++++++++++++ .../command_test.go} | 43 ++++++++++-- cmd/cli/command/new.go | 2 +- cmd/cli/command/update.go | 2 +- cmd/cli/format/format.go | 44 ------------ 5 files changed, 110 insertions(+), 50 deletions(-) rename cmd/cli/{format/format_test.go => command/command_test.go} (56%) diff --git a/cmd/cli/command/command.go b/cmd/cli/command/command.go index 754e4fd..05b76a5 100644 --- a/cmd/cli/command/command.go +++ b/cmd/cli/command/command.go @@ -2,9 +2,12 @@ package command import ( "errors" + "fmt" "strconv" + "strings" "ewintr.nl/gte/internal/configuration" + "ewintr.nl/gte/internal/task" ) var ( @@ -12,6 +15,9 @@ var ( ErrInvalidArg = errors.New("invalid argument") ErrCouldNotFindTask = errors.New("could not find task") ErrUnknownFolder = errors.New("unknown folder") + ErrFieldAlreadyUsed = errors.New("field was already used") + ErrInvalidDate = errors.New("could not understand date format") + ErrInvalidProject = errors.New("could not understand project") ) type Command interface { @@ -89,3 +95,66 @@ func parseRemote(conf *configuration.Configuration, cmdArgs []string) (Command, return cmd, ErrInvalidArg } } + +func ParseTaskFieldArgs(args []string) (*task.LocalUpdate, error) { + lu := &task.LocalUpdate{} + + action, fields := []string{}, []string{} + for _, f := range args { + if project, ok := parseProjectField(f); ok { + if lu.Project != "" { + return &task.LocalUpdate{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_PROJECT) + + } + if project == "" { + return &task.LocalUpdate{}, ErrInvalidProject + } + lu.Project = project + fields = append(fields, task.FIELD_PROJECT) + continue + } + if due, ok := parseDueField(f); ok { + if due.IsZero() { + return &task.LocalUpdate{}, ErrInvalidDate + } + if !lu.Due.IsZero() { + return &task.LocalUpdate{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_DUE) + } + lu.Due = due + fields = append(fields, task.FIELD_DUE) + continue + } + if len(f) > 0 { + action = append(action, f) + } + } + + if len(action) > 0 { + lu.Action = strings.Join(action, " ") + fields = append(fields, task.FIELD_ACTION) + } + + lu.Fields = fields + + return lu, nil +} + +func parseProjectField(s string) (string, bool) { + if !strings.HasPrefix(s, "project:") && !strings.HasPrefix(s, "p:") { + return "", false + } + split := strings.SplitN(s, ":", 2) + + return split[1], true +} + +func parseDueField(s string) (task.Date, bool) { + if !strings.HasPrefix(s, "due:") && !strings.HasPrefix(s, "d:") { + return task.Date{}, false + } + split := strings.SplitN(s, ":", 2) + + due := task.NewDateFromString(split[1]) + + return due, true +} diff --git a/cmd/cli/format/format_test.go b/cmd/cli/command/command_test.go similarity index 56% rename from cmd/cli/format/format_test.go rename to cmd/cli/command/command_test.go index 7374585..9f5e0ea 100644 --- a/cmd/cli/format/format_test.go +++ b/cmd/cli/command/command_test.go @@ -1,4 +1,4 @@ -package format_test +package command_test import ( "errors" @@ -6,7 +6,7 @@ import ( "testing" "ewintr.nl/go-kit/test" - "ewintr.nl/gte/cmd/cli/format" + "ewintr.nl/gte/cmd/cli/command" "ewintr.nl/gte/internal/task" ) @@ -53,12 +53,47 @@ func TestParseTaskFieldArgs(t *testing.T) { name: "two projects", input: "project:project1 project:project2", expUpdate: &task.LocalUpdate{}, - expErr: format.ErrFieldAlreadyUsed, + expErr: command.ErrFieldAlreadyUsed, + }, + { + name: "abbreviated", + input: "p:project1 d:2022-09-28", + expUpdate: &task.LocalUpdate{ + Fields: []string{task.FIELD_PROJECT, task.FIELD_DUE}, + Project: "project1", + Due: task.NewDate(2022, 9, 28), + }, + }, + { + name: "empty project", + input: "action project:", + expUpdate: &task.LocalUpdate{}, + expErr: command.ErrInvalidProject, + }, + { + name: "empty date", + input: "action due:", + expUpdate: &task.LocalUpdate{}, + expErr: command.ErrInvalidDate, + }, + { + name: "url", + input: "https://ewintr.nl/something?arg=1", + expUpdate: &task.LocalUpdate{ + Fields: []string{task.FIELD_ACTION}, + Action: "https://ewintr.nl/something?arg=1", + }, + }, + { + name: "misformatted date", + input: "d:20-wrong", + expUpdate: &task.LocalUpdate{}, + expErr: command.ErrInvalidDate, }, } { t.Run(tc.name, func(t *testing.T) { args := strings.Split(tc.input, " ") - act, err := format.ParseTaskFieldArgs(args) + act, err := command.ParseTaskFieldArgs(args) test.Equals(t, tc.expUpdate, act) test.Assert(t, errors.Is(err, tc.expErr), "wrong err") }) diff --git a/cmd/cli/command/new.go b/cmd/cli/command/new.go index f1a49d5..444148a 100644 --- a/cmd/cli/command/new.go +++ b/cmd/cli/command/new.go @@ -18,7 +18,7 @@ func NewNew(conf *configuration.Configuration, cmdArgs []string) (*New, error) { return &New{}, err } - update, err := format.ParseTaskFieldArgs(cmdArgs) + update, err := ParseTaskFieldArgs(cmdArgs) if err != nil { return &New{}, err } diff --git a/cmd/cli/command/update.go b/cmd/cli/command/update.go index 8a054c8..f145786 100644 --- a/cmd/cli/command/update.go +++ b/cmd/cli/command/update.go @@ -17,7 +17,7 @@ func NewUpdate(localId int, conf *configuration.Configuration, cmdArgs []string) return &Update{}, err } - update, err := format.ParseTaskFieldArgs(cmdArgs) + update, err := ParseTaskFieldArgs(cmdArgs) if err != nil { return &Update{}, err } diff --git a/cmd/cli/format/format.go b/cmd/cli/format/format.go index e2d790d..7e6be52 100644 --- a/cmd/cli/format/format.go +++ b/cmd/cli/format/format.go @@ -1,17 +1,11 @@ package format import ( - "errors" "fmt" - "strings" "ewintr.nl/gte/internal/task" ) -var ( - ErrFieldAlreadyUsed = errors.New("field was already used") -) - type Column int const ( @@ -42,41 +36,3 @@ due: %s return fmt.Sprintf("%s\n", output) } - -func ParseTaskFieldArgs(args []string) (*task.LocalUpdate, error) { - lu := &task.LocalUpdate{} - - action, fields := []string{}, []string{} - for _, f := range args { - split := strings.SplitN(f, ":", 2) - if len(split) == 2 { - switch split[0] { - case "project": - if lu.Project != "" { - return &task.LocalUpdate{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_PROJECT) - } - lu.Project = split[1] - fields = append(fields, task.FIELD_PROJECT) - case "due": - if !lu.Due.IsZero() { - return &task.LocalUpdate{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_DUE) - } - lu.Due = task.NewDateFromString(split[1]) - fields = append(fields, task.FIELD_DUE) - } - } else { - if len(f) > 0 { - action = append(action, f) - } - } - } - - if len(action) > 0 { - lu.Action = strings.Join(action, " ") - fields = append(fields, task.FIELD_ACTION) - } - - lu.Fields = fields - - return lu, nil -}