feat: add independent platform modules and multi-provider storage
This commit is contained in:
73
pagination/pagination.go
Normal file
73
pagination/pagination.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user