wip
This commit is contained in:
parent
b7da3fba1e
commit
d1209b8af1
|
@ -2,6 +2,7 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
@ -19,6 +20,8 @@ type AddCmd struct {
|
|||
localIDRepo storage.LocalID
|
||||
eventRepo storage.Event
|
||||
syncRepo storage.Sync
|
||||
title string
|
||||
flags map[string]Flag
|
||||
}
|
||||
|
||||
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,
|
||||
eventRepo: eventRepo,
|
||||
syncRepo: syncRepo,
|
||||
flags: map[string]Flag{
|
||||
FlagOn: &FlagDate{},
|
||||
FlagAt: &FlagTime{},
|
||||
FlagFor: &FlagDuration{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (add *AddCmd) Parse(args []string) (*ArgSet, error) {
|
||||
if len(args) == 0 || args[0] != "add" {
|
||||
return nil, ErrWrongCommand
|
||||
func (add *AddCmd) Parse(as *ArgSet) error {
|
||||
if len(as.Main) == 0 || as.Main[0] != "add " {
|
||||
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, 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 nil
|
||||
}
|
||||
|
||||
return as, nil
|
||||
}
|
||||
|
||||
func (add *AddCmd) Do(as *ArgSet) error {
|
||||
func (add *AddCmd) Do() error {
|
||||
startFormat := "2006-01-02"
|
||||
startStr := as.Flag(FlagOn)
|
||||
if as.HasFlag(FlagAt) {
|
||||
|
|
|
@ -2,9 +2,12 @@ package command_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/storage/memory"
|
||||
)
|
||||
|
||||
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) {
|
||||
|
@ -72,144 +89,125 @@ func TestAddParse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// func TestAdd(t *testing.T) {
|
||||
// t.Parallel()
|
||||
func TestAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// oneHour, err := time.ParseDuration("1h")
|
||||
// if err != nil {
|
||||
// t.Errorf("exp nil, got %v", err)
|
||||
// }
|
||||
// oneDay, err := time.ParseDuration("24h")
|
||||
// if err != nil {
|
||||
// t.Errorf("exp nil, got %v", err)
|
||||
// }
|
||||
oneHour, err := time.ParseDuration("1h")
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
oneDay, err := time.ParseDuration("24h")
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
|
||||
// for _, tc := range []struct {
|
||||
// name string
|
||||
// args map[string]string
|
||||
// expEvent item.Event
|
||||
// expErr bool
|
||||
// }{
|
||||
// {
|
||||
// name: "no name",
|
||||
// args: map[string]string{
|
||||
// "on": "2024-10-01",
|
||||
// "at": "9:00",
|
||||
// "for": "1h",
|
||||
// },
|
||||
// expErr: true,
|
||||
// },
|
||||
// {
|
||||
// name: "no date",
|
||||
// args: map[string]string{
|
||||
// "name": "event",
|
||||
// "at": "9:00",
|
||||
// "for": "1h",
|
||||
// },
|
||||
// expErr: true,
|
||||
// },
|
||||
// {
|
||||
// name: "duration, but no time",
|
||||
// args: map[string]string{
|
||||
// "name": "event",
|
||||
// "on": "2024-10-01",
|
||||
// "for": "1h",
|
||||
// },
|
||||
// expErr: true,
|
||||
// },
|
||||
// {
|
||||
// name: "time, but no duration",
|
||||
// args: map[string]string{
|
||||
// "name": "event",
|
||||
// "on": "2024-10-01",
|
||||
// "at": "9:00",
|
||||
// },
|
||||
// expEvent: item.Event{
|
||||
// ID: "a",
|
||||
// EventBody: item.EventBody{
|
||||
// Title: "event",
|
||||
// Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "no time, no duration",
|
||||
// args: map[string]string{
|
||||
// "name": "event",
|
||||
// "on": "2024-10-01",
|
||||
// },
|
||||
// expEvent: item.Event{
|
||||
// ID: "a",
|
||||
// EventBody: item.EventBody{
|
||||
// Title: "event",
|
||||
// Start: time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||
// Duration: oneDay,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "full",
|
||||
// args: map[string]string{
|
||||
// "name": "event",
|
||||
// "on": "2024-10-01",
|
||||
// "at": "9:00",
|
||||
// "for": "1h",
|
||||
// },
|
||||
// expEvent: item.Event{
|
||||
// ID: "a",
|
||||
// EventBody: item.EventBody{
|
||||
// Title: "event",
|
||||
// Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||
// Duration: oneHour,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// } {
|
||||
// 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))
|
||||
// }
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args *command.ArgSet
|
||||
expEvent item.Event
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
name: "time, but no duration",
|
||||
args: &command.ArgSet{
|
||||
Main: "event",
|
||||
Flags: map[string]string{
|
||||
command.FlagOn: "2024-10-01",
|
||||
command.FlagAt: "9:00",
|
||||
},
|
||||
},
|
||||
expEvent: item.Event{
|
||||
ID: "a",
|
||||
EventBody: item.EventBody{
|
||||
Title: "event",
|
||||
Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no time, no duration",
|
||||
args: &command.ArgSet{
|
||||
Main: "event",
|
||||
Flags: map[string]string{
|
||||
command.FlagOn: "2024-10-01",
|
||||
command.FlagFor: "24h",
|
||||
},
|
||||
},
|
||||
expEvent: item.Event{
|
||||
ID: "a",
|
||||
EventBody: item.EventBody{
|
||||
Title: "event",
|
||||
Start: time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||
Duration: oneDay,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "full",
|
||||
args: &command.ArgSet{
|
||||
Main: "event",
|
||||
Flags: map[string]string{
|
||||
command.FlagOn: "2024-10-01",
|
||||
command.FlagAt: "9:00",
|
||||
command.FlagFor: "1h",
|
||||
},
|
||||
},
|
||||
expEvent: item.Event{
|
||||
ID: "a",
|
||||
EventBody: item.EventBody{
|
||||
Title: "event",
|
||||
Start: time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC),
|
||||
Duration: oneHour,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
eventRepo := memory.NewEvent()
|
||||
localRepo := memory.NewLocalID()
|
||||
syncRepo := memory.NewSync()
|
||||
cmd := command.NewAddCmd(localRepo, eventRepo, syncRepo)
|
||||
actErr := cmd.Do(tc.args) != 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()
|
||||
// if err != nil {
|
||||
// t.Errorf("exp nil, got %v", err)
|
||||
// }
|
||||
// if len(actLocalIDs) != 1 {
|
||||
// t.Errorf("exp 1, got %v", len(actLocalIDs))
|
||||
// }
|
||||
// if _, ok := actLocalIDs[actEvents[0].ID]; !ok {
|
||||
// t.Errorf("exp true, got %v", ok)
|
||||
// }
|
||||
actLocalIDs, err := localRepo.FindAll()
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
if len(actLocalIDs) != 1 {
|
||||
t.Errorf("exp 1, got %v", len(actLocalIDs))
|
||||
}
|
||||
if _, ok := actLocalIDs[actEvents[0].ID]; !ok {
|
||||
t.Errorf("exp true, got %v", ok)
|
||||
}
|
||||
|
||||
// if actEvents[0].ID == "" {
|
||||
// t.Errorf("exp string not te be empty")
|
||||
// }
|
||||
// tc.expEvent.ID = actEvents[0].ID
|
||||
// if diff := cmp.Diff(tc.expEvent, actEvents[0]); diff != "" {
|
||||
// t.Errorf("(exp +, got -)\n%s", diff)
|
||||
// }
|
||||
if actEvents[0].ID == "" {
|
||||
t.Errorf("exp string not te be empty")
|
||||
}
|
||||
tc.expEvent.ID = actEvents[0].ID
|
||||
if diff := cmp.Diff(tc.expEvent, actEvents[0]); diff != "" {
|
||||
t.Errorf("(exp +, got -)\n%s", diff)
|
||||
}
|
||||
|
||||
// updated, err := syncRepo.FindAll()
|
||||
// if err != nil {
|
||||
// t.Errorf("exp nil, got %v", err)
|
||||
// }
|
||||
// if len(updated) != 1 {
|
||||
// t.Errorf("exp 1, got %v", len(updated))
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
updated, err := syncRepo.FindAll()
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
if len(updated) != 1 {
|
||||
t.Errorf("exp 1, got %v", len(updated))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,32 +6,14 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrWrongCommand = errors.New("wrong command")
|
||||
ErrInvalidArg = errors.New("invalid argument")
|
||||
)
|
||||
|
||||
type ArgSet struct {
|
||||
Main string
|
||||
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 {
|
||||
Parse(args []string) (*ArgSet, error)
|
||||
Do(args *ArgSet) error
|
||||
Parse(args *ArgSet) error
|
||||
Do() error
|
||||
}
|
||||
|
||||
type CLI struct {
|
||||
|
@ -39,22 +21,26 @@ type CLI struct {
|
|||
}
|
||||
|
||||
func (cli *CLI) Run(args []string) error {
|
||||
as, err := ParseFlags(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range cli.Commands {
|
||||
as, err := c.Parse(args)
|
||||
err := c.Parse(as)
|
||||
switch {
|
||||
case errors.Is(err, ErrWrongCommand):
|
||||
continue
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
return c.Do(as)
|
||||
return c.Do()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
main := make([]string, 0)
|
||||
var inMain bool
|
||||
|
@ -62,7 +48,7 @@ func ParseArgs(args []string) (*ArgSet, error) {
|
|||
if strings.HasPrefix(args[i], "-") {
|
||||
inMain = false
|
||||
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]
|
||||
i++
|
||||
|
@ -70,14 +56,14 @@ func ParseArgs(args []string) (*ArgSet, error) {
|
|||
}
|
||||
|
||||
if !inMain && len(main) > 0 {
|
||||
return &ArgSet{}, fmt.Errorf("two mains")
|
||||
return nil, fmt.Errorf("two mains")
|
||||
}
|
||||
inMain = true
|
||||
main = append(main, args[i])
|
||||
}
|
||||
|
||||
return &ArgSet{
|
||||
Main: strings.Join(main, " "),
|
||||
Main: main,
|
||||
Flags: flags,
|
||||
}, 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