123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- // Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
- // released under the MIT license
-
- package history
-
- import (
- "strings"
- "time"
- )
-
- // Selector represents a parameter to a CHATHISTORY command
- type Selector struct {
- Msgid string
- Time time.Time
- }
-
- // Sequence is an abstract sequence of history entries that can be queried;
- // it encapsulates restrictions such as registration time cutoffs, or
- // only looking at a single "query buffer" (DMs with a particular correspondent)
- type Sequence interface {
- Between(start, end Selector, limit int) (results []Item, err error)
- Around(start Selector, limit int) (results []Item, err error)
-
- ListCorrespondents(start, end Selector, limit int) (results []TargetListing, err error)
-
- // this are weird hacks that violate the encapsulation of Sequence to some extent;
- // Cutoff() returns the cutoff time for other code to use (it returns the zero time
- // if none is set), and Ephemeral() returns whether the backing store is in-memory
- // or a persistent database.
- Cutoff() time.Time
- Ephemeral() bool
- }
-
- // This is a bad, slow implementation of CHATHISTORY AROUND using the BETWEEN semantics
- func GenericAround(seq Sequence, start Selector, limit int) (results []Item, err error) {
- var halfLimit int
- halfLimit = (limit + 1) / 2
- initialResults, err := seq.Between(Selector{}, start, halfLimit)
- if err != nil {
- return
- } else if len(initialResults) == 0 {
- // TODO: this fails if we're doing an AROUND on the first message in the buffer
- // would be nice to fix this but whatever
- return
- }
- newStart := Selector{Time: initialResults[0].Message.Time}
- results, err = seq.Between(newStart, Selector{}, limit)
- return
- }
-
- // MinMaxAsc converts CHATHISTORY arguments into time intervals, handling the most
- // general case (BETWEEN going forwards or backwards) natively and the other ordering
- // queries (AFTER, BEFORE, LATEST) as special cases.
- func MinMaxAsc(after, before, cutoff time.Time) (min, max time.Time, ascending bool) {
- startIsZero, endIsZero := after.IsZero(), before.IsZero()
- if !startIsZero && endIsZero {
- // AFTER
- ascending = true
- } else if startIsZero && !endIsZero {
- // BEFORE
- ascending = false
- } else if !startIsZero && !endIsZero {
- if before.Before(after) {
- // BETWEEN going backwards
- before, after = after, before
- ascending = false
- } else {
- // BETWEEN going forwards
- ascending = true
- }
- } else if startIsZero && endIsZero {
- // LATEST
- ascending = false
- }
- if after.IsZero() || after.Before(cutoff) {
- // this may result in an impossible query, which is fine
- after = cutoff
- }
- return after, before, ascending
- }
-
- // maps regular msgids from JOIN, etc. to a msgid suitable for attaching
- // to a HistServ message describing the JOIN. See #491 for some history.
- func HistservMungeMsgid(msgid string) string {
- return "_" + msgid
- }
-
- // strips munging from a msgid. future schemes may not support a well-defined
- // mapping of munged msgids to true msgids, but munged msgids should always contain
- // a _, with metadata in front and data (possibly the true msgid) after.
- func NormalizeMsgid(msgid string) string {
- return strings.TrimPrefix(msgid, "_")
- }
|