This commit is contained in:
Erik Winter 2024-11-06 07:29:29 +01:00
parent 84d2b9eb18
commit 65d8ec66ef
3 changed files with 81 additions and 62 deletions

View File

@ -9,6 +9,12 @@ import (
"go-mod.ewintr.nl/planner/plan/storage" "go-mod.ewintr.nl/planner/plan/storage"
) )
const (
FlagOn = "on"
FlagAt = "at"
FlagFor = "for"
)
type AddCmd struct { type AddCmd struct {
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event 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" { 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 { if err != nil {
return false, err return nil, err
} }
if nameStr == "" { if as.Main == "" {
return fmt.Errorf("%w: name is required", ErrInvalidArg) return nil, fmt.Errorf("%w: title is required", ErrInvalidArg)
} }
if onStr == "" { if !as.HasFlag(FlagOn) {
return fmt.Errorf("%w: date is required", ErrInvalidArg) return nil, fmt.Errorf("%w: date is required", ErrInvalidArg)
} }
if atStr == "" && frStr != "" { if !as.HasFlag(FlagAt) && !as.HasFlag(FlagFor) {
return fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg) return nil, fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg)
} }
if atStr == "" && frStr == "" { if !as.HasFlag(FlagAt) && !as.HasFlag(FlagFor) {
frStr = "24h" as.SetFlag(FlagFor, "24h")
} }
if err := add.Action(title, flags); err != nil { if err := add.Action(as); err != nil {
return false, err 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" startFormat := "2006-01-02"
startStr := onStr startStr := flags["on"]
if atStr != "" { if at, okAt := flags["at"]; !okAt {
startFormat = fmt.Sprintf("%s 15:04", startFormat) 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) start, err := time.Parse(startFormat, startStr)
if err != nil { if err != nil {
@ -67,27 +72,27 @@ func (add *AddCmd) Action(nameStr, onStr, atStr, frStr string) error {
e := item.Event{ e := item.Event{
ID: uuid.New().String(), ID: uuid.New().String(),
EventBody: item.EventBody{ EventBody: item.EventBody{
Title: nameStr, Title: title,
Start: start, Start: start,
}, },
} }
if frStr != "" { if forStr, okFor := flags["for"]; okFor {
fr, err := time.ParseDuration(frStr) fr, err := time.ParseDuration(forStr)
if err != nil { if err != nil {
return fmt.Errorf("%w: could not parse duration: %s", ErrInvalidArg, err) return fmt.Errorf("%w: could not parse duration: %s", ErrInvalidArg, err)
} }
e.Duration = fr 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) return fmt.Errorf("could not store event: %v", err)
} }
localID, err := localIDRepo.Next() localID, err := add.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 := 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) 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 { 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 := syncRepo.Store(it); err != nil { if err := add.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)
} }

View File

@ -7,11 +7,31 @@ import (
) )
var ( var (
ErrWrongCommand = errors.New("wrong command")
ErrInvalidArg = errors.New("invalid argument") 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 { type Command interface {
Do(args []string) (bool, error) Parse(args []string) (*ArgSet, error)
Do(args *ArgSet) error
} }
type CLI struct { type CLI struct {
@ -20,39 +40,44 @@ type CLI struct {
func (cli *CLI) Run(args []string) error { func (cli *CLI) Run(args []string) error {
for _, c := range cli.Commands { for _, c := range cli.Commands {
worked, err := c.Do(args) as, err := c.Parse(args)
if err != nil { switch {
case errors.Is(err, ErrWrongCommand):
continue
case err != nil:
return err return err
} default:
if worked { return c.Do(as)
return nil
} }
} }
return fmt.Errorf("could not find matching command") 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) flags := make(map[string]string)
rem := make([]string, 0) main := make([]string, 0)
var inRem bool var inMain bool
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
if strings.HasPrefix(args[i], "-") { if strings.HasPrefix(args[i], "-") {
inRem = false inMain = false
if i+1 >= len(args) { 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] flags[strings.TrimPrefix(args[i], "-")] = args[i+1]
i++ i++
continue continue
} }
if !inRem && len(rem) > 0 { if !inMain && len(main) > 0 {
return "", nil, fmt.Errorf("two rems") return &ArgSet{}, fmt.Errorf("two mains")
} }
inRem = true inMain = true
rem = append(rem, args[i]) main = append(main, args[i])
} }
return strings.Join(rem, " "), flags, nil return &ArgSet{
Main: strings.Join(main, " "),
Flags: flags,
}, nil
} }

View File

@ -2,31 +2,20 @@ package command_test
import ( import (
"testing" "testing"
"go-mod.ewintr.nl/planner/plan/command"
) )
func TestCommand(t *testing.T) { func TestParseArgs(t *testing.T) {
t.Parallel() t.Parallel()
cmds := []*command.Command{
{
Name: "default",
Default: true,
},
}
cli := command.CLI{
CMDS: cmds,
}
for _, tc := range []struct { for _, tc := range []struct {
name string name string
args string args []string
exp *command.Command expRem string
expFlags map[string]string
expErr bool
}{ }{
{ {
name: "default", name: "empty",
exp: cmds[0],
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {