wip
This commit is contained in:
parent
b7da3fba1e
commit
d1209b8af1
|
@ -2,6 +2,7 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -19,6 +20,8 @@ type AddCmd struct {
|
||||||
localIDRepo storage.LocalID
|
localIDRepo storage.LocalID
|
||||||
eventRepo storage.Event
|
eventRepo storage.Event
|
||||||
syncRepo storage.Sync
|
syncRepo storage.Sync
|
||||||
|
title string
|
||||||
|
flags map[string]Flag
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddCmd(localRepo storage.LocalID, eventRepo storage.Event, syncRepo storage.Sync) Command {
|
func NewAddCmd(localRepo storage.LocalID, eventRepo storage.Event, syncRepo storage.Sync) Command {
|
||||||
|
@ -26,35 +29,43 @@ func NewAddCmd(localRepo storage.LocalID, eventRepo storage.Event, syncRepo stor
|
||||||
localIDRepo: localRepo,
|
localIDRepo: localRepo,
|
||||||
eventRepo: eventRepo,
|
eventRepo: eventRepo,
|
||||||
syncRepo: syncRepo,
|
syncRepo: syncRepo,
|
||||||
|
flags: map[string]Flag{
|
||||||
|
FlagOn: &FlagDate{},
|
||||||
|
FlagAt: &FlagTime{},
|
||||||
|
FlagFor: &FlagDuration{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (add *AddCmd) Parse(args []string) (*ArgSet, error) {
|
func (add *AddCmd) Parse(as *ArgSet) error {
|
||||||
if len(args) == 0 || args[0] != "add" {
|
if len(as.Main) == 0 || as.Main[0] != "add " {
|
||||||
return nil, ErrWrongCommand
|
return ErrWrongCommand
|
||||||
|
}
|
||||||
|
add.title = strings.Join(as.Main[1:], " ")
|
||||||
|
for k := range add.flags {
|
||||||
|
if err := add.flags[k].Set(as.Flags[k]); err != nil {
|
||||||
|
return fmt.Errorf("could not set %s: %v", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if add.title == "" {
|
||||||
|
return fmt.Errorf("%w: title is required", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
if !add.flags[FlagOn].IsSet() {
|
||||||
|
return fmt.Errorf("%w: date is required", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
if !add.flags[FlagAt].IsSet() && add.flags[FlagFor].IsSet() {
|
||||||
|
return fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg)
|
||||||
|
}
|
||||||
|
if !add.flags[FlagAt].IsSet() && !add.flags[FlagFor].IsSet() {
|
||||||
|
if err := add.flags[FlagFor].Set("24h"); err != nil {
|
||||||
|
return fmt.Errorf("could not set duration to 24 hours")
|
||||||
}
|
}
|
||||||
as, err := ParseArgs(args[1:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if as.Main == "" {
|
return nil
|
||||||
return nil, fmt.Errorf("%w: title is required", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if !as.HasFlag(FlagOn) {
|
|
||||||
return nil, fmt.Errorf("%w: date is required", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if !as.HasFlag(FlagAt) && as.HasFlag(FlagFor) {
|
|
||||||
return nil, fmt.Errorf("%w: can not have duration without start time", ErrInvalidArg)
|
|
||||||
}
|
|
||||||
if !as.HasFlag(FlagAt) && !as.HasFlag(FlagFor) {
|
|
||||||
as.SetFlag(FlagFor, "24h")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return as, nil
|
func (add *AddCmd) Do() error {
|
||||||
}
|
|
||||||
|
|
||||||
func (add *AddCmd) Do(as *ArgSet) error {
|
|
||||||
startFormat := "2006-01-02"
|
startFormat := "2006-01-02"
|
||||||
startStr := as.Flag(FlagOn)
|
startStr := as.Flag(FlagOn)
|
||||||
if as.HasFlag(FlagAt) {
|
if as.HasFlag(FlagAt) {
|
||||||
|
|
|
@ -2,9 +2,12 @@ package command_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command"
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddParse(t *testing.T) {
|
func TestAddParse(t *testing.T) {
|
||||||
|
@ -54,7 +57,21 @@ func TestAddParse(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// name: "start and duration"
|
name: "start and duration",
|
||||||
|
args: []string{"add", "title", "-on", "2024-11-10", "-at", "12:00", "-for", "1h"},
|
||||||
|
expAS: &command.ArgSet{
|
||||||
|
Main: "title",
|
||||||
|
Flags: map[string]string{
|
||||||
|
command.FlagOn: "2024-11-10",
|
||||||
|
command.FlagAt: "12:00",
|
||||||
|
command.FlagFor: "1h",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "start without duration",
|
||||||
|
args: []string{"add", "title", "-on", "2024-11-10", "-for", "1h"},
|
||||||
|
expErr: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
@ -72,144 +89,125 @@ func TestAddParse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
// t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// oneHour, err := time.ParseDuration("1h")
|
oneHour, err := time.ParseDuration("1h")
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
// }
|
}
|
||||||
// oneDay, err := time.ParseDuration("24h")
|
oneDay, err := time.ParseDuration("24h")
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
// name string
|
name string
|
||||||
// args map[string]string
|
args *command.ArgSet
|
||||||
// expEvent item.Event
|
expEvent item.Event
|
||||||
// expErr bool
|
expErr bool
|
||||||
// }{
|
}{
|
||||||
// {
|
{
|
||||||
// name: "no name",
|
name: "time, but no duration",
|
||||||
// args: map[string]string{
|
args: &command.ArgSet{
|
||||||
// "on": "2024-10-01",
|
Main: "event",
|
||||||
// "at": "9:00",
|
Flags: map[string]string{
|
||||||
// "for": "1h",
|
command.FlagOn: "2024-10-01",
|
||||||
// },
|
command.FlagAt: "9:00",
|
||||||
// expErr: true,
|
},
|
||||||
// },
|
},
|
||||||
// {
|
expEvent: item.Event{
|
||||||
// name: "no date",
|
ID: "a",
|
||||||
// args: map[string]string{
|
EventBody: item.EventBody{
|
||||||
// "name": "event",
|
Title: "event",
|
||||||
// "at": "9:00",
|
Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||||
// "for": "1h",
|
},
|
||||||
// },
|
},
|
||||||
// expErr: true,
|
},
|
||||||
// },
|
{
|
||||||
// {
|
name: "no time, no duration",
|
||||||
// name: "duration, but no time",
|
args: &command.ArgSet{
|
||||||
// args: map[string]string{
|
Main: "event",
|
||||||
// "name": "event",
|
Flags: map[string]string{
|
||||||
// "on": "2024-10-01",
|
command.FlagOn: "2024-10-01",
|
||||||
// "for": "1h",
|
command.FlagFor: "24h",
|
||||||
// },
|
},
|
||||||
// expErr: true,
|
},
|
||||||
// },
|
expEvent: item.Event{
|
||||||
// {
|
ID: "a",
|
||||||
// name: "time, but no duration",
|
EventBody: item.EventBody{
|
||||||
// args: map[string]string{
|
Title: "event",
|
||||||
// "name": "event",
|
Start: time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||||
// "on": "2024-10-01",
|
Duration: oneDay,
|
||||||
// "at": "9:00",
|
},
|
||||||
// },
|
},
|
||||||
// expEvent: item.Event{
|
},
|
||||||
// ID: "a",
|
{
|
||||||
// EventBody: item.EventBody{
|
name: "full",
|
||||||
// Title: "event",
|
args: &command.ArgSet{
|
||||||
// Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
Main: "event",
|
||||||
// },
|
Flags: map[string]string{
|
||||||
// },
|
command.FlagOn: "2024-10-01",
|
||||||
// },
|
command.FlagAt: "9:00",
|
||||||
// {
|
command.FlagFor: "1h",
|
||||||
// name: "no time, no duration",
|
},
|
||||||
// args: map[string]string{
|
},
|
||||||
// "name": "event",
|
expEvent: item.Event{
|
||||||
// "on": "2024-10-01",
|
ID: "a",
|
||||||
// },
|
EventBody: item.EventBody{
|
||||||
// expEvent: item.Event{
|
Title: "event",
|
||||||
// ID: "a",
|
Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||||
// EventBody: item.EventBody{
|
Duration: oneHour,
|
||||||
// Title: "event",
|
},
|
||||||
// Start: time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC),
|
},
|
||||||
// Duration: oneDay,
|
},
|
||||||
// },
|
} {
|
||||||
// },
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// },
|
eventRepo := memory.NewEvent()
|
||||||
// {
|
localRepo := memory.NewLocalID()
|
||||||
// name: "full",
|
syncRepo := memory.NewSync()
|
||||||
// args: map[string]string{
|
cmd := command.NewAddCmd(localRepo, eventRepo, syncRepo)
|
||||||
// "name": "event",
|
actErr := cmd.Do(tc.args) != nil
|
||||||
// "on": "2024-10-01",
|
if tc.expErr != actErr {
|
||||||
// "at": "9:00",
|
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
||||||
// "for": "1h",
|
}
|
||||||
// },
|
if tc.expErr {
|
||||||
// expEvent: item.Event{
|
return
|
||||||
// ID: "a",
|
}
|
||||||
// EventBody: item.EventBody{
|
actEvents, err := eventRepo.FindAll()
|
||||||
// Title: "event",
|
if err != nil {
|
||||||
// Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
t.Errorf("exp nil, got %v", err)
|
||||||
// Duration: oneHour,
|
}
|
||||||
// },
|
if len(actEvents) != 1 {
|
||||||
// },
|
t.Errorf("exp 1, got %d", len(actEvents))
|
||||||
// },
|
}
|
||||||
// } {
|
|
||||||
// t.Run(tc.name, func(t *testing.T) {
|
|
||||||
// eventRepo := memory.NewEvent()
|
|
||||||
// localRepo := memory.NewLocalID()
|
|
||||||
// syncRepo := memory.NewSync()
|
|
||||||
// actErr := command.Add(localRepo, eventRepo, syncRepo, tc.args["name"], tc.args["on"], tc.args["at"], tc.args["for"]) != nil
|
|
||||||
// if tc.expErr != actErr {
|
|
||||||
// t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
|
||||||
// }
|
|
||||||
// if tc.expErr {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// actEvents, err := eventRepo.FindAll()
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("exp nil, got %v", err)
|
|
||||||
// }
|
|
||||||
// if len(actEvents) != 1 {
|
|
||||||
// t.Errorf("exp 1, got %d", len(actEvents))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// actLocalIDs, err := localRepo.FindAll()
|
actLocalIDs, err := localRepo.FindAll()
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
// }
|
}
|
||||||
// if len(actLocalIDs) != 1 {
|
if len(actLocalIDs) != 1 {
|
||||||
// t.Errorf("exp 1, got %v", len(actLocalIDs))
|
t.Errorf("exp 1, got %v", len(actLocalIDs))
|
||||||
// }
|
}
|
||||||
// if _, ok := actLocalIDs[actEvents[0].ID]; !ok {
|
if _, ok := actLocalIDs[actEvents[0].ID]; !ok {
|
||||||
// t.Errorf("exp true, got %v", ok)
|
t.Errorf("exp true, got %v", ok)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if actEvents[0].ID == "" {
|
if actEvents[0].ID == "" {
|
||||||
// t.Errorf("exp string not te be empty")
|
t.Errorf("exp string not te be empty")
|
||||||
// }
|
}
|
||||||
// tc.expEvent.ID = actEvents[0].ID
|
tc.expEvent.ID = actEvents[0].ID
|
||||||
// if diff := cmp.Diff(tc.expEvent, actEvents[0]); diff != "" {
|
if diff := cmp.Diff(tc.expEvent, actEvents[0]); diff != "" {
|
||||||
// t.Errorf("(exp +, got -)\n%s", diff)
|
t.Errorf("(exp +, got -)\n%s", diff)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// updated, err := syncRepo.FindAll()
|
updated, err := syncRepo.FindAll()
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
// }
|
}
|
||||||
// if len(updated) != 1 {
|
if len(updated) != 1 {
|
||||||
// t.Errorf("exp 1, got %v", len(updated))
|
t.Errorf("exp 1, got %v", len(updated))
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
|
@ -6,32 +6,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrWrongCommand = errors.New("wrong command")
|
|
||||||
ErrInvalidArg = errors.New("invalid argument")
|
|
||||||
)
|
|
||||||
|
|
||||||
type ArgSet struct {
|
type ArgSet struct {
|
||||||
Main string
|
Main []string
|
||||||
Flags map[string]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 {
|
||||||
Parse(args []string) (*ArgSet, error)
|
Parse(args *ArgSet) error
|
||||||
Do(args *ArgSet) error
|
Do() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
|
@ -39,22 +21,26 @@ type CLI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) Run(args []string) error {
|
func (cli *CLI) Run(args []string) error {
|
||||||
|
as, err := ParseFlags(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, c := range cli.Commands {
|
for _, c := range cli.Commands {
|
||||||
as, err := c.Parse(args)
|
err := c.Parse(as)
|
||||||
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 c.Do(as)
|
return c.Do()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("could not find matching command")
|
return fmt.Errorf("could not find matching command")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseArgs(args []string) (*ArgSet, error) {
|
func ParseFlags(args []string) (*ArgSet, error) {
|
||||||
flags := make(map[string]string)
|
flags := make(map[string]string)
|
||||||
main := make([]string, 0)
|
main := make([]string, 0)
|
||||||
var inMain bool
|
var inMain bool
|
||||||
|
@ -62,7 +48,7 @@ func ParseArgs(args []string) (*ArgSet, error) {
|
||||||
if strings.HasPrefix(args[i], "-") {
|
if strings.HasPrefix(args[i], "-") {
|
||||||
inMain = false
|
inMain = false
|
||||||
if i+1 >= len(args) {
|
if i+1 >= len(args) {
|
||||||
return &ArgSet{}, fmt.Errorf("flag wihout value")
|
return nil, fmt.Errorf("flag wihout value")
|
||||||
}
|
}
|
||||||
flags[strings.TrimPrefix(args[i], "-")] = args[i+1]
|
flags[strings.TrimPrefix(args[i], "-")] = args[i+1]
|
||||||
i++
|
i++
|
||||||
|
@ -70,14 +56,14 @@ func ParseArgs(args []string) (*ArgSet, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !inMain && len(main) > 0 {
|
if !inMain && len(main) > 0 {
|
||||||
return &ArgSet{}, fmt.Errorf("two mains")
|
return nil, fmt.Errorf("two mains")
|
||||||
}
|
}
|
||||||
inMain = true
|
inMain = true
|
||||||
main = append(main, args[i])
|
main = append(main, args[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ArgSet{
|
return &ArgSet{
|
||||||
Main: strings.Join(main, " "),
|
Main: main,
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlagDate struct {
|
||||||
|
Name string
|
||||||
|
Value time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FlagDate) Set(val string) error {
|
||||||
|
d, err := time.Parse(DateFormat, val)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse date: %v", d)
|
||||||
|
}
|
||||||
|
ft.Value = d
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FlagDate) IsSet() bool {
|
||||||
|
return ft.Value.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlagTime struct {
|
||||||
|
Name string
|
||||||
|
Value time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FlagTime) Set(val string) error {
|
||||||
|
d, err := time.Parse(TimeFormat, val)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse date: %v", d)
|
||||||
|
}
|
||||||
|
ft.Value = d
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *FlagTime) IsSet() bool {
|
||||||
|
return fd.Value.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
Loading…
Reference in New Issue