1
0

pagination.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package qs
  2. import (
  3. "errors"
  4. "net/http"
  5. "net/url"
  6. "strconv"
  7. )
  8. // Query error.
  9. var (
  10. ErrInvalidLimit = errors.New("invalid limit")
  11. ErrInvalidOffset = errors.New("invalid offset")
  12. ErrInvalidPage = errors.New("invalid page")
  13. )
  14. // Pagination represents a page size and offset for, most likely, a database query.
  15. type Pagination struct {
  16. Limit int `json:"limit"` // Maximum number of results in the page.
  17. Offset int `json:"offset"` // Results offset.
  18. Page int `json:"page,omitempty"` // Page number. This is 0 if the query specifies Offset directly.
  19. }
  20. // ReadPaginationOptions configures the behaviour of ReadPagination.
  21. type ReadPaginationOptions struct {
  22. LimitKey string // Query string key for limit. The default value is "limit"
  23. OffsetKey string // Query string key for offset. The default value is "offset"
  24. PageKey string // Query string key for page. The default value is "page"
  25. MaxLimit int // If this is > 0, the limit is clamped to this maximum value
  26. MinLimit int // The limit is clamped to this minimum value
  27. }
  28. // ReadPagination parses URL values into a Pagination struct.
  29. // This function offers support for both Page and Offset values.
  30. // If both are provided, Offset is always prioritised.
  31. // If only Page is provided, Offset is calculated based on Limit.
  32. func ReadPagination(values url.Values, opt *ReadPaginationOptions) (*Pagination, error) {
  33. opt = initPaginationOptions(opt)
  34. limit := 0
  35. offset := 0
  36. page := 0
  37. var err error = nil
  38. if values.Has(opt.LimitKey) {
  39. limit, err = strconv.Atoi(values.Get(opt.LimitKey))
  40. if err != nil {
  41. return nil, ErrInvalidLimit
  42. }
  43. }
  44. if opt.MaxLimit > 0 && limit > opt.MaxLimit {
  45. limit = opt.MaxLimit
  46. } else if limit < opt.MinLimit {
  47. limit = opt.MinLimit
  48. }
  49. if values.Has(opt.OffsetKey) {
  50. offset, err = strconv.Atoi(values.Get(opt.OffsetKey))
  51. if err != nil {
  52. return nil, ErrInvalidOffset
  53. }
  54. } else if values.Has(opt.PageKey) {
  55. page, err = strconv.Atoi(values.Get(opt.PageKey))
  56. if err != nil {
  57. return nil, ErrInvalidPage
  58. }
  59. offset = (page - 1) * limit
  60. }
  61. pag := &Pagination{
  62. Limit: limit,
  63. Offset: offset,
  64. Page: page,
  65. }
  66. return pag, nil
  67. }
  68. // ReadRequestPagination parses a request's query string into a slice of filters.
  69. // This function always returns a value if it does not encounter an error.
  70. func ReadRequestPagination(req *http.Request, opt *ReadPaginationOptions) (*Pagination, error) {
  71. return ReadPagination(req.URL.Query(), opt)
  72. }
  73. // ReadStringPagination parses a query string literal into a slice of filters.
  74. // This function always returns a value if it does not encounter an error.
  75. func ReadStringPagination(qs string, opt *ReadPaginationOptions) (*Pagination, error) {
  76. values, err := url.ParseQuery(qs)
  77. if err != nil {
  78. return nil, err
  79. }
  80. return ReadPagination(values, opt)
  81. }
  82. func initPaginationOptions(opt *ReadPaginationOptions) *ReadPaginationOptions {
  83. def := &ReadPaginationOptions{
  84. LimitKey: "limit",
  85. OffsetKey: "offset",
  86. PageKey: "page",
  87. }
  88. if opt != nil {
  89. if len(opt.LimitKey) > 0 {
  90. def.LimitKey = opt.LimitKey
  91. }
  92. if len(opt.OffsetKey) > 0 {
  93. def.OffsetKey = opt.OffsetKey
  94. }
  95. if len(opt.PageKey) > 0 {
  96. def.PageKey = opt.PageKey
  97. }
  98. if opt.MaxLimit > def.MaxLimit {
  99. def.MaxLimit = opt.MaxLimit
  100. }
  101. if opt.MinLimit > def.MinLimit {
  102. def.MinLimit = opt.MinLimit
  103. }
  104. }
  105. return def
  106. }