You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. // Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package history
  4. import (
  5. "time"
  6. )
  7. // Selector represents a parameter to a CHATHISTORY command;
  8. // at most one of Msgid or Time may be nonzero
  9. type Selector struct {
  10. Msgid string
  11. Time time.Time
  12. }
  13. // Sequence is an abstract sequence of history entries that can be queried;
  14. // it encapsulates restrictions such as registration time cutoffs, or
  15. // only looking at a single "query buffer" (DMs with a particular correspondent)
  16. type Sequence interface {
  17. Between(start, end Selector, limit int) (results []Item, complete bool, err error)
  18. Around(start Selector, limit int) (results []Item, err error)
  19. }
  20. // This is a bad, slow implementation of CHATHISTORY AROUND using the BETWEEN semantics
  21. func GenericAround(seq Sequence, start Selector, limit int) (results []Item, err error) {
  22. var halfLimit int
  23. halfLimit = (limit + 1) / 2
  24. initialResults, _, err := seq.Between(Selector{}, start, halfLimit)
  25. if err != nil {
  26. return
  27. } else if len(initialResults) == 0 {
  28. // TODO: this fails if we're doing an AROUND on the first message in the buffer
  29. // would be nice to fix this but whatever
  30. return
  31. }
  32. newStart := Selector{Time: initialResults[0].Message.Time}
  33. results, _, err = seq.Between(newStart, Selector{}, limit)
  34. return
  35. }
  36. // MinMaxAsc converts CHATHISTORY arguments into time intervals, handling the most
  37. // general case (BETWEEN going forwards or backwards) natively and the other ordering
  38. // queries (AFTER, BEFORE, LATEST) as special cases.
  39. func MinMaxAsc(after, before, cutoff time.Time) (min, max time.Time, ascending bool) {
  40. startIsZero, endIsZero := after.IsZero(), before.IsZero()
  41. if !startIsZero && endIsZero {
  42. // AFTER
  43. ascending = true
  44. } else if startIsZero && !endIsZero {
  45. // BEFORE
  46. ascending = false
  47. } else if !startIsZero && !endIsZero {
  48. if before.Before(after) {
  49. // BETWEEN going backwards
  50. before, after = after, before
  51. ascending = false
  52. } else {
  53. // BETWEEN going forwards
  54. ascending = true
  55. }
  56. } else if startIsZero && endIsZero {
  57. // LATEST
  58. ascending = false
  59. }
  60. if after.IsZero() || after.Before(cutoff) {
  61. // this may result in an impossible query, which is fine
  62. after = cutoff
  63. }
  64. return after, before, ascending
  65. }