blob: 38078f08112ca34d1f0c47a3d57a918641fac70c [file] [log] [blame]
package gophercloud
import "errors"
var (
// ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist.
ErrPageNotAvailable = errors.New("The requested Collection page does not exist.")
)
// Collection must be satisfied by the result type of any resource collection.
// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated.
type Collection interface {
// NextPageURL generates the URL for the page of data that follows this collection.
// Return "" if no such page exists.
NextPageURL() string
// Concat creates a new Collection that contains all of the elements from this page and another page.
// It's used to aggregate results for the AllPages method.
Concat(Collection) Collection
}
// Pager knows how to advance through a specific resource collection, one page at a time.
type Pager struct {
initialURL string
advance func(string) (Collection, error)
}
// NewPager constructs a manually-configured pager.
// Supply the URL for the first page and a function that requests a specific page given a URL.
func NewPager(initialURL string, advance func(string) (Collection, error)) Pager {
return Pager{
initialURL: initialURL,
advance: advance,
}
}
// NewSinglePager constructs a Pager that "iterates" over a single-paged Collection.
// Supply a function that returns the only page.
func NewSinglePager(only func() (Collection, error)) Pager {
consumed := false
single := func(_ string) (Collection, error) {
if !consumed {
consumed = true
return only()
}
return nil, ErrPageNotAvailable
}
return Pager{
initialURL: "",
advance: single,
}
}
// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
// Return "false" from the handler to prematurely stop iterating.
func (p Pager) EachPage(handler func(Collection) bool) error {
currentURL := p.initialURL
for {
currentPage, err := p.advance(currentURL)
if err != nil {
return err
}
if !handler(currentPage) {
return nil
}
currentURL = currentPage.NextPageURL()
if currentURL == "" {
return nil
}
}
}
// AllPages accumulates every page reachable from a Pager into a single Collection, for convenience.
func (p Pager) AllPages() (Collection, error) {
var megaPage Collection
err := p.EachPage(func(page Collection) bool {
if megaPage == nil {
megaPage = page
} else {
megaPage = megaPage.Concat(page)
}
return true
})
return megaPage, err
}
// PaginationLinks stores the `next` and `previous` links that are provided by some (but not all) paginated resources.
type PaginationLinks struct {
// Next is the full URL to the next page of results, or nil if this is the last page.
Next *string `json:"next,omitempty"`
// Previous is the full URL to the previous page of results, or nil if this is the first page.
Previous *string `json:"previous,omitempty"`
}