683b0ddbf4
Prepare the extracted library for external consumption with grouped godoc, extension point docs, and setup requirements. Wire ErrBadCursorString into decode paths and drop redundant Paginate*Conds aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package cursor
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gitea.auvem.com/go-toolkit/dbx"
|
|
)
|
|
|
|
// PageInfo holds standard pagination information, including cursors for the
|
|
// start and end of the page, and flags indicating whether there are more pages
|
|
// before or after the current page.
|
|
//
|
|
// Modeled after the PageInfo type in the GraphQL Relay specification.
|
|
type PageInfo struct {
|
|
// The cursor string for the last item in the current page.
|
|
EndCursor *string `json:"endCursor,omitempty"`
|
|
// Whether there are more items after the current page.
|
|
HasNextPage bool `json:"hasNextPage"`
|
|
// Whether there are more items before the current page.
|
|
HasPreviousPage bool `json:"hasPreviousPage"`
|
|
// The cursor string for the first item in the current page.
|
|
StartCursor *string `json:"startCursor,omitempty"`
|
|
}
|
|
|
|
// CursorFunc builds a [GenericCursor] for a connection node.
|
|
type CursorFunc[T any] = func(*T) (GenericCursor, error)
|
|
|
|
// Edge represents a single edge in a connection, containing a node of type T
|
|
// and a cursor string for pagination.
|
|
type Edge[T any] struct {
|
|
Node *T
|
|
Cursor string
|
|
}
|
|
|
|
// Connection represents a paginated list of edges, along with page information.
|
|
type Connection[T any] struct {
|
|
Edges []*Edge[T]
|
|
PageInfo *PageInfo
|
|
TotalCount int
|
|
}
|
|
|
|
// BuildEdges constructs a Connection object from a list of items of type T,
|
|
// using the provided Queryable interface for database operations and a
|
|
// count function to determine the total number of items. It also uses a
|
|
// cursor function to generate cursors for each item in the list.
|
|
func BuildEdges[T any](
|
|
sqlo dbx.Queryable,
|
|
countFn QueryCountFn,
|
|
list []*T,
|
|
cursorFunc CursorFunc[T],
|
|
) (
|
|
conn *Connection[T],
|
|
err error,
|
|
) {
|
|
// Create edges for the connection
|
|
edges := make([]*Edge[T], len(list))
|
|
for i, item := range list {
|
|
if item == nil {
|
|
return nil, fmt.Errorf("item at index %d is nil", i)
|
|
}
|
|
|
|
c, err := cursorFunc(item)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get cursor for item at index %d: %w", i, err)
|
|
}
|
|
cstr, err := c.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to encode cursor for item at index %d: %w", i, err)
|
|
}
|
|
|
|
edges[i] = &Edge[T]{
|
|
Node: item,
|
|
Cursor: cstr,
|
|
}
|
|
}
|
|
|
|
// Create page info and connection object
|
|
var pageInfo PageInfo
|
|
var countResult QueryCountResult
|
|
|
|
if len(edges) > 0 {
|
|
// Set start and end cursor strings
|
|
pageInfo.StartCursor = &edges[0].Cursor
|
|
pageInfo.EndCursor = &edges[len(edges)-1].Cursor
|
|
|
|
// Fetch total count. Comparisons use composite tuple logic when the cursor
|
|
// encodes both order and index columns.
|
|
startCursor, err := cursorFunc(edges[0].Node)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get start cursor: %w", err)
|
|
}
|
|
endCursor, err := cursorFunc(edges[len(edges)-1].Node)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get end cursor: %w", err)
|
|
}
|
|
|
|
if countResult, err = countFn(sqlo, startCursor, endCursor); err != nil {
|
|
return nil, fmt.Errorf("failed to query count: %w", err)
|
|
}
|
|
}
|
|
|
|
pageInfo.HasNextPage = countResult.After > 0
|
|
pageInfo.HasPreviousPage = countResult.Before > 0
|
|
|
|
return &Connection[T]{
|
|
Edges: edges,
|
|
PageInfo: &pageInfo,
|
|
TotalCount: countResult.Total,
|
|
}, nil
|
|
}
|
|
|
|
// ExtractNodes extracts the nodes from a Connection object and returns them
|
|
// as a slice of pointers to type T.
|
|
func ExtractNodes[T any](conn *Connection[T]) []*T {
|
|
nodes := make([]*T, len(conn.Edges))
|
|
for i, edge := range conn.Edges {
|
|
nodes[i] = edge.Node
|
|
}
|
|
return nodes
|
|
}
|