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"
)
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)
}

View File

@ -7,11 +7,31 @@ import (
)
var (
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
}

View File

@ -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
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) {