Compare commits

..

3 Commits

Author SHA1 Message Date
Erik Winter 0abe8ad0ac mem 2025-01-20 10:52:03 +01:00
Erik Winter 6677d156d8 sqlite 2025-01-19 11:16:34 +01:00
Erik Winter 4818d7e71a type 2025-01-19 10:56:03 +01:00
23 changed files with 171 additions and 455 deletions

BIN
dist/plan vendored

Binary file not shown.

View File

@ -1,56 +0,0 @@
package arg
import (
"fmt"
"slices"
"strings"
"go-mod.ewintr.nl/planner/plan/command"
)
func FindFields(args []string) ([]string, map[string]string) {
fields := make(map[string]string)
main := make([]string, 0)
for i := 0; i < len(args); i++ {
if strings.HasPrefix(args[i], "http://") || strings.HasPrefix(args[i], "https://") {
main = append(main, args[i])
continue
}
// normal key:value
if k, v, ok := strings.Cut(args[i], ":"); ok && !strings.Contains(k, " ") {
fields[k] = v
continue
}
// empty key:
if !strings.Contains(args[i], " ") && strings.HasSuffix(args[i], ":") {
k := strings.TrimSuffix(args[i], ":")
fields[k] = ""
}
main = append(main, args[i])
}
return main, fields
}
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", command.ErrInvalidArg, tk)
}
res[tk] = v
delete(fields, k)
}
}
}
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", command.ErrInvalidArg, strings.Join(ks, ","))
}
return res, nil
}

View File

@ -1,57 +0,0 @@
package cli
import (
"errors"
"fmt"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/command/schedule"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/sync/client"
)
type CLI struct {
repos command.Repositories
client client.Client
cmdArgs []command.CommandArgs
}
func NewCLI(repos command.Repositories, client client.Client) *CLI {
return &CLI{
repos: repos,
client: client,
cmdArgs: []command.CommandArgs{
command.NewSyncArgs(),
// task
task.NewShowArgs(), task.NewProjectsArgs(),
task.NewAddArgs(), task.NewDeleteArgs(), task.NewListArgs(),
task.NewUpdateArgs(),
// schedule
schedule.NewAddArgs(),
},
}
}
func (cli *CLI) Run(args []string) error {
main, fields := arg.FindFields(args)
for _, ca := range cli.cmdArgs {
cmd, err := ca.Parse(main, fields)
switch {
case errors.Is(err, command.ErrWrongCommand):
continue
case err != nil:
return err
}
result, err := cmd.Do(cli.repos, cli.client)
if err != nil {
return err
}
fmt.Println(result.Render())
return nil
}
return fmt.Errorf("could not find matching command")
}

View File

