Compare commits
No commits in common. "7de4408926538d9a09e4a4aefd19833830fe5681" and "7360744e34f540acaec057390222505d065f9a2d" have entirely different histories.
7de4408926
...
7360744e34
|
@ -66,6 +66,7 @@ func (d *Date) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDate(year, month, day int) Date {
|
func NewDate(year, month, day int) Date {
|
||||||
|
|
||||||
if year == 0 || month == 0 || month > 12 || day == 0 {
|
if year == 0 || month == 0 || month > 12 || day == 0 {
|
||||||
return Date{}
|
return Date{}
|
||||||
}
|
}
|
||||||
|
|
169
item/event.go
169
item/event.go
|
@ -1,110 +1,111 @@
|
||||||
package item
|
package item
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EventBody struct {
|
type EventBody struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Date Date `json:"date"`
|
Start time.Time `json:"start"`
|
||||||
Time Time `json:"time"`
|
|
||||||
Duration time.Duration `json:"duration"`
|
Duration time.Duration `json:"duration"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (e EventBody) MarshalJSON() ([]byte, error) {
|
func (e EventBody) MarshalJSON() ([]byte, error) {
|
||||||
// type Alias EventBody
|
type Alias EventBody
|
||||||
// return json.Marshal(&struct {
|
return json.Marshal(&struct {
|
||||||
// Start string `json:"start"`
|
Start string `json:"start"`
|
||||||
// Duration string `json:"duration"`
|
Duration string `json:"duration"`
|
||||||
// *Alias
|
*Alias
|
||||||
// }{
|
}{
|
||||||
// Start: e.Start.UTC().Format(time.RFC3339),
|
Start: e.Start.UTC().Format(time.RFC3339),
|
||||||
// Duration: e.Duration.String(),
|
Duration: e.Duration.String(),
|
||||||
// Alias: (*Alias)(&e),
|
Alias: (*Alias)(&e),
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func (e *EventBody) UnmarshalJSON(data []byte) error {
|
func (e *EventBody) UnmarshalJSON(data []byte) error {
|
||||||
// type Alias EventBody
|
type Alias EventBody
|
||||||
// aux := &struct {
|
aux := &struct {
|
||||||
// Start string `json:"start"`
|
Start string `json:"start"`
|
||||||
// Duration string `json:"duration"`
|
Duration string `json:"duration"`
|
||||||
// *Alias
|
*Alias
|
||||||
// }{
|
}{
|
||||||
// Alias: (*Alias)(e),
|
Alias: (*Alias)(e),
|
||||||
// }
|
}
|
||||||
// if err := json.Unmarshal(data, &aux); err != nil {
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// var err error
|
var err error
|
||||||
// if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
|
if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return nil
|
return nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// type Event struct {
|
type Event struct {
|
||||||
// ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
// Recurrer Recurrer `json:"recurrer"`
|
Recurrer Recurrer `json:"recurrer"`
|
||||||
// RecurNext time.Time `json:"recurNext"`
|
RecurNext time.Time `json:"recurNext"`
|
||||||
// EventBody
|
EventBody
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func NewEvent(i Item) (Event, error) {
|
func NewEvent(i Item) (Event, error) {
|
||||||
// if i.Kind != KindEvent {
|
if i.Kind != KindEvent {
|
||||||
// return Event{}, fmt.Errorf("item is not an event")
|
return Event{}, fmt.Errorf("item is not an event")
|
||||||
// }
|
}
|
||||||
|
|
||||||
// var e Event
|
var e Event
|
||||||
// if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
|
if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
|
||||||
// return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
|
return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// e.ID = i.ID
|
e.ID = i.ID
|
||||||
// e.Recurrer = i.Recurrer
|
e.Recurrer = i.Recurrer
|
||||||
// e.RecurNext = i.RecurNext
|
e.RecurNext = i.RecurNext
|
||||||
|
|
||||||
// return e, nil
|
return e, nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func (e Event) Item() (Item, error) {
|
func (e Event) Item() (Item, error) {
|
||||||
// body, err := json.Marshal(EventBody{
|
body, err := json.Marshal(EventBody{
|
||||||
// Title: e.Title,
|
Title: e.Title,
|
||||||
// Start: e.Start,
|
Start: e.Start,
|
||||||
// Duration: e.Duration,
|
Duration: e.Duration,
|
||||||
// })
|
})
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return Item{}, fmt.Errorf("could not marshal event to json")
|
return Item{}, fmt.Errorf("could not marshal event to json")
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return Item{
|
return Item{
|
||||||
// ID: e.ID,
|
ID: e.ID,
|
||||||
// Kind: KindEvent,
|
Kind: KindEvent,
|
||||||
// Recurrer: e.Recurrer,
|
Recurrer: e.Recurrer,
|
||||||
// RecurNext: e.RecurNext,
|
RecurNext: e.RecurNext,
|
||||||
// Body: string(body),
|
Body: string(body),
|
||||||
// }, nil
|
}, nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func (e Event) Valid() bool {
|
func (e Event) Valid() bool {
|
||||||
// if e.Title == "" {
|
if e.Title == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e.Start.IsZero() || e.Start.Year() < 2024 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e.Duration.Seconds() < 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// if e.Recurrer != nil && !e.Recurrer.Valid() {
|
||||||
// return false
|
// return false
|
||||||
// }
|
// }
|
||||||
// if e.Start.IsZero() || e.Start.Year() < 2024 {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if e.Duration.Seconds() < 1 {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// // if e.Recurrer != nil && !e.Recurrer.Valid() {
|
|
||||||
// // return false
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// return true
|
return true
|
||||||
// }
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ type Item struct {
|
||||||
Updated time.Time `json:"updated"`
|
Updated time.Time `json:"updated"`
|
||||||
Deleted bool `json:"deleted"`
|
Deleted bool `json:"deleted"`
|
||||||
Recurrer Recurrer `json:"recurrer"`
|
Recurrer Recurrer `json:"recurrer"`
|
||||||
RecurNext Date `json:"recurNext"`
|
RecurNext time.Time `json:"recurNext"`
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
70
item/time.go
70
item/time.go
|
@ -1,70 +0,0 @@
|
||||||
package item
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
TimeFormat = "15:04"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Time struct {
|
|
||||||
t time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Time) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(t.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
|
||||||
timeString := ""
|
|
||||||
if err := json.Unmarshal(data, &timeString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nt := NewTimeFromString(timeString)
|
|
||||||
t.t = nt.Time()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTime(hour, minute int) Time {
|
|
||||||
return Time{
|
|
||||||
t: time.Date(0, 0, 0, hour, minute, 0, 0, time.UTC),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTimeFromString(timeStr string) Time {
|
|
||||||
tm, err := time.Parse(TimeFormat, timeStr)
|
|
||||||
if err != nil {
|
|
||||||
return Time{t: time.Time{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Time{t: tm}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) String() string {
|
|
||||||
return t.t.Format(TimeFormat)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) Time() time.Time {
|
|
||||||
return t.t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) IsZero() bool {
|
|
||||||
return t.t.IsZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) Hour() int {
|
|
||||||
return t.t.Hour()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) Minute() int {
|
|
||||||
return t.t.Minute()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Time) Add(d time.Duration) Time {
|
|
||||||
return Time{
|
|
||||||
t: t.t.Add(d),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package item_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/item"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTime(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
h, m := 11, 18
|
|
||||||
tm := item.NewTime(h, m)
|
|
||||||
expStr := "11:18"
|
|
||||||
if expStr != tm.String() {
|
|
||||||
t.Errorf("exp %v, got %v", expStr, tm.String())
|
|
||||||
}
|
|
||||||
actJSON, err := json.Marshal(tm)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
expJSON := fmt.Sprintf("%q", expStr)
|
|
||||||
if expJSON != string(actJSON) {
|
|
||||||
t.Errorf("exp %v, got %v", expJSON, string(actJSON))
|
|
||||||
}
|
|
||||||
var actTM item.Time
|
|
||||||
if err := json.Unmarshal(actJSON, &actTM); err != nil {
|
|
||||||
t.Errorf("exp nil, got %v", err)
|
|
||||||
}
|
|
||||||
if expStr != actTM.String() {
|
|
||||||
t.Errorf("ecp %v, got %v", expStr, actTM.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimeFromString(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
str string
|
|
||||||
exp string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
exp: "00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid",
|
|
||||||
str: "invalid",
|
|
||||||
exp: "00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid",
|
|
||||||
str: "11:42",
|
|
||||||
exp: "11:42",
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
act := item.NewTimeFromString(tc.str)
|
|
||||||
if tc.exp != act.String() {
|
|
||||||
t.Errorf("exp %v, got %v", tc.exp, act.String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,66 +2,70 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNotFound = errors.New("not found")
|
ErrNotFound = errors.New("not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// type LocalID interface {
|
type LocalID interface {
|
||||||
// FindAll() (map[string]int, error)
|
FindAll() (map[string]int, error)
|
||||||
// FindOrNext(id string) (int, error)
|
FindOrNext(id string) (int, error)
|
||||||
// Next() (int, error)
|
Next() (int, error)
|
||||||
// Store(id string, localID int) error
|
Store(id string, localID int) error
|
||||||
// Delete(id string) error
|
Delete(id string) error
|
||||||
// }
|
}
|
||||||
|
|
||||||
// type Sync interface {
|
type Sync interface {
|
||||||
// FindAll() ([]item.Item, error)
|
FindAll() ([]item.Item, error)
|
||||||
// Store(i item.Item) error
|
Store(i item.Item) error
|
||||||
// DeleteAll() error
|
DeleteAll() error
|
||||||
// LastUpdate() (time.Time, error)
|
LastUpdate() (time.Time, error)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// type Event interface {
|
type Event interface {
|
||||||
// Store(event item.Event) error
|
Store(event item.Event) error
|
||||||
// Find(id string) (item.Event, error)
|
Find(id string) (item.Event, error)
|
||||||
// FindAll() ([]item.Event, error)
|
FindAll() ([]item.Event, error)
|
||||||
// Delete(id string) error
|
Delete(id string) error
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func NextLocalID(used []int) int {
|
func NextLocalID(used []int) int {
|
||||||
// if len(used) == 0 {
|
if len(used) == 0 {
|
||||||
// return 1
|
return 1
|
||||||
// }
|
}
|
||||||
|
|
||||||
// sort.Ints(used)
|
sort.Ints(used)
|
||||||
// usedMax := 1
|
usedMax := 1
|
||||||
// for _, u := range used {
|
for _, u := range used {
|
||||||
// if u > usedMax {
|
if u > usedMax {
|
||||||
// usedMax = u
|
usedMax = u
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// var limit int
|
var limit int
|
||||||
// for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
|
for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
|
||||||
// }
|
}
|
||||||
|
|
||||||
// newId := used[len(used)-1] + 1
|
newId := used[len(used)-1] + 1
|
||||||
// if newId < limit {
|
if newId < limit {
|
||||||
// return newId
|
return newId
|
||||||
// }
|
}
|
||||||
|
|
||||||
// usedMap := map[int]bool{}
|
usedMap := map[int]bool{}
|
||||||
// for _, u := range used {
|
for _, u := range used {
|
||||||
// usedMap[u] = true
|
usedMap[u] = true
|
||||||
// }
|
}
|
||||||
|
|
||||||
// for i := 1; i < limit; i++ {
|
for i := 1; i < limit; i++ {
|
||||||
// if _, ok := usedMap[i]; !ok {
|
if _, ok := usedMap[i]; !ok {
|
||||||
// return i
|
return i
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return limit
|
return limit
|
||||||
// }
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -47,33 +48,32 @@ func (m *Memory) Updated(kinds []item.Kind, timestamp time.Time) ([]item.Item, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) RecursBefore(date time.Time) ([]item.Item, error) {
|
func (m *Memory) RecursBefore(date time.Time) ([]item.Item, error) {
|
||||||
// res := make([]item.Item, 0)
|
res := make([]item.Item, 0)
|
||||||
// for _, i := range m.items {
|
for _, i := range m.items {
|
||||||
// if i.Recurrer == nil {
|
if i.Recurrer == nil {
|
||||||
// continue
|
continue
|
||||||
// }
|
}
|
||||||
// if i.RecurNext.Before(date) {
|
if i.RecurNext.Before(date) {
|
||||||
// res = append(res, i)
|
res = append(res, i)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return res, nil
|
return res, nil
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) RecursNext(id string, date time.Time, ts time.Time) error {
|
func (m *Memory) RecursNext(id string, date time.Time, ts time.Time) error {
|
||||||
// i, ok := m.items[id]
|
i, ok := m.items[id]
|
||||||
// if !ok {
|
if !ok {
|
||||||
// return ErrNotFound
|
return ErrNotFound
|
||||||
// }
|
}
|
||||||
// if i.Recurrer == nil {
|
if i.Recurrer == nil {
|
||||||
// return ErrNotARecurrer
|
return ErrNotARecurrer
|
||||||
// }
|
}
|
||||||
// if !i.Recurrer.On(date) {
|
if !i.Recurrer.On(date) {
|
||||||
// return fmt.Errorf("item does not recur on %v", date)
|
return fmt.Errorf("item does not recur on %v", date)
|
||||||
// }
|
}
|
||||||
// i.RecurNext = date
|
i.RecurNext = date
|
||||||
// i.Updated = ts
|
i.Updated = ts
|
||||||
// m.items[id] = i
|
m.items[id] = i
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue