From 03252488cf54db84341ed30d8bb488cb9847eff0 Mon Sep 17 00:00:00 2001 From: Erik Winter Date: Mon, 6 Jun 2022 11:17:49 +0200 Subject: [PATCH] every n days recurrer --- internal/task/recur.go | 71 +++++++++++++++++++++++++++++-------- internal/task/recur_test.go | 47 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 14 deletions(-) diff --git a/internal/task/recur.go b/internal/task/recur.go index c5a2447..2407dbd 100644 --- a/internal/task/recur.go +++ b/internal/task/recur.go @@ -25,20 +25,13 @@ func NewRecurrer(recurStr string) Recurrer { terms = terms[1:] - if recur, ok := ParseDaily(start, terms); ok { - return recur - } - if recur, ok := ParseWeekly(start, terms); ok { - return recur - } - if recur, ok := ParseBiweekly(start, terms); ok { - return recur - } - if recur, ok := ParseEveryNWeeks(start, terms); ok { - return recur - } - if recur, ok := ParseEveryNMonths(start, terms); ok { - return recur + for _, parseFunc := range []func(Date, []string) (Recurrer, bool){ + ParseDaily, ParseEveryNDays, ParseWeekly, ParseBiweekly, + ParseEveryNWeeks, ParseEveryNMonths, + } { + if recur, ok := parseFunc(start, terms); ok { + return recur + } } return nil @@ -48,6 +41,7 @@ type Daily struct { Start Date } +// yyyy-mm-dd, daily func ParseDaily(start Date, terms []string) (Recurrer, bool) { if len(terms) < 1 { return nil, false @@ -70,6 +64,55 @@ func (d Daily) String() string { return fmt.Sprintf("%s, daily", d.Start.String()) } +type EveryNDays struct { + Start Date + N int +} + +// yyyy-mm-dd, every 3 days +func ParseEveryNDays(start Date, terms []string) (Recurrer, bool) { + if len(terms) != 1 { + return EveryNDays{}, false + } + + terms = strings.Split(terms[0], " ") + if len(terms) != 3 || terms[0] != "every" || terms[2] != "days" { + return EveryNDays{}, false + } + + n, err := strconv.Atoi(terms[1]) + if err != nil { + return EveryNDays{}, false + } + + return EveryNDays{ + Start: start, + N: n, + }, true +} + +func (nd EveryNDays) RecursOn(date Date) bool { + if nd.Start.After(date) { + return false + } + + testDate := nd.Start + for { + switch { + case testDate.Equal(date): + return true + case testDate.After(date): + return false + default: + testDate = testDate.Add(nd.N) + } + } +} + +func (nd EveryNDays) String() string { + return fmt.Sprintf("%s, every %d days", nd.Start.String(), nd.N) +} + type Weekly struct { Start Date Weekdays Weekdays diff --git a/internal/task/recur_test.go b/internal/task/recur_test.go index 7e905e6..d183b8c 100644 --- a/internal/task/recur_test.go +++ b/internal/task/recur_test.go @@ -50,6 +50,53 @@ func TestDaily(t *testing.T) { }) } +func TestEveryNDays(t *testing.T) { + every := task.EveryNDays{ + Start: task.NewDate(2022, 6, 8), + N: 5, + } + everyStr := "2022-06-08 (wednesday), every 5 days" + + t.Run("parse", func(t *testing.T) { + test.Equals(t, every, task.NewRecurrer(everyStr)) + }) + + t.Run("string", func(t *testing.T) { + test.Equals(t, everyStr, every.String()) + }) + + t.Run("recurs on", func(t *testing.T) { + for _, tc := range []struct { + name string + date task.Date + exp bool + }{ + { + name: "before", + date: task.NewDate(2022, 1, 1), + }, + { + name: "start", + date: every.Start, + exp: true, + }, + { + name: "after true", + date: every.Start.Add(15), + exp: true, + }, + { + name: "after false", + date: every.Start.Add(16), + }, + } { + t.Run(tc.name, func(t *testing.T) { + test.Equals(t, tc.exp, every.RecursOn(tc.date)) + }) + } + }) +} + func TestParseWeekly(t *testing.T) { start := task.NewDate(2021, 2, 7) for _, tc := range []struct {