This commit is contained in:
Erik Winter 2024-12-22 11:43:37 +01:00
parent 6972f08123
commit 7de4408926
6 changed files with 295 additions and 164 deletions

View File

@ -66,7 +66,6 @@ func (d *Date) UnmarshalJSON(data []byte) error {
}
func NewDate(year, month, day int) Date {
if year == 0 || month == 0 || month > 12 || day == 0 {
return Date{}
}

View File

@ -1,111 +1,110 @@
package item
import (
"encoding/json"
"fmt"
"time"
)
type EventBody struct {
Title string `json:"title"`
Start time.Time `json:"start"`
Date Date `json:"date"`
Time Time `json:"time"`
Duration time.Duration `json:"duration"`
}
func (e EventBody) MarshalJSON() ([]byte, error) {
type Alias EventBody
return json.Marshal(&struct {
Start string `json:"start"`
Duration string `json:"duration"`
*Alias
}{
Start: e.Start.UTC().Format(time.RFC3339),
Duration: e.Duration.String(),
Alias: (*Alias)(&e),
})
}
// func (e EventBody) MarshalJSON() ([]byte, error) {
// type Alias EventBody
// return json.Marshal(&struct {
// Start string `json:"start"`
// Duration string `json:"duration"`
// *Alias
// }{
// Start: e.Start.UTC().Format(time.RFC3339),
// Duration: e.Duration.String(),
// Alias: (*Alias)(&e),
// })
// }
func (e *EventBody) UnmarshalJSON(data []byte) error {
type Alias EventBody
aux := &struct {
Start string `json:"start"`
Duration string `json:"duration"`
*Alias
}{
Alias: (*Alias)(e),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
// func (e *EventBody) UnmarshalJSON(data []byte) error {
// type Alias EventBody
// aux := &struct {
// Start string `json:"start"`
// Duration string `json:"duration"`
// *Alias
// }{
// Alias: (*Alias)(e),
// }
// if err := json.Unmarshal(data, &aux); err != nil {
// return err
// }
var err error
if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
return err
}
// var err error
// if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
// return err
// }
if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
return err
}
// if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
// return err
// }
return nil
}
// return nil
// }
type Event struct {
ID string `json:"id"`
Recurrer Recurrer `json:"recurrer"`
RecurNext time.Time `json:"recurNext"`
EventBody
}
// type Event struct {
// ID string `json:"id"`
// Recurrer Recurrer `json:"recurrer"`
// RecurNext time.Time `json:"recurNext"`
// EventBody
// }
func NewEvent(i Item) (Event, error) {
if i.Kind != KindEvent {
return Event{}, fmt.Errorf("item is not an event")
}
// func NewEvent(i Item) (Event, error) {
// if i.Kind != KindEvent {
// return Event{}, fmt.Errorf("item is not an event")
// }
var e Event
if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
}
// var e Event
// if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
// return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
// }
e.ID = i.ID
e.Recurrer = i.Recurrer
e.RecurNext = i.RecurNext
// e.ID = i.ID
// e.Recurrer = i.Recurrer
// e.RecurNext = i.RecurNext
return e, nil
}
// return e, nil
// }
func (e Event) Item() (Item, error) {
body, err := json.Marshal(EventBody{
Title: e.Title,
Start: e.Start,
Duration: e.Duration,
})
if err != nil {
return Item{}, fmt.Errorf("could not marshal event to json")
}
// func (e Event) Item() (Item, error) {
// body, err := json.Marshal(EventBody{
// Title: e.Title,
// Start: e.Start,
// Duration: e.Duration,
// })
// if err != nil {
// return Item{}, fmt.Errorf("could not marshal event to json")
// }
return Item{
ID: e.ID,
Kind: KindEvent,
Recurrer: e.Recurrer,
RecurNext: e.RecurNext,
Body: string(body),
}, nil
}
// return Item{
// ID: e.ID,
// Kind: KindEvent,
// Recurrer: e.Recurrer,
// RecurNext: e.RecurNext,
// Body: string(body),
// }, nil
// }
func (e Event) Valid() bool {
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
// }
// func (e Event) Valid() bool {
// 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 true
}
// return true
// }

