feat: initial version
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
package pagination
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func Validate(first *int, after *string, last *int, before *string) error {
|
||||
if first != nil && last != nil {
|
||||
return fmt.Errorf("only one of first and last can be provided")
|
||||
}
|
||||
if first != nil && *first < 0 {
|
||||
return fmt.Errorf("first must be greater than 0")
|
||||
}
|
||||
if last != nil && *last < 0 {
|
||||
return fmt.Errorf("last must be greater than 0")
|
||||
}
|
||||
if after != nil && len(*after) > 0 && before != nil && len(*before) > 0 {
|
||||
return fmt.Errorf("only one of after and before can be provided")
|
||||
}
|
||||
if ValidateCursor(after) != nil {
|
||||
return fmt.Errorf("after is not a valid cursor")
|
||||
}
|
||||
if ValidateCursor(before) != nil {
|
||||
return fmt.Errorf("before is not a valid cursor")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateCursor(cursor *string) error {
|
||||
_, err := DecodeCursor(cursor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeCursor(cursor *string) (string, error) {
|
||||
if cursor == nil {
|
||||
return "", nil
|
||||
}
|
||||
b64, err := base64.StdEncoding.DecodeString(*cursor)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b64), nil
|
||||
}
|
||||
|
||||
func EncodeCursor(cursor string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(cursor))
|
||||
}
|
||||
|
||||
func GetPage[T any](items []T, first *int, after *string, last *int, before *string, max int, fn func(T) string) ([]T, PageInfo) {
|
||||
if items == nil {
|
||||
return nil, PageInfo{}
|
||||
}
|
||||
tmp := min(max, len(items))
|
||||
sIx := 0
|
||||
eIx := sIx + tmp
|
||||
if first != nil {
|
||||
tmp = *first
|
||||
eIx = sIx + tmp
|
||||
} else if last != nil {
|
||||
tmp = *last
|
||||
sIx = len(items) - tmp
|
||||
eIx = len(items)
|
||||
}
|
||||
if cursor, err := DecodeCursor(after); err == nil && cursor != "" {
|
||||
idx := slices.IndexFunc(items, func(item T) bool {
|
||||
return fn(item) == cursor
|
||||
})
|
||||
idx = idx + 1
|
||||
if idx+tmp >= len(items) {
|
||||
tmp = len(items) - idx
|
||||
}
|
||||
sIx = idx
|
||||
eIx = idx + tmp
|
||||
} else if cursor, err := DecodeCursor(before); err == nil && cursor != "" {
|
||||
idx := slices.IndexFunc(items, func(item T) bool {
|
||||
return fn(item) == cursor
|
||||
})
|
||||
f := idx - tmp
|
||||
if f < 0 {
|
||||
f = 0
|
||||
}
|
||||
sIx = f
|
||||
eIx = idx
|
||||
}
|
||||
page := items[sIx:eIx]
|
||||
return page, PageInfo{
|
||||
StartCursor: ptr(EncodeCursor(fn(page[0]))),
|
||||
HasNextPage: eIx < len(items),
|
||||
HasPreviousPage: sIx > 0,
|
||||
EndCursor: ptr(EncodeCursor(fn(page[len(page)-1]))),
|
||||
}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
StartCursor *string
|
||||
HasNextPage bool
|
||||
HasPreviousPage bool
|
||||
EndCursor *string
|
||||
}
|
||||
Reference in New Issue
Block a user