74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
// Package pagination defines bounded offset pagination with camelCase fields.
|
|
package pagination
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"strconv"
|
|
)
|
|
|
|
type Config struct {
|
|
DefaultSize int `json:"defaultSize"`
|
|
MaxSize int `json:"maxSize"`
|
|
MaxOffset int64 `json:"maxOffset"`
|
|
}
|
|
type Request struct {
|
|
Page int64 `json:"page"`
|
|
PageSize int `json:"pageSize"`
|
|
}
|
|
|
|
func (r Request) Offset() int64 { return (r.Page - 1) * int64(r.PageSize) }
|
|
func Parse(query url.Values, cfg Config) (Request, error) {
|
|
if cfg.DefaultSize == 0 {
|
|
cfg.DefaultSize = 20
|
|
}
|
|
if cfg.MaxSize == 0 {
|
|
cfg.MaxSize = 100
|
|
}
|
|
if cfg.MaxOffset == 0 {
|
|
cfg.MaxOffset = 1000000
|
|
}
|
|
if cfg.DefaultSize < 1 || cfg.MaxSize < cfg.DefaultSize || cfg.MaxSize > 10000 || cfg.MaxOffset < 0 {
|
|
return Request{}, errors.New("pagination: invalid configuration")
|
|
}
|
|
page, size := int64(1), int64(cfg.DefaultSize)
|
|
for key, target := range map[string]*int64{"page": &page, "pageSize": &size} {
|
|
if v, ok := query[key]; ok {
|
|
if len(v) != 1 {
|
|
return Request{}, errors.New("pagination: duplicate parameter")
|
|
}
|
|
n, err := strconv.ParseInt(v[0], 10, 64)
|
|
if err != nil || n < 1 {
|
|
return Request{}, errors.New("pagination: positive integer required")
|
|
}
|
|
*target = n
|
|
}
|
|
}
|
|
if size > int64(cfg.MaxSize) || (page-1) > cfg.MaxOffset/size {
|
|
return Request{}, errors.New("pagination: size or offset exceeds limit")
|
|
}
|
|
return Request{Page: page, PageSize: int(size)}, nil
|
|
}
|
|
|
|
type Result[T any] struct {
|
|
Items []T `json:"items"`
|
|
Page int64 `json:"page"`
|
|
PageSize int `json:"pageSize"`
|
|
Total int64 `json:"total"`
|
|
TotalPages int64 `json:"totalPages"`
|
|
}
|
|
|
|
func NewResult[T any](r Request, items []T, total int64) (Result[T], error) {
|
|
if r.Page < 1 || r.PageSize < 1 || total < 0 || len(items) > r.PageSize || int64(len(items)) > total {
|
|
return Result[T]{}, errors.New("pagination: invalid result")
|
|
}
|
|
if items == nil {
|
|
items = []T{}
|
|
}
|
|
pages := total / int64(r.PageSize)
|
|
if total%int64(r.PageSize) != 0 {
|
|
pages++
|
|
}
|
|
return Result[T]{Items: items, Page: r.Page, PageSize: r.PageSize, Total: total, TotalPages: pages}, nil
|
|
}
|