blob: c95d744c29f7e6e61c4bc6e88da452657277391f [file] [log] [blame]
Ash Wilsonc8e68872014-09-16 10:36:56 -04001package pagination
2
3import "errors"
4
5var (
6 // ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist.
7 ErrPageNotAvailable = errors.New("The requested page does not exist.")
8)
9
10// Page must be satisfied by the result type of any resource collection.
11// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated.
12// Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs,
13// instead.
14// Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type
15// will need to implement.
16type Page interface {
17
18 // NextPageURL generates the URL for the page of data that follows this collection.
19 // Return "" if no such page exists.
20 NextPageURL() (string, error)
21
22 // IsEmpty returns true if this Page has no items in it.
23 IsEmpty() (bool, error)
24}
25
26// Pager knows how to advance through a specific resource collection, one page at a time.
27type Pager struct {
28 initialURL string
29
30 fetchNextPage func(string) (Page, error)
31}
32
33// NewPager constructs a manually-configured pager.
34// Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page.
35func NewPager(initialURL string, fetchNextPage func(string) (Page, error)) Pager {
36 return Pager{
37 initialURL: initialURL,
38 fetchNextPage: fetchNextPage,
39 }
40}
41
42// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
43// Return "false" from the handler to prematurely stop iterating.
44func (p Pager) EachPage(handler func(Page) (bool, error)) error {
45 currentURL := p.initialURL
46 for {
47 currentPage, err := p.fetchNextPage(currentURL)
48 if err != nil {
49 return err
50 }
51
52 empty, err := currentPage.IsEmpty()
53 if err != nil {
54 return err
55 }
56 if empty {
57 return nil
58 }
59
60 ok, err := handler(currentPage)
61 if err != nil {
62 return err
63 }
64 if !ok {
65 return nil
66 }
67
68 currentURL, err = currentPage.NextPageURL()
69 if err != nil {
70 return err
71 }
72 if currentURL == "" {
73 return nil
74 }
75 }
76}