schedule add
This commit is contained in:
parent
f28a364df5
commit
ad78080e62
|
@ -2,12 +2,17 @@ package item
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidKind = errors.New("invalid kind")
|
||||||
|
)
|
||||||
|
|
||||||
type Kind string
|
type Kind string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package item
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScheduleBody struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schedule struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Date Date `json:"date"`
|
||||||
|
Recurrer Recurrer `json:"recurrer"`
|
||||||
|
RecurNext Date `json:"recurNext"`
|
||||||
|
ScheduleBody
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSchedule(i Item) (Schedule, error) {
|
||||||
|
if i.Kind != KindSchedule {
|
||||||
|
return Schedule{}, ErrInvalidKind
|
||||||
|
}
|
||||||
|
|
||||||
|
var s Schedule
|
||||||
|
if err := json.Unmarshal([]byte(i.Body), &s); err != nil {
|
||||||
|
return Schedule{}, fmt.Errorf("could not unmarshal item body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ID = i.ID
|
||||||
|
s.Date = i.Date
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Schedule) Item() (Item, error) {
|
||||||
|
body, err := json.Marshal(s.ScheduleBody)
|
||||||
|
if err != nil {
|
||||||
|
return Item{}, fmt.Errorf("could not marshal schedule body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Item{
|
||||||
|
ID: s.ID,
|
||||||
|
Kind: KindSchedule,
|
||||||
|
Date: s.Date,
|
||||||
|
Body: string(body),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ScheduleDiff(a, b Schedule) string {
|
||||||
|
aJSON, _ := json.Marshal(a)
|
||||||
|
bJSON, _ := json.Marshal(b)
|
||||||
|
|
||||||
|
return cmp.Diff(string(aJSON), string(bJSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ScheduleDiffs(a, b []Schedule) string {
|
||||||
|
aJSON, _ := json.Marshal(a)
|
||||||
|
bJSON, _ := json.Marshal(b)
|
||||||
|
|
||||||
|
return cmp.Diff(string(aJSON), string(bJSON))
|
||||||
|
}
|
|
@ -56,7 +56,7 @@ type Task struct {
|
||||||
|
|
||||||
func NewTask(i Item) (Task, error) {
|
func NewTask(i Item) (Task, error) {
|
||||||
if i.Kind != KindTask {
|
if i.Kind != KindTask {
|
||||||
return Task{}, fmt.Errorf("item is not an task")
|
return Task{}, ErrInvalidKind
|
||||||
}
|
}
|
||||||
|
|
||||||
var t Task
|
var t Task
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
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++ {
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package command_test
|
package arg_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/cli/arg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindFields(t *testing.T) {
|
func TestFindFields(t *testing.T) {
|
||||||
|
@ -55,7 +55,7 @@ func TestFindFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
actMain, actFields := command.FindFields(tc.args)
|
actMain, actFields := arg.FindFields(tc.args)
|
||||||
if diff := cmp.Diff(tc.expMain, actMain); diff != "" {
|
if diff := cmp.Diff(tc.expMain, actMain); diff != "" {
|
||||||
t.Errorf("(exp +, got -)\n%s", diff)
|
t.Errorf("(exp +, got -)\n%s", diff)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func TestResolveFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
actRes, actErr := command.ResolveFields(tc.fields, tmpl)
|
actRes, actErr := arg.ResolveFields(tc.fields, tmpl)
|
||||||
if tc.expErr != (actErr != nil) {
|
if tc.expErr != (actErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actErr != nil)
|
t.Errorf("exp %v, got %v", tc.expErr, actErr != nil)
|
||||||
}
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
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")
|
||||||
|
}
|
|
@ -2,9 +2,6 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
|
@ -25,6 +22,7 @@ type Repositories interface {
|
||||||
LocalID(tx *storage.Tx) storage.LocalID
|
LocalID(tx *storage.Tx) storage.LocalID
|
||||||
Sync(tx *storage.Tx) storage.Sync
|
Sync(tx *storage.Tx) storage.Sync
|
||||||
Task(tx *storage.Tx) storage.Task
|
Task(tx *storage.Tx) storage.Task
|
||||||
|
Schedule(tx *storage.Tx) storage.Schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandArgs interface {
|
type CommandArgs interface {
|
||||||
|
@ -38,88 +36,3 @@ type Command interface {
|
||||||
type CommandResult interface {
|
type CommandResult interface {
|
||||||
Render() string
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
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)))
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,6 +8,8 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"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/format"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
)
|
)
|
||||||
|
@ -29,16 +31,16 @@ func NewAddArgs() AddArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
func (aa AddArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
|
||||||
if len(main) == 0 || !slices.Contains([]string{"add", "a", "new", "n"}, main[0]) {
|
if len(main) == 0 || !slices.Contains([]string{"add", "a", "new", "n"}, main[0]) {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
main = main[1:]
|
main = main[1:]
|
||||||
if len(main) == 0 {
|
if len(main) == 0 {
|
||||||
return nil, fmt.Errorf("%w: title is required for add", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: title is required for add", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
fields, err := ResolveFields(fields, aa.fieldTPL)
|
fields, err := arg.ResolveFields(fields, aa.fieldTPL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -56,28 +58,28 @@ func (aa AddArgs) Parse(main []string, fields map[string]string) (Command, error
|
||||||
if val, ok := fields["date"]; ok {
|
if val, ok := fields["date"]; ok {
|
||||||
d := item.NewDateFromString(val)
|
d := item.NewDateFromString(val)
|
||||||
if d.IsZero() {
|
if d.IsZero() {
|
||||||
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse date", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
tsk.Date = d
|
tsk.Date = d
|
||||||
}
|
}
|
||||||
if val, ok := fields["time"]; ok {
|
if val, ok := fields["time"]; ok {
|
||||||
t := item.NewTimeFromString(val)
|
t := item.NewTimeFromString(val)
|
||||||
if t.IsZero() {
|
if t.IsZero() {
|
||||||
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse time", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
tsk.Time = t
|
tsk.Time = t
|
||||||
}
|
}
|
||||||
if val, ok := fields["duration"]; ok {
|
if val, ok := fields["duration"]; ok {
|
||||||
d, err := time.ParseDuration(val)
|
d, err := time.ParseDuration(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse duration", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
tsk.Duration = d
|
tsk.Duration = d
|
||||||
}
|
}
|
||||||
if val, ok := fields["recurrer"]; ok {
|
if val, ok := fields["recurrer"]; ok {
|
||||||
rec := item.NewRecurrer(val)
|
rec := item.NewRecurrer(val)
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse recurrer", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
tsk.Recurrer = rec
|
tsk.Recurrer = rec
|
||||||
tsk.RecurNext = tsk.Recurrer.First()
|
tsk.RecurNext = tsk.Recurrer.First()
|
||||||
|
@ -94,7 +96,7 @@ type Add struct {
|
||||||
Args AddArgs
|
Args AddArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Add) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (a Add) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
||||||
|
@ -102,7 +104,7 @@ func (a Add) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
if err := repos.Task(tx).Store(a.Args.Task); err != nil {
|
if err := repos.Task(tx).Store(a.Args.Task); err != nil {
|
||||||
return nil, fmt.Errorf("could not store event: %v", err)
|
return nil, fmt.Errorf("could not store task: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localID, err := repos.LocalID(tx).Next()
|
localID, err := repos.LocalID(tx).Next()
|
||||||
|
@ -115,7 +117,7 @@ func (a Add) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
||||||
|
|
||||||
it, err := a.Args.Task.Item()
|
it, err := a.Args.Task.Item()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not convert event to sync item: %v", err)
|
return nil, fmt.Errorf("could not convert task to sync item: %v", err)
|
||||||
}
|
}
|
||||||
if err := repos.Sync(tx).Store(it); err != nil {
|
if err := repos.Sync(tx).Store(it); err != nil {
|
||||||
return nil, fmt.Errorf("could not store sync item: %v", err)
|
return nil, fmt.Errorf("could not store sync item: %v", err)
|
|
@ -1,11 +1,11 @@
|
||||||
package command_test
|
package task_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command/task"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
@ -63,7 +63,7 @@ func TestAdd(t *testing.T) {
|
||||||
mems := memory.New()
|
mems := memory.New()
|
||||||
|
|
||||||
// parse
|
// parse
|
||||||
cmd, actParseErr := command.NewAddArgs().Parse(tc.main, tc.fields)
|
cmd, actParseErr := task.NewAddArgs().Parse(tc.main, tc.fields)
|
||||||
if tc.expErr != (actParseErr != nil) {
|
if tc.expErr != (actParseErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
|
||||||
}
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/plan/command"
|
||||||
"go-mod.ewintr.nl/planner/plan/format"
|
"go-mod.ewintr.nl/planner/plan/format"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
)
|
)
|
||||||
|
@ -17,9 +18,9 @@ func NewDeleteArgs() DeleteArgs {
|
||||||
return DeleteArgs{}
|
return DeleteArgs{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, error) {
|
func (da DeleteArgs) Parse(main []string, flags map[string]string) (command.Command, error) {
|
||||||
if len(main) != 2 {
|
if len(main) != 2 {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
aliases := []string{"d", "delete", "done"}
|
aliases := []string{"d", "delete", "done"}
|
||||||
var localIDStr string
|
var localIDStr string
|
||||||
|
@ -29,7 +30,7 @@ func (da DeleteArgs) Parse(main []string, flags map[string]string) (Command, err
|
||||||
case slices.Contains(aliases, main[1]):
|
case slices.Contains(aliases, main[1]):
|
||||||
localIDStr = main[0]
|
localIDStr = main[0]
|
||||||
default:
|
default:
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
localID, err := strconv.Atoi(localIDStr)
|
localID, err := strconv.Atoi(localIDStr)
|
||||||
|
@ -48,7 +49,7 @@ type Delete struct {
|
||||||
Args DeleteArgs
|
Args DeleteArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (del Delete) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (del Delete) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
|
@ -1,11 +1,11 @@
|
||||||
package command_test
|
package task_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command/task"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ func TestDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse
|
// parse
|
||||||
cmd, actParseErr := command.NewDeleteArgs().Parse(tc.main, tc.flags)
|
cmd, actParseErr := task.NewDeleteArgs().Parse(tc.main, tc.flags)
|
||||||
if tc.expParseErr != (actParseErr != nil) {
|
if tc.expParseErr != (actParseErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
|
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -7,6 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"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/format"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
|
@ -31,12 +33,12 @@ func NewListArgs() ListArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (la ListArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
func (la ListArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
|
||||||
if len(main) > 1 {
|
if len(main) > 1 {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
fields, err := ResolveFields(fields, la.fieldTPL)
|
fields, err := arg.ResolveFields(fields, la.fieldTPL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -63,7 +65,7 @@ func (la ListArgs) Parse(main []string, fields map[string]string) (Command, erro
|
||||||
// fields["from"] = today.String()
|
// fields["from"] = today.String()
|
||||||
// fields["to"] = today.String()
|
// fields["to"] = today.String()
|
||||||
default:
|
default:
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +99,7 @@ type List struct {
|
||||||
Args ListArgs
|
Args ListArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list List) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (list List) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
|
@ -1,4 +1,4 @@
|
||||||
package command_test
|
package task_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command/task"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,20 +20,20 @@ func TestListParse(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
main []string
|
main []string
|
||||||
fields map[string]string
|
fields map[string]string
|
||||||
expArgs command.ListArgs
|
expArgs task.ListArgs
|
||||||
expErr bool
|
expErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
main: []string{},
|
main: []string{},
|
||||||
fields: map[string]string{},
|
fields: map[string]string{},
|
||||||
expArgs: command.ListArgs{},
|
expArgs: task.ListArgs{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "today",
|
name: "today",
|
||||||
main: []string{"tod"},
|
main: []string{"tod"},
|
||||||
fields: map[string]string{},
|
fields: map[string]string{},
|
||||||
expArgs: command.ListArgs{
|
expArgs: task.ListArgs{
|
||||||
To: today,
|
To: today,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -41,7 +41,7 @@ func TestListParse(t *testing.T) {
|
||||||
name: "tomorrow",
|
name: "tomorrow",
|
||||||
main: []string{"tom"},
|
main: []string{"tom"},
|
||||||
fields: map[string]string{},
|
fields: map[string]string{},
|
||||||
expArgs: command.ListArgs{
|
expArgs: task.ListArgs{
|
||||||
From: today.Add(1),
|
From: today.Add(1),
|
||||||
To: today.Add(1),
|
To: today.Add(1),
|
||||||
},
|
},
|
||||||
|
@ -50,14 +50,14 @@ func TestListParse(t *testing.T) {
|
||||||
name: "week",
|
name: "week",
|
||||||
main: []string{"week"},
|
main: []string{"week"},
|
||||||
fields: map[string]string{},
|
fields: map[string]string{},
|
||||||
expArgs: command.ListArgs{
|
expArgs: task.ListArgs{
|
||||||
From: today,
|
From: today,
|
||||||
To: today.Add(7),
|
To: today.Add(7),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
nla := command.NewListArgs()
|
nla := task.NewListArgs()
|
||||||
cmd, actErr := nla.Parse(tc.main, tc.fields)
|
cmd, actErr := nla.Parse(tc.main, tc.fields)
|
||||||
if tc.expErr != (actErr != nil) {
|
if tc.expErr != (actErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", 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 {
|
if tc.expErr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
listCmd, ok := cmd.(command.List)
|
listCmd, ok := cmd.(task.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("exp true, got false")
|
t.Errorf("exp true, got false")
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ func TestList(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
cmd command.List
|
cmd task.List
|
||||||
expRes bool
|
expRes bool
|
||||||
expErr bool
|
expErr bool
|
||||||
}{
|
}{
|
||||||
|
@ -107,8 +107,8 @@ func TestList(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty list",
|
name: "empty list",
|
||||||
cmd: command.List{
|
cmd: task.List{
|
||||||
Args: command.ListArgs{
|
Args: task.ListArgs{
|
||||||
HasRecurrer: true,
|
HasRecurrer: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -120,7 +120,7 @@ func TestList(t *testing.T) {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
listRes := res.(command.ListResult)
|
listRes := res.(task.ListResult)
|
||||||
actRes := len(listRes.Tasks) > 0
|
actRes := len(listRes.Tasks) > 0
|
||||||
if tc.expRes != actRes {
|
if tc.expRes != actRes {
|
||||||
t.Errorf("exp %v, got %v", tc.expRes, actRes)
|
t.Errorf("exp %v, got %v", tc.expRes, actRes)
|
|
@ -1,9 +1,10 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/plan/command"
|
||||||
"go-mod.ewintr.nl/planner/plan/format"
|
"go-mod.ewintr.nl/planner/plan/format"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
)
|
)
|
||||||
|
@ -14,9 +15,9 @@ func NewProjectsArgs() ProjectsArgs {
|
||||||
return ProjectsArgs{}
|
return ProjectsArgs{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
|
||||||
if len(main) != 1 || main[0] != "projects" {
|
if len(main) != 1 || main[0] != "projects" {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return Projects{}, nil
|
return Projects{}, nil
|
||||||
|
@ -24,7 +25,7 @@ func (pa ProjectsArgs) Parse(main []string, fields map[string]string) (Command,
|
||||||
|
|
||||||
type Projects struct{}
|
type Projects struct{}
|
||||||
|
|
||||||
func (ps Projects) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (ps Projects) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"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/format"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
|
@ -19,13 +20,13 @@ func NewShowArgs() ShowArgs {
|
||||||
return ShowArgs{}
|
return ShowArgs{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sa ShowArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
func (sa ShowArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
|
||||||
if len(main) != 1 {
|
if len(main) != 1 {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
lid, err := strconv.Atoi(main[0])
|
lid, err := strconv.Atoi(main[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Show{
|
return &Show{
|
||||||
|
@ -39,7 +40,7 @@ type Show struct {
|
||||||
args ShowArgs
|
args ShowArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Show) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (s Show) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
|
@ -1,11 +1,11 @@
|
||||||
package command_test
|
package task_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command/task"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
"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) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// parse
|
// parse
|
||||||
cmd, actParseErr := command.NewShowArgs().Parse(tc.main, nil)
|
cmd, actParseErr := task.NewShowArgs().Parse(tc.main, nil)
|
||||||
if tc.expParseErr != (actParseErr != nil) {
|
if tc.expParseErr != (actParseErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr != nil)
|
t.Errorf("exp %v, got %v", tc.expParseErr, actParseErr != nil)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -9,6 +9,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"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/format"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage"
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
|
@ -38,9 +40,9 @@ func NewUpdateArgs() UpdateArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, error) {
|
func (ua UpdateArgs) Parse(main []string, fields map[string]string) (command.Command, error) {
|
||||||
if len(main) < 2 {
|
if len(main) < 2 {
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
aliases := []string{"u", "update", "m", "mod"}
|
aliases := []string{"u", "update", "m", "mod"}
|
||||||
var localIDStr string
|
var localIDStr string
|
||||||
|
@ -50,13 +52,13 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, er
|
||||||
case slices.Contains(aliases, main[1]):
|
case slices.Contains(aliases, main[1]):
|
||||||
localIDStr = main[0]
|
localIDStr = main[0]
|
||||||
default:
|
default:
|
||||||
return nil, ErrWrongCommand
|
return nil, command.ErrWrongCommand
|
||||||
}
|
}
|
||||||
localID, err := strconv.Atoi(localIDStr)
|
localID, err := strconv.Atoi(localIDStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("not a local id: %v", main[1])
|
return nil, fmt.Errorf("not a local id: %v", main[1])
|
||||||
}
|
}
|
||||||
fields, err = ResolveFields(fields, ua.fieldTPL)
|
fields, err = arg.ResolveFields(fields, ua.fieldTPL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -75,7 +77,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, er
|
||||||
if val != "" {
|
if val != "" {
|
||||||
d := item.NewDateFromString(val)
|
d := item.NewDateFromString(val)
|
||||||
if d.IsZero() {
|
if d.IsZero() {
|
||||||
return nil, fmt.Errorf("%w: could not parse date", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse date", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
args.Date = d
|
args.Date = d
|
||||||
}
|
}
|
||||||
|
@ -85,7 +87,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, er
|
||||||
if val != "" {
|
if val != "" {
|
||||||
t := item.NewTimeFromString(val)
|
t := item.NewTimeFromString(val)
|
||||||
if t.IsZero() {
|
if t.IsZero() {
|
||||||
return nil, fmt.Errorf("%w: could not parse time", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse time", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
args.Time = t
|
args.Time = t
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, er
|
||||||
if val != "" {
|
if val != "" {
|
||||||
d, err := time.ParseDuration(val)
|
d, err := time.ParseDuration(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: could not parse duration", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse duration", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
args.Duration = d
|
args.Duration = d
|
||||||
}
|
}
|
||||||
|
@ -105,7 +107,7 @@ func (ua UpdateArgs) Parse(main []string, fields map[string]string) (Command, er
|
||||||
if val != "" {
|
if val != "" {
|
||||||
rec := item.NewRecurrer(val)
|
rec := item.NewRecurrer(val)
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
return nil, fmt.Errorf("%w: could not parse recurrer", ErrInvalidArg)
|
return nil, fmt.Errorf("%w: could not parse recurrer", command.ErrInvalidArg)
|
||||||
}
|
}
|
||||||
args.Recurrer = rec
|
args.Recurrer = rec
|
||||||
}
|
}
|
||||||
|
@ -118,7 +120,7 @@ type Update struct {
|
||||||
args UpdateArgs
|
args UpdateArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u Update) Do(repos Repositories, _ client.Client) (CommandResult, error) {
|
func (u Update) Do(repos command.Repositories, _ client.Client) (command.CommandResult, error) {
|
||||||
tx, err := repos.Begin()
|
tx, err := repos.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start transaction: %v", err)
|
return nil, fmt.Errorf("could not start transaction: %v", err)
|
|
@ -1,4 +1,4 @@
|
||||||
package command_test
|
package task_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
"go-mod.ewintr.nl/planner/item"
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/command/task"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ func TestUpdateExecute(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse
|
// parse
|
||||||
cmd, actErr := command.NewUpdateArgs().Parse(tc.main, tc.fields)
|
cmd, actErr := task.NewUpdateArgs().Parse(tc.main, tc.fields)
|
||||||
if tc.expParseErr != (actErr != nil) {
|
if tc.expParseErr != (actErr != nil) {
|
||||||
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
|
t.Errorf("exp %v, got %v", tc.expParseErr, actErr)
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/plan/command"
|
"go-mod.ewintr.nl/planner/plan/cli"
|
||||||
"go-mod.ewintr.nl/planner/plan/storage/sqlite"
|
"go-mod.ewintr.nl/planner/plan/storage/sqlite"
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
@ -35,7 +35,7 @@ func main() {
|
||||||
|
|
||||||
syncClient := client.New(conf.SyncURL, conf.ApiKey)
|
syncClient := client.New(conf.SyncURL, conf.ApiKey)
|
||||||
|
|
||||||
cli := command.NewCLI(repos, syncClient)
|
cli := cli.NewCLI(repos, syncClient)
|
||||||
if err := cli.Run(os.Args[1:]); err != nil {
|
if err := cli.Run(os.Args[1:]); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -5,16 +5,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Memories struct {
|
type Memories struct {
|
||||||
localID *LocalID
|
localID *LocalID
|
||||||
sync *Sync
|
sync *Sync
|
||||||
task *Task
|
task *Task
|
||||||
|
schedule *Schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Memories {
|
func New() *Memories {
|
||||||
return &Memories{
|
return &Memories{
|
||||||
localID: NewLocalID(),
|
localID: NewLocalID(),
|
||||||
sync: NewSync(),
|
sync: NewSync(),
|
||||||
task: NewTask(),
|
task: NewTask(),
|
||||||
|
schedule: NewSchedule(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,3 +35,7 @@ func (mems *Memories) Sync(_ *storage.Tx) storage.Sync {
|
||||||
func (mems *Memories) Task(_ *storage.Tx) storage.Task {
|
func (mems *Memories) Task(_ *storage.Tx) storage.Task {
|
||||||
return mems.task
|
return mems.task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mems *Memories) Schedule(_ *storage.Tx) storage.Schedule {
|
||||||
|
return mems.schedule
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Schedule struct {
|
||||||
|
scheds map[string]item.Schedule
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSchedule() *Schedule {
|
||||||
|
return &Schedule{
|
||||||
|
scheds: make(map[string]item.Schedule),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schedule) Store(sched item.Schedule) error {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
s.scheds[sched.ID] = sched
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schedule) Find(start, end item.Date) ([]item.Schedule, error) {
|
||||||
|
s.mutex.RLock()
|
||||||
|
defer s.mutex.RUnlock()
|
||||||
|
|
||||||
|
res := make([]item.Schedule, 0)
|
||||||
|
for _, sched := range s.scheds {
|
||||||
|
if start.After(sched.Date) || sched.Date.After(end) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res = append(res, sched)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schedule) Delete(id string) error {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
if _, exists := s.scheds[id]; !exists {
|
||||||
|
return storage.ErrNotFound
|
||||||
|
}
|
||||||
|
delete(s.scheds, id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package memory_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSchedule(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mem := memory.NewSchedule()
|
||||||
|
|
||||||
|
actScheds, actErr := mem.Find(item.NewDateFromString("1900-01-01"), item.NewDateFromString("9999-12-31"))
|
||||||
|
if actErr != nil {
|
||||||
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
|
}
|
||||||
|
if len(actScheds) != 0 {
|
||||||
|
t.Errorf("exp 0, got %d", len(actScheds))
|
||||||
|
}
|
||||||
|
|
||||||
|
s1 := item.Schedule{
|
||||||
|
ID: "id-1",
|
||||||
|
Date: item.NewDateFromString("2025-01-20"),
|
||||||
|
}
|
||||||
|
if err := mem.Store(s1); err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
|
s2 := item.Schedule{
|
||||||
|
ID: "id-2",
|
||||||
|
Date: item.NewDateFromString("2025-01-21"),
|
||||||
|
}
|
||||||
|
if err := mem.Store(s2); err != nil {
|
||||||
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
start string
|
||||||
|
end string
|
||||||
|
exp []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all",
|
||||||
|
start: "1900-01-01",
|
||||||
|
end: "9999-12-31",
|
||||||
|
exp: []string{s1.ID, s2.ID},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "last",
|
||||||
|
start: s2.Date.String(),
|
||||||
|
end: "9999-12-31",
|
||||||
|
exp: []string{s2.ID},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "first",
|
||||||
|
start: "1900-01-01",
|
||||||
|
end: s1.Date.String(),
|
||||||
|
exp: []string{s1.ID},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "none",
|
||||||
|
start: "1900-01-01",
|
||||||
|
end: "2025-01-01",
|
||||||
|
exp: make([]string, 0),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actScheds, actErr = mem.Find(item.NewDateFromString(tc.start), item.NewDateFromString(tc.end))
|
||||||
|
if actErr != nil {
|
||||||
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
|
}
|
||||||
|
actIDs := make([]string, 0, len(actScheds))
|
||||||
|
for _, s := range actScheds {
|
||||||
|
actIDs = append(actIDs, s.ID)
|
||||||
|
}
|
||||||
|
sort.Strings(actIDs)
|
||||||
|
if diff := cmp.Diff(tc.exp, actIDs); diff != "" {
|
||||||
|
t.Errorf("(+exp, -got)%s\n", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ func (t *Task) FindMany(params storage.TaskListParams) ([]item.Task, error) {
|
||||||
|
|
||||||
tasks := make([]item.Task, 0, len(t.tasks))
|
tasks := make([]item.Task, 0, len(t.tasks))
|
||||||
for _, tsk := range t.tasks {
|
for _, tsk := range t.tasks {
|
||||||
if storage.Match(tsk, params) {
|
if storage.MatchTask(tsk, params) {
|
||||||
tasks = append(tasks, tsk)
|
tasks = append(tasks, tsk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,4 +44,11 @@ var migrations = []string{
|
||||||
`ALTER TABLE tasks ADD COLUMN project TEXT NOT NULL DEFAULT ''`,
|
`ALTER TABLE tasks ADD COLUMN project TEXT NOT NULL DEFAULT ''`,
|
||||||
`CREATE TABLE syncupdate ("timestamp" TIMESTAMP NOT NULL)`,
|
`CREATE TABLE syncupdate ("timestamp" TIMESTAMP NOT NULL)`,
|
||||||
`INSERT INTO syncupdate (timestamp) VALUES ("0001-01-01T00:00:00Z")`,
|
`INSERT INTO syncupdate (timestamp) VALUES ("0001-01-01T00:00:00Z")`,
|
||||||
|
|
||||||
|
`CREATE TABLE schedules (
|
||||||
|
"id" TEXT UNIQUE NOT NULL DEFAULT '',
|
||||||
|
"title" TEXT NOT NULL DEFAULT '',
|
||||||
|
"date" TEXT NOT NULL DEFAULT '',
|
||||||
|
"recur" TEXT NOT NULL DEFAULT '',
|
||||||
|
"recur_next" TEXT NOT NULL DEFAULT '')`,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SqliteSchedule struct {
|
||||||
|
tx *storage.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SqliteSchedule) Store(sched item.Schedule) error {
|
||||||
|
var recurStr string
|
||||||
|
if sched.Recurrer != nil {
|
||||||
|
recurStr = sched.Recurrer.String()
|
||||||
|
}
|
||||||
|
if _, err := ss.tx.Exec(`
|
||||||
|
INSERT INTO schedules
|
||||||
|
(id, title, date, recur)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?)
|
||||||
|
ON CONFLICT(id) DO UPDATE
|
||||||
|
SET
|
||||||
|
title=?,
|
||||||
|
date=?,
|
||||||
|
recur=?
|
||||||
|
`,
|
||||||
|
sched.ID, sched.Title, sched.Date.String(), recurStr,
|
||||||
|
sched.Title, sched.Date.String(), recurStr); err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SqliteSchedule) Find(start, end item.Date) ([]item.Schedule, error) {
|
||||||
|
rows, err := ss.tx.Query(`SELECT
|
||||||
|
id, title, date, recur
|
||||||
|
FROM schedules
|
||||||
|
WHERE date >= ? AND date <= ?`, start.String(), end.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
scheds := make([]item.Schedule, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var sched item.Schedule
|
||||||
|
var dateStr, recurStr string
|
||||||
|
if err := rows.Scan(&sched.ID, &sched.Title, &dateStr, &recurStr); err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
|
}
|
||||||
|
sched.Date = item.NewDateFromString(dateStr)
|
||||||
|
sched.Recurrer = item.NewRecurrer(recurStr)
|
||||||
|
|
||||||
|
scheds = append(scheds, sched)
|
||||||
|
}
|
||||||
|
|
||||||
|
return scheds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SqliteSchedule) Delete(id string) error {
|
||||||
|
|
||||||
|
result, err := ss.tx.Exec(`
|
||||||
|
DELETE FROM schedules
|
||||||
|
WHERE id = ?`, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsAffected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rowsAffected == 0 {
|
||||||
|
return storage.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -44,6 +44,10 @@ func (sqs *Sqlites) Task(tx *storage.Tx) storage.Task {
|
||||||
return &SqliteTask{tx: tx}
|
return &SqliteTask{tx: tx}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sqs *Sqlites) Schedule(tx *storage.Tx) storage.Schedule {
|
||||||
|
return &SqliteSchedule{tx: tx}
|
||||||
|
}
|
||||||
|
|
||||||
func NewSqlites(dbPath string) (*Sqlites, error) {
|
func NewSqlites(dbPath string) (*Sqlites, error) {
|
||||||
db, err := sql.Open("sqlite", dbPath)
|
db, err := sql.Open("sqlite", dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -49,7 +49,13 @@ type Task interface {
|
||||||
Projects() (map[string]int, error)
|
Projects() (map[string]int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Match(tsk item.Task, params TaskListParams) bool {
|
type Schedule interface {
|
||||||
|
Store(sched item.Schedule) error
|
||||||
|
Find(start, end item.Date) ([]item.Schedule, error)
|
||||||
|
Delete(id string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchTask(tsk item.Task, params TaskListParams) bool {
|
||||||
if params.HasRecurrer && tsk.Recurrer == nil {
|
if params.HasRecurrer && tsk.Recurrer == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,10 +59,10 @@ func TestMatch(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
if !storage.Match(tskMatch, tc.params) {
|
if !storage.MatchTask(tskMatch, tc.params) {
|
||||||
t.Errorf("exp tsk to match")
|
t.Errorf("exp tsk to match")
|
||||||
}
|
}
|
||||||
if storage.Match(tskNotMatch, tc.params) {
|
if storage.MatchTask(tskNotMatch, tc.params) {
|
||||||
t.Errorf("exp tsk to not match")
|
t.Errorf("exp tsk to not match")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue