jsonpage.gno
1.56 Kb · 72 lines
1package jsonpage
2
3import (
4 "strconv"
5
6 "gno.land/p/nt/bptree/v0"
7 "gno.land/p/nt/mux/v0"
8 "gno.land/p/onbloc/json"
9)
10
11type JSONRenderer interface {
12 RenderJSON() *json.Node
13}
14
15const (
16 maxLimit = 100
17 defaultLimit = 20
18)
19
20// Render returns a paginated JSON output.
21// CONTRACT: provide non-nil renderer func or values from the bptree.BPTree must
22// implement JSONRenderer.
23func Render(t bptree.ITree, r *mux.Request, renderer func(string, any) *json.Node) *json.Node {
24 var (
25 page = 1
26 limit = defaultLimit
27 )
28 p, err := strconv.Atoi(r.Query.Get("page"))
29 if err == nil && p > 0 {
30 page = p
31 }
32 l, err := strconv.Atoi(r.Query.Get("limit"))
33 if err == nil && l > 0 {
34 limit = l
35 }
36 return render(t, page, limit, renderer, false)
37}
38
39func render(t bptree.ITree, page, limit int, renderer func(string, any) *json.Node, reverse bool) *json.Node {
40 total := t.Size()
41 if limit == 0 || limit > total {
42 limit = total
43 }
44 if limit > maxLimit {
45 limit = maxLimit
46 }
47 var (
48 offset = (page - 1) * limit
49 items []*json.Node
50 iterate = t.IterateByOffset
51 )
52 if reverse {
53 iterate = t.ReverseIterateByOffset
54 }
55 iterate(offset, limit, func(k string, v any) bool {
56 if renderer != nil {
57 items = append(items, renderer(k, v))
58 } else {
59 items = append(items, v.(JSONRenderer).RenderJSON())
60 }
61 return false
62 })
63 totalPages := 1
64 if limit > 0 {
65 totalPages = (total + limit - 1) / limit
66 }
67 return json.ObjectNode("", map[string]*json.Node{
68 "items": json.ArrayNode("", items),
69 "page": json.NumberNode("", float64(page)),
70 "total": json.NumberNode("", float64(totalPages)),
71 })
72}