@ -1,4 +1,4 @@
package task
package command
import (
"fmt"
@ -8,8 +8,6 @@ import (
"github.com/google/uuid"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/sync/client"
)
@ -31,16 +29,16 @@ func NewAddArgs() AddArgs {
}
}
func (aa AddArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error) {
if len(main) == 0 || !slices.Contains([]string{"add", "a", "new", "n"}, main[0]) {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
main = main[1:]
if len(main) == 0 {
return nil, fmt.Errorf("%w: title is required for add", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: title is required for add", ErrInvalidArg)
}
fields, err := arg.ResolveFields(fields, aa.fieldTPL)
fields, err := ResolveFields(fields, aa.fieldTPL)
if err != nil {
return nil, err
}
@ -58,28 +56,28 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (command.Comman
if val, ok := fields["date"]; ok {
d := item.NewDateFromString(val)
if d.IsZero() {
return nil, fmt.Errorf("%w: could not parse date", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
}
tsk.Date = d
}
if val, ok := fields["time"]; ok {
t := item.NewTimeFromString(val)
if t.IsZero() {
return nil, fmt.Errorf("%w: could not parse time", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
}
tsk.Time = t
}
if val, ok := fields["duration"]; ok {
d, err := time.ParseDuration(val)
if err != nil {
return nil, fmt.Errorf("%w: could not parse duration", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
}
tsk.Duration = d
}
if val, ok := fields["recurrer"]; ok {
rec := item.NewRecurrer(val)
if rec == nil {
return nil, fmt.Errorf("%w: could not parse recurrer", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
}
tsk.Recurrer = rec
tsk.RecurNext = tsk.Recurrer.First()
@ -96,7 +94,7 @@ type Add struct {
Args AddArgs
}
func (a Add) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (a Add) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)
@ -104,7 +102,7 @@ func (a Add) Do(repos command.Repositories, _ client.Client) (command.CommandRes
defer tx.Rollback()
if err := repos.Task(tx).Store(a.Args.Task); err != nil {
return nil, fmt.Errorf("could not store task: %v", err)
return nil, fmt.Errorf("could not store event: %v", err)
}
localID, err := repos.LocalID(tx).Next()
@ -117,7 +115,7 @@ func (a Add) Do(repos command.Repositories, _ client.Client) (command.CommandRes
it, err := a.Args.Task.Item()
if err != nil {
return nil, fmt.Errorf("could not convert task to sync item: %v", err)
return nil, fmt.Errorf("could not convert event to sync item: %v", err)
}
if err := repos.Sync(tx).Store(it); err != nil {
return nil, fmt.Errorf("could not store sync item: %v", err)

View File

@ -1,11 +1,11 @@
package task_test
package command_test
import (
"testing"
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
@ -63,7 +63,7 @@ func TestAdd(t *testing.T) {
mems := memory.New()
// parse
cmd, actParseErr := task.NewAddArgs().Parse(tc.main, tc.fields)
cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields)
if tc.expErr != (actParseErr != nil) {
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
}

View File

@ -2,6 +2,9 @@ package command
import (
"errors"
"fmt"
"slices"
"strings"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/sync/client"
@ -22,7 +25,6 @@ type Repositories interface {
LocalID(tx *storage.Tx) storage.LocalID
Sync(tx *storage.Tx) storage.Sync
Task(tx *storage.Tx) storage.Task
Schedule(tx *storage.Tx) storage.Schedule
}
type CommandArgs interface {
@ -36,3 +38,88 @@ type Command interface {
type CommandResult interface {
Render() string
}
type CLI struct {
repos Repositories
client client.Client
cmdArgs []CommandArgs
}
func NewCLI(repos Repositories, client client.Client) *CLI {
return &CLI{
repos: repos,
client: client,
cmdArgs: []CommandArgs{
NewShowArgs(), NewProjectsArgs(),
NewAddArgs(), NewDeleteArgs(), NewListArgs(),
NewSyncArgs(), NewUpdateArgs(),
},
}
}
func (cli *CLI) Run(args []string) error {
main, fields := FindFields(args)
for _, ca := range cli.cmdArgs {
cmd, err := ca.Parse(main, fields)
switch {
case errors.Is(err, ErrWrongCommand):
continue
case err != nil:
return err
}
result, err := cmd.Do(cli.repos, cli.client)
if err != nil {
return err
}
fmt.Println(result.Render())
return nil
}
return fmt.Errorf("could not find matching command")
}
func FindFields(args []string) ([]string, map[string]string) {
fields := make(map[string]string)
main := make([]string, 0)
for i := 0; i < len(args); i++ {
// normal key:value
if k, v, ok := strings.Cut(args[i], ":"); ok && !strings.Contains(k, " ") {
fields[k] = v
continue
}
// empty key:
if !strings.Contains(args[i], " ") && strings.HasSuffix(args[i], ":") {
k := strings.TrimSuffix(args[i], ":")
fields[k] = ""
}
main = append(main, args[i])
}
return main, fields
}
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)
}
}
}
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, ","))
}
return res, nil
}

View File

@ -1,14 +1,12 @@
package arg_test
package command_test
import (
"testing"
"github.com/google/go-cmp/cmp"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
)
// Please add a testcase to the following function that checks whether fields
// that start with "http:" and "https:" are ignored
func TestFindFields(t *testing.T) {
t.Parallel()
@ -55,17 +53,9 @@ func TestFindFields(t *testing.T) {
"flag1": "value1",
},
},
{
name: "ignore http and https fields",
args: []string{"one", "http://example.com", "two", "https://example.com", "flag1:value1"},
expMain: []string{"one", "http://example.com", "two", "https://example.com"},
expFields: map[string]string{
"flag1": "value1",
},
},
} {
t.Run(tc.name, func(t *testing.T) {
actMain, actFields := arg.FindFields(tc.args)
actMain, actFields := command.FindFields(tc.args)
if diff := cmp.Diff(tc.expMain, actMain); diff != "" {
t.Errorf("(exp +, got -)\n%s", diff)
}
@ -121,7 +111,7 @@ func TestResolveFields(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
actRes, actErr := arg.ResolveFields(tc.fields, tmpl)
actRes, actErr := command.ResolveFields(tc.fields, tmpl)
if tc.expErr != (actErr != nil) {
t.Errorf("exp %v, got %v", tc.expErr, actErr != nil)
}

View File

@ -1,11 +1,10 @@
package task
package command
import (
"fmt"
"slices"
"strconv"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/sync/client"
)
@ -18,9 +17,9 @@ func NewDeleteArgs() DeleteArgs {
return DeleteArgs{}
}
func (da DeleteArgs) Parse(main []string, flags map[string]string) (command.Command, error) {
func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, error) {
if len(main) != 2 {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
aliases := []string{"d", "delete", "done"}
var localIDStr string
@ -30,7 +29,7 @@ func (da DeleteArgs) Parse(main []string, flags map[string]string) (command.Comm
case slices.Contains(aliases, main[1]):
localIDStr = main[0]
default:
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
localID, err := strconv.Atoi(localIDStr)
@ -49,7 +48,7 @@ type Delete struct {
Args DeleteArgs
}
func (del Delete) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (del Delete) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)

View File

@ -1,11 +1,11 @@
package task_test
package command_test
import (
"errors"
"testing"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
@ -59,7 +59,7 @@ func TestDelete(t *testing.T) {
}
// parse
cmd, actParseErr := task.NewDeleteArgs().Parse(tc.main, tc.flags)
cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags)
if tc.expParseErr != (actParseErr != nil) {
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
}

View File

@ -1,4 +1,4 @@
package task
package command
import (
"fmt"
@ -7,8 +7,6 @@ import (
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/sync/client"
@ -33,12 +31,12 @@ func NewListArgs() ListArgs {
}
}
func (la ListArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
func (la ListArgs) Parse(main []string, fields map[string]string) (Command, error) {
if len(main) > 1 {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
fields, err := arg.ResolveFields(fields, la.fieldTPL)
fields, err := ResolveFields(fields, la.fieldTPL)
if err != nil {
return nil, err
}
@ -65,7 +63,7 @@ func (la ListArgs) Parse(main []string, fields map[string]string) (command.Comma
// fields["from"] = today.String()
// fields["to"] = today.String()
default:
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
}
@ -99,7 +97,7 @@ type List struct {
Args ListArgs
}
func (list List) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (list List) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)

View File

@ -1,4 +1,4 @@
package task_test
package command_test
import (
"testing"
@ -7,7 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
@ -20,20 +20,20 @@ func TestListParse(t *testing.T) {
name string
main []string
fields map[string]string
expArgs task.ListArgs
expArgs command.ListArgs
expErr bool
}{
{
name: "empty",
main: []string{},
fields: map[string]string{},
expArgs: task.ListArgs{},
expArgs: command.ListArgs{},
},
{
name: "today",
main: []string{"tod"},
fields: map[string]string{},
expArgs: task.ListArgs{
expArgs: command.ListArgs{
To: today,
},
},
@ -41,7 +41,7 @@ func TestListParse(t *testing.T) {
name: "tomorrow",
main: []string{"tom"},
fields: map[string]string{},
expArgs: task.ListArgs{
expArgs: command.ListArgs{
From: today.Add(1),
To: today.Add(1),
},
@ -50,14 +50,14 @@ func TestListParse(t *testing.T) {
name: "week",
main: []string{"week"},
fields: map[string]string{},
expArgs: task.ListArgs{
expArgs: command.ListArgs{
From: today,
To: today.Add(7),
},
},
} {
t.Run(tc.name, func(t *testing.T) {
nla := task.NewListArgs()
nla := command.NewListArgs()
cmd, actErr := nla.Parse(tc.main, tc.fields)
if tc.expErr != (actErr != nil) {
t.Errorf("exp %v, got %v", tc.expErr, actErr != nil)
@ -65,7 +65,7 @@ func TestListParse(t *testing.T) {
if tc.expErr {
return
}
listCmd, ok := cmd.(task.List)
listCmd, ok := cmd.(command.List)
if !ok {
t.Errorf("exp true, got false")
}
@ -97,7 +97,7 @@ func TestList(t *testing.T) {
for _, tc := range []struct {
name string
cmd task.List
cmd command.List
expRes bool
expErr bool
}{
@ -107,8 +107,8 @@ func TestList(t *testing.T) {
},
{
name: "empty list",
cmd: task.List{
Args: task.ListArgs{
cmd: command.List{
Args: command.ListArgs{
HasRecurrer: true,
},
},
@ -120,7 +120,7 @@ func TestList(t *testing.T) {
t.Errorf("exp nil, got %v", err)
}
listRes := res.(task.ListResult)
listRes := res.(command.ListResult)
actRes := len(listRes.Tasks) > 0
if tc.expRes != actRes {
t.Errorf("exp %v, got %v", tc.expRes, actRes)

View File

@ -1,10 +1,9 @@
package task
package command
import (
"fmt"
"sort"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/sync/client"
)
@ -15,9 +14,9 @@ func NewProjectsArgs() ProjectsArgs {
return ProjectsArgs{}
}
func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (Command, error) {
if len(main) != 1 || main[0] != "projects" {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
return Projects{}, nil
@ -25,7 +24,7 @@ func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (command.C
type Projects struct{}
func (ps Projects) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (ps Projects) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)

View File

@ -1,124 +0,0 @@
package schedule
import (
"fmt"
"slices"
"strings"
"github.com/google/uuid"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/sync/client"
)
type AddArgs struct {
fieldTPL map[string][]string
Schedule item.Schedule
}
func NewAddArgs() AddArgs {
return AddArgs{
fieldTPL: map[string][]string{
"date": {"d", "date", "on"},
"recurrer": {"rec", "recurrer"},
},
}
}
func (aa AddArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
if len(main) == 0 || !slices.Contains([]string{"s", "sched", "schedule"}, main[0]) {
return nil, command.ErrWrongCommand
}
main = main[1:]
if len(main) == 0 || !slices.Contains([]string{"add", "a", "new", "n"}, main[0]) {
return nil, command.ErrWrongCommand
}
main = main[1:]
if len(main) == 0 {
return nil, fmt.Errorf("%w: title is required for add", command.ErrInvalidArg)
}
fields, err := arg.ResolveFields(fields, aa.fieldTPL)
if err != nil {
return nil, err
}
sched := item.Schedule{
ID: uuid.New().String(),
ScheduleBody: item.ScheduleBody{
Title: strings.Join(main, " "),
},
}
if val, ok := fields["date"]; ok {
d := item.NewDateFromString(val)
if d.IsZero() {
return nil, fmt.Errorf("%w: could not parse date", command.ErrInvalidArg)
}
sched.Date = d
}
if val, ok := fields["recurrer"]; ok {
rec := item.NewRecurrer(val)
if rec == nil {
return nil, fmt.Errorf("%w: could not parse recurrer", command.ErrInvalidArg)
}
sched.Recurrer = rec
sched.RecurNext = sched.Recurrer.First()
}
return &Add{
Args: AddArgs{
Schedule: sched,
},
}, nil
}
type Add struct {
Args AddArgs
}
func (a Add) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)
}
defer tx.Rollback()
if err := repos.Schedule(tx).Store(a.Args.Schedule); err != nil {
return nil, fmt.Errorf("could not store schedule: %v", err)
}
localID, err := repos.LocalID(tx).Next()
if err != nil {
return nil, fmt.Errorf("could not create next local id: %v", err)
}
if err := repos.LocalID(tx).Store(a.Args.Schedule.ID, localID); err != nil {
return nil, fmt.Errorf("could not store local id: %v", err)
}
it, err := a.Args.Schedule.Item()
if err != nil {
return nil, fmt.Errorf("could not convert schedule to sync item: %v", err)
}
if err := repos.Sync(tx).Store(it); err != nil {
return nil, fmt.Errorf("could not store sync item: %v", err)
}
if err := tx.Commit(); err != nil {
return nil, fmt.Errorf("could not add schedule: %v", err)
}
return AddResult{
LocalID: localID,
}, nil
}
type AddResult struct {
LocalID int
}
func (ar AddResult) Render() string {
return fmt.Sprintf("stored schedule %s", format.Bold(fmt.Sprintf("%d", ar.LocalID)))
}

View File

@ -1,105 +0,0 @@
package schedule_test
import (
"testing"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/schedule"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
func TestAdd(t *testing.T) {
t.Parallel()
aDate := item.NewDate(2024, 11, 2)
for _, tc := range []struct {
name string
main []string
fields map[string]string
expErr bool
expSchedule item.Schedule
}{
{
name: "empty",
expErr: true,
},
{
name: "title missing",
main: []string{"sched", "add"},
fields: map[string]string{
"date": aDate.String(),
},
expErr: true,
},
{
name: "all",
main: []string{"sched", "add", "title"},
fields: map[string]string{
"date": aDate.String(),
},
expSchedule: item.Schedule{
ID: "title",
Date: aDate,
ScheduleBody: item.ScheduleBody{
Title: "title",
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
// setup
mems := memory.New()
// parse
cmd, actParseErr := schedule.NewAddArgs().Parse(tc.main, tc.fields)
if tc.expErr != (actParseErr != nil) {
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
}
if tc.expErr {
return
}
// do
if _, err := cmd.Do(mems, nil); err != nil {
t.Errorf("exp nil, got %v", err)
}
// check
actSchedules, err := mems.Schedule(nil).Find(aDate.Add(-1), aDate.Add(1))
if err != nil {
t.Errorf("exp nil, got %v", err)
}
if len(actSchedules) != 1 {
t.Errorf("exp 1, got %d", len(actSchedules))
}
actLocalIDs, err := mems.LocalID(nil).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[actSchedules[0].ID]; !ok {
t.Errorf("exp true, got %v", ok)
}
if actSchedules[0].ID == "" {
t.Errorf("exp string not te be empty")
}
tc.expSchedule.ID = actSchedules[0].ID
if diff := item.ScheduleDiff(tc.expSchedule, actSchedules[0]); diff != "" {
t.Errorf("(exp -, got +)\n%s", diff)
}
updated, err := mems.Sync(nil).FindAll()
if err != nil {
t.Errorf("exp nil, got %v", err)
}
if len(updated) != 1 {
t.Errorf("exp 1, got %v", len(updated))
}
})
}
}

View File

@ -1,4 +1,4 @@
package task
package command
import (
"errors"
@ -6,7 +6,6 @@ import (
"strconv"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/sync/client"
@ -20,13 +19,13 @@ func NewShowArgs() ShowArgs {
return ShowArgs{}
}
func (sa ShowArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
func (sa ShowArgs) Parse(main []string, fields map[string]string) (Command, error) {
if len(main) != 1 {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
lid, err := strconv.Atoi(main[0])
if err != nil {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
return &Show{
@ -40,7 +39,7 @@ type Show struct {
args ShowArgs
}
func (s Show) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (s Show) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)

View File

@ -1,11 +1,11 @@
package task_test
package command_test
import (
"fmt"
"testing"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
@ -60,7 +60,7 @@ func TestShow(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
// parse
cmd, actParseErr := task.NewShowArgs().Parse(tc.main, nil)
cmd, actParseErr := command.NewShowArgs().Parse(tc.main, nil)
if tc.expParseErr != (actParseErr != nil) {
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr != nil)
}

View File

@ -1,4 +1,4 @@
package task
package command
import (
"errors"
@ -9,8 +9,6 @@ import (
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/cli/arg"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/format"
"go-mod.ewintr.nl/planner/plan/storage"
"go-mod.ewintr.nl/planner/sync/client"
@ -40,9 +38,9 @@ func NewUpdateArgs() UpdateArgs {
}
}
func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, error) {
if len(main) < 2 {
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
aliases := []string{"u", "update", "m", "mod"}
var localIDStr string
@ -52,13 +50,13 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Com
case slices.Contains(aliases, main[1]):
localIDStr = main[0]
default:
return nil, command.ErrWrongCommand
return nil, ErrWrongCommand
}
localID, err := strconv.Atoi(localIDStr)
if err != nil {
return nil, fmt.Errorf("not a local id: %v", main[1])
}
fields, err = arg.ResolveFields(fields, ua.fieldTPL)
fields, err = ResolveFields(fields, ua.fieldTPL)
if err != nil {
return nil, err
}
@ -77,7 +75,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Com
if val != "" {
d := item.NewDateFromString(val)
if d.IsZero() {
return nil, fmt.Errorf("%w: could not parse date", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
}
args.Date = d
}
@ -87,7 +85,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Com
if val != "" {
t := item.NewTimeFromString(val)
if t.IsZero() {
return nil, fmt.Errorf("%w: could not parse time", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
}
args.Time = t
}
@ -97,7 +95,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Com
if val != "" {
d, err := time.ParseDuration(val)
if err != nil {
return nil, fmt.Errorf("%w: could not parse duration", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
}
args.Duration = d
}
@ -107,7 +105,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Com
if val != "" {
rec := item.NewRecurrer(val)
if rec == nil {
return nil, fmt.Errorf("%w: could not parse recurrer", command.ErrInvalidArg)
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
}
args.Recurrer = rec
}
@ -120,7 +118,7 @@ type Update struct {
args UpdateArgs
}
func (u Update) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
func (u Update) Do(repos Repositories, _ client.Client) (CommandResult, error) {
tx, err := repos.Begin()
if err != nil {
return nil, fmt.Errorf("could not start transaction: %v", err)

View File

@ -1,4 +1,4 @@
package task_test
package command_test
import (
"fmt"
@ -6,7 +6,7 @@ import (
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/command/task"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage/memory"
)
@ -207,7 +207,7 @@ func TestUpdateExecute(t *testing.T) {
}
// parse
cmd, actErr := task.NewUpdateArgs().Parse(tc.main, tc.fields)
cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields)
if tc.expParseErr != (actErr != nil) {
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
}

View File

@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
"go-mod.ewintr.nl/planner/plan/cli"
"go-mod.ewintr.nl/planner/plan/command"
"go-mod.ewintr.nl/planner/plan/storage/sqlite"
"go-mod.ewintr.nl/planner/sync/client"
"gopkg.in/yaml.v3"
@ -35,7 +35,7 @@ func main() {
syncClient := client.New(conf.SyncURL, conf.ApiKey)
cli := cli.NewCLI(repos, syncClient)
cli := command.NewCLI(repos, syncClient)
if err := cli.Run(os.Args[1:]); err != nil {
fmt.Println(err)
os.Exit(1)

View File

@ -8,7 +8,6 @@ type Memories struct {
localID *LocalID
sync *Sync
task *Task
schedule *Schedule
}
func New() *Memories {
@ -16,7 +15,6 @@ func New() *Memories {
localID: NewLocalID(),
sync: NewSync(),
task: NewTask(),
schedule: NewSchedule(),
}
}
@ -35,7 +33,3 @@ func (mems *Memories) Sync(_ *storage.Tx) storage.Sync {
func (mems *Memories) Task(_ *storage.Tx) storage.Task {
return mems.task
}
func (mems *Memories) Schedule(_ *storage.Tx) storage.Schedule {
return mems.schedule
}

View File

@ -18,14 +18,14 @@ func (ss *SqliteSchedule) Store(sched item.Schedule) error {
}
if _, err := ss.tx.Exec(`
INSERT INTO schedules
(id, title, date, recur)
(id, title, date, recurrer)
VALUES
(?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE
SET
title=?,
date=?,
recur=?
recurrer=?
`,
sched.ID, sched.Title, sched.Date.String(), recurStr,
sched.Title, sched.Date.String(), recurStr); err != nil {
@ -36,7 +36,7 @@ recur=?
func (ss *SqliteSchedule) Find(start, end item.Date) ([]item.Schedule, error) {
rows, err := ss.tx.Query(`SELECT
id, title, date, recur
id, title, date, recurrer
FROM schedules
WHERE date >= ? AND date <= ?`, start.String(), end.String())
if err != nil {

View File

@ -44,10 +44,6 @@ func (sqs *Sqlites) Task(tx *storage.Tx) storage.Task {
return &SqliteTask{tx: tx}
}
func (sqs *Sqlites) Schedule(tx *storage.Tx) storage.Schedule {
return &SqliteSchedule{tx: tx}
}
func NewSqlites(dbPath string) (*Sqlites, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil {

View File

@ -24,7 +24,7 @@ func New(url, apiKey string) *HTTP {
baseURL: url,
apiKey: apiKey,
c: &http.Client{
Timeout: 300 * time.Second,
Timeout: 10 * time.Second,
},
}
}