diff --git a/Makefile b/Makefile index d98d161..1588f92 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ test: go test ./... -deploy: +deploy: test go build -o gte-process-inbox ./cmd/process-inbox/main.go go build -o gte-generate-recurring ./cmd/generate-recurring/main.go scp gte-* zerocontent.org:bin/ diff --git a/internal/task/recur.go b/internal/task/recur.go index e20172b..c0e4ff4 100644 --- a/internal/task/recur.go +++ b/internal/task/recur.go @@ -2,6 +2,7 @@ package task import ( "fmt" + "strconv" "strings" "time" ) @@ -33,6 +34,9 @@ func NewRecurrer(recurStr string) Recurrer { if recur, ok := ParseBiweekly(start, terms); ok { return recur } + if recur, ok := ParseEveryNWeeks(start, terms); ok { + return recur + } return nil } @@ -179,3 +183,45 @@ func (b Biweekly) RecursOn(date Date) bool { func (b Biweekly) String() string { return fmt.Sprintf("%s, biweekly, %s", b.Start.String(), strings.ToLower(b.Weekday.String())) } + +type EveryNWeeks struct { + Start Date + N int +} + +// yyyy-mm-dd, every 3 weeks +func ParseEveryNWeeks(start Date, terms []string) (Recurrer, bool) { + if len(terms) < 1 || len(terms) > 1 { + return nil, false + } + + terms = strings.Split(terms[0], " ") + if len(terms) != 3 || terms[0] != "every" || terms[2] != "weeks" { + return nil, false + } + n, err := strconv.Atoi(terms[1]) + if err != nil || n < 1 { + return nil, false + } + + return EveryNWeeks{ + Start: start, + N: n, + }, true +} + +func (enw EveryNWeeks) RecursOn(date Date) bool { + if enw.Start.After(date) { + return false + } + if enw.Start.Equal(date) { + return true + } + + intervalDays := enw.N * 7 + return enw.Start.DaysBetween(date)%intervalDays == 0 +} + +func (enw EveryNWeeks) String() string { + return fmt.Sprintf("%s, every %d weeks", enw.Start.String(), enw.N) +} diff --git a/internal/task/recur_test.go b/internal/task/recur_test.go index 8e53d89..bb95f91 100644 --- a/internal/task/recur_test.go +++ b/internal/task/recur_test.go @@ -240,3 +240,63 @@ func TestBiweekly(t *testing.T) { } }) } + +func TestEveryNWeeks(t *testing.T) { + everyNWeeks := task.EveryNWeeks{ + Start: task.NewDate(2021, 2, 3), + N: 3, + } + everyNWeeksStr := "2021-02-03 (wednesday), every 3 weeks" + + t.Run("parse", func(t *testing.T) { + test.Equals(t, everyNWeeks, task.NewRecurrer(everyNWeeksStr)) + }) + + t.Run("string", func(t *testing.T) { + test.Equals(t, everyNWeeksStr, everyNWeeks.String()) + }) + + t.Run("recurs on", func(t *testing.T) { + for _, tc := range []struct { + name string + date task.Date + exp bool + }{ + { + name: "before start", + date: task.NewDate(2021, 1, 27), + }, + { + name: "on start", + date: task.NewDate(2021, 2, 3), + exp: true, + }, + { + name: "wrong day", + date: task.NewDate(2021, 2, 4), + }, + { + name: "one week after", + date: task.NewDate(2021, 2, 10), + }, + { + name: "first interval", + date: task.NewDate(2021, 2, 24), + exp: true, + }, + { + name: "second interval", + date: task.NewDate(2021, 3, 17), + exp: true, + }, + { + name: "second interval plus one week", + date: task.NewDate(2021, 3, 24), + }, + } { + t.Run(tc.name, func(t *testing.T) { + test.Equals(t, tc.exp, everyNWeeks.RecursOn(tc.date)) + }) + } + }) +}