blob: 38078f08112ca34d1f0c47a3d57a918641fac70c [file] [log] [blame]
Ash Wilson64d67b22014-09-05 13:04:12 -04001package gophercloud
2
Ash Wilsone30b76b2014-09-12 08:36:17 -04003import "errors"
Ash Wilson64d67b22014-09-05 13:04:12 -04004
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 Collection page does not exist.")
8)
9
Ash Wilsone30b76b2014-09-12 08:36:17 -040010// Collection 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.
Ash Wilson64d67b22014-09-05 13:04:12 -040012type Collection interface {
13
Ash Wilsone30b76b2014-09-12 08:36:17 -040014 // NextPageURL generates the URL for the page of data that follows this collection.
15 // Return "" if no such page exists.
16 NextPageURL() string
Ash Wilsonb110fc92014-09-08 13:54:59 -040017
Ash Wilsone30b76b2014-09-12 08:36:17 -040018 // Concat creates a new Collection that contains all of the elements from this page and another page.
19 // It's used to aggregate results for the AllPages method.
Ash Wilsonb110fc92014-09-08 13:54:59 -040020 Concat(Collection) Collection
Ash Wilson64d67b22014-09-05 13:04:12 -040021}
22
Ash Wilsone30b76b2014-09-12 08:36:17 -040023// Pager knows how to advance through a specific resource collection, one page at a time.
24type Pager struct {
25 initialURL string
Ash Wilson64d67b22014-09-05 13:04:12 -040026
Ash Wilsone30b76b2014-09-12 08:36:17 -040027 advance func(string) (Collection, error)
28}
29
30// NewPager constructs a manually-configured pager.
31// Supply the URL for the first page and a function that requests a specific page given a URL.
32func NewPager(initialURL string, advance func(string) (Collection, error)) Pager {
33 return Pager{
34 initialURL: initialURL,
35 advance: advance,
36 }
37}
38
39// NewSinglePager constructs a Pager that "iterates" over a single-paged Collection.
40// Supply a function that returns the only page.
41func NewSinglePager(only func() (Collection, error)) Pager {
42 consumed := false
43 single := func(_ string) (Collection, error) {
44 if !consumed {
45 consumed = true
46 return only()
47 }
48 return nil, ErrPageNotAvailable
49 }
50
51 return Pager{
52 initialURL: "",
53 advance: single,
54 }
55}
56
57// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
58// Return "false" from the handler to prematurely stop iterating.
59func (p Pager) EachPage(handler func(Collection) bool) error {
60 currentURL := p.initialURL
Ash Wilson64d67b22014-09-05 13:04:12 -040061 for {
Ash Wilsone30b76b2014-09-12 08:36:17 -040062 currentPage, err := p.advance(currentURL)
Ash Wilson64d67b22014-09-05 13:04:12 -040063 if err != nil {
64 return err
65 }
Ash Wilsone30b76b2014-09-12 08:36:17 -040066
67 if !handler(currentPage) {
68 return nil
69 }
70
71 currentURL = currentPage.NextPageURL()
72 if currentURL == "" {
73 return nil
74 }
Ash Wilson64d67b22014-09-05 13:04:12 -040075 }
76}
77
Ash Wilsone30b76b2014-09-12 08:36:17 -040078// AllPages accumulates every page reachable from a Pager into a single Collection, for convenience.
79func (p Pager) AllPages() (Collection, error) {
80 var megaPage Collection
Ash Wilsonb110fc92014-09-08 13:54:59 -040081
Ash Wilsone30b76b2014-09-12 08:36:17 -040082 err := p.EachPage(func(page Collection) bool {
83 if megaPage == nil {
84 megaPage = page
Ash Wilsonb110fc92014-09-08 13:54:59 -040085 } else {
86 megaPage = megaPage.Concat(page)
87 }
88 return true
89 })
90
91 return megaPage, err
Ash Wilson64d67b22014-09-05 13:04:12 -040092}
93
Ash Wilson64d67b22014-09-05 13:04:12 -040094// PaginationLinks stores the `next` and `previous` links that are provided by some (but not all) paginated resources.
95type PaginationLinks struct {
96
97 // Next is the full URL to the next page of results, or nil if this is the last page.
98 Next *string `json:"next,omitempty"`
99
100 // Previous is the full URL to the previous page of results, or nil if this is the first page.
101 Previous *string `json:"previous,omitempty"`
102}