carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package applog
2
3import (
4 "encoding/base64"
5 "encoding/json"
6 "time"
7)
8
9// Cursor names a position in the merged timeline — the last record a reader
10// has seen, by exactly the (TS, Box, Seq) triple MergeRecords orders by.
11// Opaque base64 on the wire so its shape is the server's to evolve.
12type Cursor struct {
13 TS time.Time `json:"ts"`
14 Box string `json:"box"`
15 Seq uint64 `json:"seq"`
16}
17
18// Before reports whether c sits strictly before r in merge order — the
19// follow test: records with c.Before(r) are new since the cursor.
20func (c Cursor) Before(r Record) bool {
21 if !c.TS.Equal(r.TS) {
22 return c.TS.Before(r.TS)
23 }
24 if c.Box != r.Box {
25 return c.Box < r.Box
26 }
27 return c.Seq < r.Seq
28}
29
30// EncodeCursor renders the position just after r.
31func EncodeCursor(r Record) string {
32 b, _ := json.Marshal(Cursor{TS: r.TS, Box: r.Box, Seq: r.Seq}) // cannot fail on this shape
33 return base64.RawURLEncoding.EncodeToString(b)
34}
35
36// DecodeCursor parses what EncodeCursor produced; ok=false for anything
37// else (including future shapes this build cannot read — the caller treats
38// that as "no cursor", which re-tails rather than erroring a follow).
39func DecodeCursor(s string) (Cursor, bool) {
40 b, err := base64.RawURLEncoding.DecodeString(s)
41 if err != nil {
42 return Cursor{}, false
43 }
44 var c Cursor
45 if json.Unmarshal(b, &c) != nil || c.TS.IsZero() {
46 return Cursor{}, false
47 }
48 return c, true
49}
50