import files, add README & LICENSE
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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 is a function that takes a pointer to an object type T, the
|
||||
// underlying database view object, and returns a GenericCursor or an error.
|
||||
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 (see meeting pagination).
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user