Monorepo for Tangled
tangled.org
1package db
2
3import (
4 "database/sql"
5 "fmt"
6)
7
8const (
9 ListDefaultLimit = 50
10 ListMaxLimit = 1000
11)
12
13type ListPage struct {
14 Limit int
15 Cursor *int
16 Desc bool
17}
18
19func (p ListPage) limit() int {
20 switch {
21 case p.Limit <= 0:
22 return ListDefaultLimit
23 case p.Limit > ListMaxLimit:
24 return ListMaxLimit
25 default:
26 return p.Limit
27 }
28}
29
30func (p ListPage) clause() (string, []any) {
31 dir, cmp := "asc", ">"
32 if p.Desc {
33 dir, cmp = "desc", "<"
34 }
35 if p.Cursor != nil {
36 return fmt.Sprintf("where id %s ? order by id %s limit ?", cmp, dir), []any{*p.Cursor, p.limit() + 1}
37 }
38 return fmt.Sprintf("order by id %s limit ?", dir), []any{p.limit() + 1}
39}
40
41func listPaged[T any](
42 q DBTX,
43 query string,
44 args []any,
45 p ListPage,
46 scan func(*sql.Rows) (T, error),
47 idOf func(T) int,
48) ([]T, *int, error) {
49 clause, pArgs := p.clause()
50 rows, err := q.Query("select * from ("+query+") "+clause, append(args, pArgs...)...)
51 if err != nil {
52 return nil, nil, err
53 }
54 defer rows.Close()
55
56 out := []T{}
57 for rows.Next() {
58 v, err := scan(rows)
59 if err != nil {
60 return nil, nil, err
61 }
62 out = append(out, v)
63 }
64 if err := rows.Err(); err != nil {
65 return nil, nil, err
66 }
67
68 if limit := p.limit(); len(out) > limit {
69 out = out[:limit]
70 last := idOf(out[len(out)-1])
71 return out, &last, nil
72 }
73 return out, nil, nil
74}