import Foundation
/// Collects every page of a listing into one array.
public enum Paginator {
/// Cursor listings: `fetch` is called with nil first, then with each page's `next`
/// cursor until it is nil.
public static func cursor<Cursor: Sendable, Page: Sendable, Item: Sendable>(
fetch: (Cursor?) async throws -> Page,
items: (Page) -> [Item],
next: (Page) -> Cursor?
) async throws -> [Item] {
var all: [Item] = []
var cursor: Cursor? = nil
repeat {
let page = try await fetch(cursor)
all.append(contentsOf: items(page))
cursor = next(page)
} while cursor != nil
return all
}
/// Offset listings: walks `limit`/`offset` (defaults 100 and 0) until a page comes
/// back short. A `total` field is deliberately ignored.
public static func offset<Page: Sendable, Item: Sendable>(
limit: Int? = nil, offset: Int? = nil,
fetch: (_ limit: Int, _ offset: Int) async throws -> Page,
items: (Page) -> [Item]
) async throws -> [Item] {
let limit = limit ?? 100
var offset = offset ?? 0
var all: [Item] = []
while true {
let rows = items(try await fetch(limit, offset))
all.append(contentsOf: rows)
if rows.count < limit { return all }
offset += rows.count
}
}
}