diff --git a/plan/command/add.go b/plan/command/add.go index d97b4cd..dd22ce9 100644 --- a/plan/command/add.go +++ b/plan/command/add.go @@ -9,6 +9,12 @@ import ( "go-mod.ewintr.nl/planner/plan/storage" ) +const ( + FlagOn = "on" + FlagAt = "at" + FlagFor = "for" +) + type AddCmd struct { localIDRepo storage.LocalID eventRepo storage.Event @@ -23,41 +29,40 @@ func NewAddCmd(localRepo storage.LocalID, eventRepo storage.Event, syncRepo stor } } -func (add *AddCmd) Do(args []string) (bool, error) { +func (add *AddCmd) Parse(args []string) (*ArgSet, error) { if len(args) == 0 || args[0] != "add" { - return false, nil + return nil, ErrWrongCommand } - title, flags, err := ParseArgs(args[1:]) + as, err := ParseArgs(args[1:]) if err != nil { - return false, err + return nil, err } - if nameStr == "" { - return fmt.Errorf("%w: name is required", ErrInvalidArg) + if as.Main == "" { + return nil, fmt.Errorf("%w: title is required", ErrInvalidArg) } - if onStr == "" { - return fmt.Errorf("%w: date is required", ErrInvalidArg) + if !as.HasFlag(FlagOn) { + return nil, fmt.Errorf("%w: date is required", ErrInvalidArg) } - if atStr == "" && frStr != "" { - return fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg) + if !as.HasFlag(FlagAt) && !as.HasFlag(FlagFor) { + return nil, fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg) } - if atStr == "" && frStr == "" { - frStr = "24h" + if !as.HasFlag(FlagAt) && !as.HasFlag(FlagFor) { + as.SetFlag(FlagFor, "24h") } - if err := add.Action(title, flags); err != nil { - return false, err + if err := add.Action(as); err != nil { + return nil, err } - return true, nil + return as, nil } -func (add *AddCmd) Action(nameStr, onStr, atStr, frStr string) error { - +func (add *AddCmd) Action(as *ArgSet) error { startFormat := "2006-01-02" - startStr := onStr - if atStr != "" { + startStr := flags["on"] + if at, okAt := flags["at"]; !okAt { startFormat = fmt.Sprintf("%s 15:04", startFormat) - startStr = fmt.Sprintf("%s %s", startStr, atStr) + startStr = fmt.Sprintf("%s %s", startStr, at) } start, err := time.Parse(startFormat, startStr) if err != nil { @@ -67,27 +72,27 @@ func (add *AddCmd) Action(nameStr, onStr, atStr, frStr string) error { e := item.Event{ ID: uuid.New().String(), EventBody: item.EventBody{ - Title: nameStr, + Title: title, Start: start, }, } - if frStr != "" { - fr, err := time.ParseDuration(frStr) + if forStr, okFor := flags["for"]; okFor { + fr, err := time.ParseDuration(forStr) if err != nil { return fmt.Errorf("%w: could not parse duration: %s", ErrInvalidArg, err) } e.Duration = fr } - if err := eventRepo.Store(e); err != nil { + if err := add.eventRepo.Store(e); err != nil { return fmt.Errorf("could not store event: %v", err) } - localID, err := localIDRepo.Next() + localID, err := add.localIDRepo.Next() if err != nil { return fmt.Errorf("could not create next local id: %v", err) } - if err := localIDRepo.Store(e.ID, localID); err != nil { + if err := add.localIDRepo.Store(e.ID, localID); err != nil { return fmt.Errorf("could not store local id: %v", err) } @@ -95,7 +100,7 @@ func (add *AddCmd) Action(nameStr, onStr, atStr, frStr string) error { if err != nil { return fmt.Errorf("could not convert event to sync item: %v", err) } - if err := syncRepo.Store(it); err != nil { + if err := add.syncRepo.Store(it); err != nil { return fmt.Errorf("could not store sync item: %v", err) } diff --git a/plan/command/command.go b/plan/command/command.go index 0b7b3d3..30c3389 100644 --- a/plan/command/command.go +++ b/plan/command/command.go @@ -7,11 +7,31 @@ import ( ) var ( - ErrInvalidArg = errors.New("invalid argument") + ErrWrongCommand = errors.New("wrong command") + ErrInvalidArg = errors.New("invalid argument") ) +type ArgSet struct { + Main string + Flags map[string]string +} + +func (as *ArgSet) HasFlag(name string) bool { + _, ok := as.Flags[name] + return ok +} + +func (as *ArgSet) Flag(name string) string { + return as.Flags[name] +} + +func (as *ArgSet) SetFlag(name, value string) { + as.Flags[name] = value +} + type Command interface { - Do(args []string) (bool, error) + Parse(args []string) (*ArgSet, error) + Do(args *ArgSet) error } type CLI struct { @@ -20,39 +40,44 @@ type CLI struct { func (cli *CLI) Run(args []string) error { for _, c := range cli.Commands { - worked, err := c.Do(args) - if err != nil { + as, err := c.Parse(args) + switch { + case errors.Is(err, ErrWrongCommand): + continue + case err != nil: return err - } - if worked { - return nil + default: + return c.Do(as) } } return fmt.Errorf("could not find matching command") } -func ParseArgs(args []string) (string, map[string]string, error) { +func ParseArgs(args []string) (*ArgSet, error) { flags := make(map[string]string) - rem := make([]string, 0) - var inRem bool + main := make([]string, 0) + var inMain bool for i := 0; i < len(args); i++ { if strings.HasPrefix(args[i], "-") { - inRem = false + inMain = false if i+1 >= len(args) { - return "", nil, fmt.Errorf("flag wihout value") + return &ArgSet{}, fmt.Errorf("flag wihout value") } flags[strings.TrimPrefix(args[i], "-")] = args[i+1] i++ continue } - if !inRem && len(rem) > 0 { - return "", nil, fmt.Errorf("two rems") + if !inMain && len(main) > 0 { + return &ArgSet{}, fmt.Errorf("two mains") } - inRem = true - rem = append(rem, args[i]) + inMain = true + main = append(main, args[i]) } - return strings.Join(rem, " "), flags, nil + return &ArgSet{ + Main: strings.Join(main, " "), + Flags: flags, + }, nil } diff --git a/plan/command/command_test.go b/plan/command/command_test.go index 958b197..d6567fb 100644 --- a/plan/command/command_test.go +++ b/plan/command/command_test.go @@ -2,31 +2,20 @@ package command_test import ( "testing" - - "go-mod.ewintr.nl/planner/plan/command" ) -func TestCommand(t *testing.T) { +func TestParseArgs(t *testing.T) { t.Parallel() - cmds := []*command.Command{ - { - Name: "default", - Default: true, - }, - } - cli := command.CLI{ - CMDS: cmds, - } - for _, tc := range []struct { - name string - args string - exp *command.Command + name string + args []string + expRem string + expFlags map[string]string + expErr bool }{ { - name: "default", - exp: cmds[0], + name: "empty", }, } { t.Run(tc.name, func(t *testing.T) {