planner/plan/command/command.go

126 lines
2.6 KiB
Go
Raw Permalink Normal View History

2024-10-29 07:22:04 +01:00
package command
import (
"errors"
"fmt"
2024-12-27 11:20:32 +01:00
"slices"
2024-10-29 07:22:04 +01:00
"strings"
2024-12-27 11:20:32 +01:00
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/sync/client"
2024-10-29 07:22:04 +01:00
)
const (
2024-12-27 11:20:32 +01:00
DateFormat = "2006-01-02"
TimeFormat = "15:04"
)
var (
ErrWrongCommand = errors.New("wrong command")
ErrInvalidArg = errors.New("invalid argument")
2024-10-29 07:22:04 +01:00
)
2025-01-13 09:13:48 +01:00
type Repositories interface {
Begin() (*storage.Tx, error)
LocalID(tx *storage.Tx) storage.LocalID
Sync(tx *storage.Tx) storage.Sync
Task(tx *storage.Tx) storage.Task
2024-12-27 11:20:32 +01:00
}
type CommandArgs interface {
Parse(main []string, fields map[string]string) (Command, error)
}
2024-10-29 07:22:04 +01:00
type Command interface {
2025-01-13 09:13:48 +01:00
Do(repos Repositories, client client.Client) (CommandResult, error)
2024-12-29 09:32:49 +01:00
}
type CommandResult interface {
Render() string
2024-10-29 07:22:04 +01:00
}
type CLI struct {
2025-01-13 09:13:48 +01:00
repos Repositories
client client.Client
2024-12-27 11:20:32 +01:00
cmdArgs []CommandArgs
2024-10-29 07:22:04 +01:00
}
2025-01-13 09:13:48 +01:00
func NewCLI(repos Repositories, client client.Client) *CLI {
2024-12-27 11:20:32 +01:00
return &CLI{
2025-01-13 09:13:48 +01:00
repos: repos,
client: client,
2024-12-27 11:20:32 +01:00
cmdArgs: []CommandArgs{
2025-01-05 12:17:57 +01:00
NewShowArgs(), NewProjectsArgs(),
2024-12-27 11:20:32 +01:00
NewAddArgs(), NewDeleteArgs(), NewListArgs(),
NewSyncArgs(), NewUpdateArgs(),
},
2024-10-29 07:22:04 +01:00
}
2024-12-27 11:20:32 +01:00
}
func (cli *CLI) Run(args []string) error {
2024-12-29 09:32:49 +01:00
main, fields := FindFields(args)
2024-12-27 11:20:32 +01:00
for _, ca := range cli.cmdArgs {
2024-12-29 09:32:49 +01:00
cmd, err := ca.Parse(main, fields)
2024-10-29 07:22:04 +01:00
switch {
case errors.Is(err, ErrWrongCommand):
continue
case err != nil:
return err
}
2024-12-29 09:32:49 +01:00
2025-01-13 09:13:48 +01:00
result, err := cmd.Do(cli.repos, cli.client)
2024-12-29 09:32:49 +01:00
if err != nil {
return err
}
fmt.Println(result.Render())
return nil
2024-10-29 07:22:04 +01:00
}
return fmt.Errorf("could not find matching command")
}
2024-12-27 11:20:32 +01:00
func FindFields(args []string) ([]string, map[string]string) {
fields := make(map[string]string)
2024-10-29 07:22:04 +01:00
main := make([]string, 0)
for i := 0; i < len(args); i++ {
// normal key:value
2024-12-27 11:20:32 +01:00
if k, v, ok := strings.Cut(args[i], ":"); ok && !strings.Contains(k, " ") {
fields[k] = v
2024-10-29 07:22:04 +01:00
continue
}
// empty key:
if !strings.Contains(args[i], " ") && strings.HasSuffix(args[i], ":") {
k := strings.TrimSuffix(args[i], ":")
fields[k] = ""
}
2024-12-27 11:20:32 +01:00
main = append(main, args[i])
}
return main, fields
}
2024-10-29 07:22:04 +01:00
2024-12-27 11:20:32 +01:00
func ResolveFields(fields map[string]string, tmpl map[string][]string) (map[string]string, error) {
res := make(map[string]string)
for k, v := range fields {
for tk, tv := range tmpl {
if slices.Contains(tv, k) {
if _, ok := res[tk]; ok {
return nil, fmt.Errorf("%w: duplicate field: %v", ErrInvalidArg, tk)
}
res[tk] = v
delete(fields, k)
}
2024-10-29 07:22:04 +01:00
}
2024-12-27 11:20:32 +01:00
}
if len(fields) > 0 {
ks := make([]string, 0, len(fields))
for k := range fields {
ks = append(ks, k)
}
return nil, fmt.Errorf("%w: unknown field(s): %v", ErrInvalidArg, strings.Join(ks, ","))
2024-10-29 07:22:04 +01:00
}
2024-12-27 11:20:32 +01:00
return res, nil
2024-10-29 07:22:04 +01:00
}