mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-21 10:12:53 +01:00
80 lines
2.6 KiB
Go
80 lines
2.6 KiB
Go
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
|
// released under the MIT license
|
|
|
|
package history
|
|
|
|
import (
|
|
"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
|
|
}
|