diff options
author | Robin Jarry <robin@jarry.cc> | 2023-10-17 14:40:08 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-10-28 19:24:55 +0200 |
commit | 57088312fdd8e602a084bd5736a0e22a34be9ec0 (patch) | |
tree | 8c5544262cf8c1772ec661748cfa4d5491ff4c77 /worker/lib/daterange.go | |
parent | 591659b52867cb118d1f82d41693a02123935e0c (diff) | |
download | aerc-57088312fdd8e602a084bd5736a0e22a34be9ec0.tar.gz |
worker: move shared code to lib
Avoid importing code from worker/lib into lib. It should only be the
other way around. Move the message parsing code used by maildir,
notmuch, mbox and the eml viewer into a lib/rfc822 package.
Adapt imports accordingly.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Moritz Poldrack <moritz@poldrack.dev>
Tested-by: Inwit <inwit@sindominio.net>
Diffstat (limited to 'worker/lib/daterange.go')
-rw-r--r-- | worker/lib/daterange.go | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/worker/lib/daterange.go b/worker/lib/daterange.go deleted file mode 100644 index b08bf177..00000000 --- a/worker/lib/daterange.go +++ /dev/null @@ -1,471 +0,0 @@ -package lib - -import ( - "fmt" - "strings" - "time" - - "git.sr.ht/~rjarry/aerc/log" -) - -const dateFmt = "2006-01-02" - -// ParseDateRange parses a date range into a start and end date. Dates are -// expected to be in the YYYY-MM-DD format. -// -// Start and end dates are connected by the range operator ".." where end date -// is not included in the date range. -// -// ParseDateRange can also parse open-ended ranges, i.e. start.. or ..end are -// allowed. -// -// Relative date terms (such as "1 week 1 day" or "1w 1d") can be used, too. -func ParseDateRange(s string) (start, end time.Time, err error) { - s = cleanInput(s) - s = ensureRangeOp(s) - i := strings.Index(s, "..") - switch { - case i < 0: - // single date - start, err = translate(s) - if err != nil { - err = fmt.Errorf("failed to parse date: %w", err) - return - } - end = start.AddDate(0, 0, 1) - - case i == 0: - // end date only - if len(s) < 2 { - err = fmt.Errorf("no date found") - return - } - end, err = translate(s[2:]) - if err != nil { - err = fmt.Errorf("failed to parse date: %w", err) - return - } - - case i > 0: - // start date first - start, err = translate(s[:i]) - if err != nil { - err = fmt.Errorf("failed to parse date: %w", err) - return - } - if len(s[i:]) <= 2 { - return - } - // and end dates if available - end, err = translate(s[(i + 2):]) - if err != nil { - err = fmt.Errorf("failed to parse date: %w", err) - return - } - } - - return -} - -type dictFunc = func(bool) time.Time - -// dict is a dictionary to translate words to dates. Map key must be at least 3 -// characters for matching purposes. -var dict map[string]dictFunc = map[string]dictFunc{ - "today": func(_ bool) time.Time { - return time.Now() - }, - "yesterday": func(_ bool) time.Time { - return time.Now().AddDate(0, 0, -1) - }, - "week": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Monday)+diff) - }, - "month": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(0, diff, -t.Day()+1) - }, - "year": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, 0, -t.YearDay()+1) - }, - "monday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Monday)+diff) - }, - "tuesday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Tuesday)+diff) - }, - "wednesday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Wednesday)+diff) - }, - "thursday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Thursday)+diff) - }, - "friday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Friday)+diff) - }, - "saturday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Saturday)+diff) - }, - "sunday": func(this bool) time.Time { - diff := 0 - if !this { - diff = -7 - } - return time.Now().AddDate(0, 0, - daydiff(time.Sunday)+diff) - }, - "january": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.January), -t.Day()+1) - }, - "february": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.February), -t.Day()+1) - }, - "march": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.March), -t.Day()+1) - }, - "april": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.April), -t.Day()+1) - }, - "may": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.May), -t.Day()+1) - }, - "june": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.June), -t.Day()+1) - }, - "july": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.July), -t.Day()+1) - }, - "august": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.August), -t.Day()+1) - }, - "september": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.September), -t.Day()+1) - }, - "october": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.October), -t.Day()+1) - }, - "november": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.November), -t.Day()+1) - }, - "december": func(this bool) time.Time { - diff := 0 - if !this { - diff = -1 - } - t := time.Now() - return t.AddDate(diff, - monthdiff(time.December), -t.Day()+1) - }, -} - -func daydiff(d time.Weekday) int { - daydiff := d - time.Now().Weekday() - if daydiff > 0 { - return int(daydiff) - 7 - } - return int(daydiff) -} - -func monthdiff(d time.Month) int { - monthdiff := d - time.Now().Month() - if monthdiff > 0 { - return int(monthdiff) - 12 - } - return int(monthdiff) -} - -// translate translates regular time words into date strings -func translate(s string) (time.Time, error) { - if s == "" { - return time.Now(), fmt.Errorf("empty string") - } - log.Tracef("input: %s", s) - s0 := s - - // if next characters is integer, then parse a relative date - if '0' <= s[0] && s[0] <= '9' && hasUnit(s) { - relDate, err := ParseRelativeDate(s) - if err != nil { - log.Errorf("could not parse relative date from '%s': %v", - s0, err) - } else { - log.Tracef("relative date: translated to %v from %s", - relDate, s0) - return bod(relDate.Apply(time.Now())), nil - } - } - - // consult dictionary for terms translation - s, this, hasPrefix := handlePrefix(s) - for term, dateFn := range dict { - if term == "month" && !hasPrefix { - continue - } - if strings.Contains(term, s) { - log.Tracef("dictionary: translated to %s from %s", - term, s0) - return bod(dateFn(this)), nil - } - } - - // this is a regular date, parse it in the normal format - log.Infof("parse: translates %s to regular format", s0) - return time.Parse(dateFmt, s) -} - -// bod returns the begin of the day -func bod(t time.Time) time.Time { - y, m, d := t.Date() - return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) -} - -func handlePrefix(s string) (string, bool, bool) { - var hasPrefix bool - this := true - if strings.HasPrefix(s, "this") { - hasPrefix = true - s = strings.TrimPrefix(s, "this") - } - if strings.HasPrefix(s, "last") { - hasPrefix = true - this = false - s = strings.TrimPrefix(s, "last") - } - return s, this, hasPrefix -} - -func cleanInput(s string) string { - s = strings.ToLower(s) - s = strings.ReplaceAll(s, " ", "") - s = strings.ReplaceAll(s, "_", "") - return s -} - -// RelDate is the relative date in the past, e.g. yesterday would be -// represented as RelDate{0,0,1}. -type RelDate struct { - Year uint - Month uint - Day uint -} - -func (d RelDate) Apply(t time.Time) time.Time { - return t.AddDate(-int(d.Year), -int(d.Month), -int(d.Day)) -} - -// ParseRelativeDate parses a string of relative terms into a DateAdd. -// -// Syntax: N (year|month|week|day) .. -// -// The following are valid inputs: -// 5weeks1day -// 5w1d -// -// Adapted from the Go stdlib in src/time/format.go -func ParseRelativeDate(s string) (RelDate, error) { - s0 := s - s = cleanInput(s) - var da RelDate - for s != "" { - var n uint - - var err error - - // expect an integer - if !('0' <= s[0] && s[0] <= '9') { - return da, fmt.Errorf("not a valid relative term: %s", - s0) - } - - // consume integer - n, s, err = leadingInt(s) - if err != nil { - return da, fmt.Errorf("cannot read integer in %s", - s0) - } - - // consume the units - i := 0 - for ; i < len(s); i++ { - c := s[i] - if '0' <= c && c <= '9' { - break - } - } - if i == 0 { - return da, fmt.Errorf("missing unit in %s", s0) - } - - u := s[:i] - s = s[i:] - switch u[0] { - case 'y': - da.Year += n - case 'm': - da.Month += n - case 'w': - da.Day += 7 * n - case 'd': - da.Day += n - default: - return da, fmt.Errorf("unknown unit %s in %s", u, s0) - } - - } - - return da, nil -} - -func hasUnit(s string) (has bool) { - for _, u := range "ymwd" { - if strings.Contains(s, string(u)) { - return true - } - } - return false -} - -// leadingInt parses and returns the leading integer in s. -// -// Adapted from the Go stdlib in src/time/format.go -func leadingInt(s string) (x uint, rem string, err error) { - i := 0 - for ; i < len(s); i++ { - c := s[i] - if c < '0' || c > '9' { - break - } - x = x*10 + uint(c) - '0' - } - return x, s[i:], nil -} - -func ensureRangeOp(s string) string { - if strings.Contains(s, "..") { - return s - } - s0 := s - for _, m := range []string{"this", "last"} { - for _, u := range []string{"year", "month", "week"} { - term := m + u - if strings.Contains(s, term) { - if m == "last" { - return s0 + "..this" + u - } else { - return s0 + ".." - } - } - } - } - return s0 -} |