github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/pagination/pagepagination/pagination.go (about) 1 package pagepagination 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/url" 7 "strconv" 8 "strings" 9 ) 10 11 type PagePaginator struct { 12 MaxItems int 13 DefaultItems int 14 } 15 16 func (p *PagePaginator) defaults() { 17 if p.MaxItems == 0 { 18 p.MaxItems = 1000 19 } 20 21 if p.DefaultItems == 0 { 22 p.DefaultItems = 250 23 } 24 } 25 26 // ParsePagination parses limit and page from *http.Request with given limits and defaults. 27 func (p *PagePaginator) ParsePagination(r *http.Request) (page, itemsPerPage int) { 28 p.defaults() 29 30 if offsetParam := r.URL.Query().Get("page"); offsetParam == "" { 31 page = 0 32 } else { 33 if offset, err := strconv.ParseInt(offsetParam, 10, 0); err != nil { 34 page = 0 35 } else { 36 page = int(offset) 37 } 38 } 39 40 if limitParam := r.URL.Query().Get("per_page"); limitParam == "" { 41 itemsPerPage = p.DefaultItems 42 } else { 43 if limit, err := strconv.ParseInt(limitParam, 10, 0); err != nil { 44 itemsPerPage = p.DefaultItems 45 } else { 46 itemsPerPage = int(limit) 47 } 48 } 49 50 if itemsPerPage > p.MaxItems { 51 itemsPerPage = p.MaxItems 52 } 53 54 if itemsPerPage < 1 { 55 itemsPerPage = 1 56 } 57 58 if page < 0 { 59 page = 0 60 } 61 62 return 63 } 64 65 func header(u *url.URL, rel string, limit, page int64) string { 66 q := u.Query() 67 q.Set("per_page", fmt.Sprintf("%d", limit)) 68 q.Set("page", fmt.Sprintf("%d", page/limit)) 69 u.RawQuery = q.Encode() 70 return fmt.Sprintf("<%s>; rel=\"%s\"", u.String(), rel) 71 } 72 73 func PaginationHeader(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int) { 74 if itemsPerPage <= 0 { 75 itemsPerPage = 1 76 } 77 78 itemsPerPage64 := int64(itemsPerPage) 79 offset := int64(page) * itemsPerPage64 80 81 // lastOffset will either equal the offset required to contain the remainder, 82 // or the limit. 83 var lastOffset int64 84 if total%itemsPerPage64 == 0 { 85 lastOffset = total - itemsPerPage64 86 } else { 87 lastOffset = (total / itemsPerPage64) * itemsPerPage64 88 } 89 90 w.Header().Set("X-Total-Count", strconv.FormatInt(total, 10)) 91 92 // Check for last page 93 if offset >= lastOffset { 94 if total == 0 { 95 w.Header().Set("Link", strings.Join([]string{ 96 header(u, "first", itemsPerPage64, 0), 97 header(u, "next", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64), 98 header(u, "prev", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64), 99 }, ",")) 100 return 101 } 102 103 if total < itemsPerPage64 { 104 w.Header().Set("link", header(u, "first", total, 0)) 105 return 106 } 107 108 w.Header().Set("Link", strings.Join([]string{ 109 header(u, "first", itemsPerPage64, 0), 110 header(u, "prev", itemsPerPage64, lastOffset-itemsPerPage64), 111 }, ",")) 112 return 113 } 114 115 if offset < itemsPerPage64 { 116 w.Header().Set("Link", strings.Join([]string{ 117 header(u, "next", itemsPerPage64, itemsPerPage64), 118 header(u, "last", itemsPerPage64, lastOffset), 119 }, ",")) 120 return 121 } 122 123 w.Header().Set("Link", strings.Join([]string{ 124 header(u, "first", itemsPerPage64, 0), 125 header(u, "next", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64), 126 header(u, "prev", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64), 127 header(u, "last", itemsPerPage64, lastOffset), 128 }, ",")) 129 }