70
item/time.go Normal file
View File

@ -0,0 +1,70 @@
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),
}
}

67
item/time_test.go Normal file
View File

@ -0,0 +1,67 @@
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())
}
})
}
}

View File

@ -2,70 +2,66 @@ package storage
import (
"errors"
"sort"
"time"
"go-mod.ewintr.nl/planner/item"
)
var (
ErrNotFound = errors.New("not found")
)
type LocalID interface {
FindAll() (map[string]int, error)
FindOrNext(id string) (int, error)
Next() (int, error)
Store(id string, localID int) error
Delete(id string) error
}
// type LocalID interface {
// FindAll() (map[string]int, error)
// FindOrNext(id string) (int, error)
// Next() (int, error)
// Store(id string, localID int) error
// Delete(id string) error
// }
type Sync interface {
FindAll() ([]item.Item, error)
Store(i item.Item) error
DeleteAll() error
LastUpdate() (time.Time, error)
}
// type Sync interface {
// FindAll() ([]item.Item, error)
// Store(i item.Item) error
// DeleteAll() error
// LastUpdate() (time.Time, error)
// }
type Event interface {
Store(event item.Event) error
Find(id string) (item.Event, error)
FindAll() ([]item.Event, error)
Delete(id string) error
}
// type Event interface {
// Store(event item.Event) error
// Find(id string) (item.Event, error)
// FindAll() ([]item.Event, error)
// Delete(id string) error
// }
func NextLocalID(used []int) int {
if len(used) == 0 {
return 1
}
// func NextLocalID(used []int) int {
// if len(used) == 0 {
// return 1
// }
sort.Ints(used)
usedMax := 1
for _, u := range used {
if u > usedMax {
usedMax = u
}
}
// sort.Ints(used)
// usedMax := 1
// for _, u := range used {
// if u > usedMax {
// usedMax = u
// }
// }
var limit int
for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
}
// var limit int
// for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
// }
newId := used[len(used)-1] + 1
if newId < limit {
return newId
}
// newId := used[len(used)-1] + 1
// if newId < limit {
// return newId
// }
usedMap := map[int]bool{}
for _, u := range used {
usedMap[u] = true
}
// usedMap := map[int]bool{}
// for _, u := range used {
// usedMap[u] = true
// }
for i := 1; i < limit; i++ {
if _, ok := usedMap[i]; !ok {
return i
}
}
// for i := 1; i < limit; i++ {
// if _, ok := usedMap[i]; !ok {
// return i
// }
// }
return limit
}
// return limit
// }

View File

@ -1,7 +1,6 @@
package main
import (
"fmt"
"slices"
"sync"
"time"
@ -48,32 +47,33 @@ func (m *Memory) Updated(kinds []item.Kind, timestamp time.Time) ([]item.Item, e
}
func (m *Memory) RecursBefore(date time.Time) ([]item.Item, error) {
res := make([]item.Item, 0)
for _, i := range m.items {
if i.Recurrer == nil {
continue
}
if i.RecurNext.Before(date) {
res = append(res, i)
}
}
return res, nil
// res := make([]item.Item, 0)
// for _, i := range m.items {
// if i.Recurrer == nil {
// continue
// }
// if i.RecurNext.Before(date) {
// res = append(res, i)
// }
// }
// return res, nil
return nil, nil
}
func (m *Memory) RecursNext(id string, date time.Time, ts time.Time) error {
i, ok := m.items[id]
if !ok {
return ErrNotFound
}
if i.Recurrer == nil {
return ErrNotARecurrer
}
if !i.Recurrer.On(date) {
return fmt.Errorf("item does not recur on %v", date)
}
i.RecurNext = date
i.Updated = ts
m.items[id] = i
// i, ok := m.items[id]
// if !ok {
// return ErrNotFound
// }
// if i.Recurrer == nil {
// return ErrNotARecurrer
// }
// if !i.Recurrer.On(date) {
// return fmt.Errorf("item does not recur on %v", date)
// }
// i.RecurNext = date
// i.Updated = ts
// m.items[id] = i
return nil
}