|  | package gophercloud | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  |  | 
|  | "github.com/racker/perigee" | 
|  | ) | 
|  |  | 
|  | 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 describes the minimum functionality that any collection resource must implement to be able to use | 
|  | // the global paging and iteration functions. | 
|  | // Every resource that returns a list of multiple results must implement this functionality, whether or not it is paged. | 
|  | // In addition to the methods provided here, each collection should also provide an AsItem(Page) method that | 
|  | // casts the Page to its more specific type and returns the Page's contents as a slice. | 
|  | type Collection interface { | 
|  |  | 
|  | // Pager returns one of the concrete Pager implementations from this package, or a custom one. | 
|  | // The style of Pager returned determines how the collection is paged. | 
|  | Pager() Pager | 
|  |  | 
|  | // Concat the contents of another collection on to the end of this one. | 
|  | // Return a new collection that contains elements from both. | 
|  | Concat(Collection) Collection | 
|  | } | 
|  |  | 
|  | // EachPage iterates through a Collection one page at a time. | 
|  | // The handler function will be invoked with a Collection containing each page. | 
|  | // If the handler returns true, iteration will continue. If it returns false, no more pages will be fetched. | 
|  | func EachPage(first Collection, handler func(Collection) bool) error { | 
|  | p := first.Pager() | 
|  | var err error | 
|  | current := first | 
|  |  | 
|  | for { | 
|  | if !handler(current) { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | if !p.HasNextPage() { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | current, err = p.NextPage() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // AllPages consolidates all pages reachable from a provided starting point into a single mega-Page. | 
|  | // Use this only when you know that the full set will always fit within memory. | 
|  | func AllPages(first Collection) (Collection, error) { | 
|  | megaPage := first | 
|  | isFirst := true | 
|  |  | 
|  | err := EachPage(first, func(page Collection) bool { | 
|  | if isFirst { | 
|  | isFirst = false | 
|  | } else { | 
|  | megaPage = megaPage.Concat(page) | 
|  | } | 
|  | return true | 
|  | }) | 
|  |  | 
|  | return megaPage, err | 
|  | } | 
|  |  | 
|  | // Pager describes a specific paging idiom for a Collection resource. | 
|  | // Generally, to use a Pager, the Collection must also implement a more specialized interface than Collection. | 
|  | // Clients should not generally interact with Pagers directly. | 
|  | // Instead, use the more convenient collection traversal methods: AllPages and EachPage. | 
|  | type Pager interface { | 
|  |  | 
|  | // HasNextPage returns true if a call to NextPage will return an additional Page of results. | 
|  | HasNextPage() bool | 
|  |  | 
|  | // NextPage returns the next Page in the sequence. | 
|  | // Panics if no page is available, so always check HasNextPage first. | 
|  | NextPage() (Collection, error) | 
|  | } | 
|  |  | 
|  | // SinglePager is used by collections that are not actually paged. | 
|  | // It has no additional interface requirements for its host Page. | 
|  | type SinglePager struct{} | 
|  |  | 
|  | // HasNextPage always reports false. | 
|  | func (p SinglePager) HasNextPage() bool { | 
|  | return false | 
|  | } | 
|  |  | 
|  | // NextPage always returns an ErrPageNotAvailable. | 
|  | func (p SinglePager) NextPage() (Collection, error) { | 
|  | return nil, ErrPageNotAvailable | 
|  | } | 
|  |  | 
|  | // 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"` | 
|  | } | 
|  |  | 
|  | // LinkCollection must be satisfied by a Page that uses a LinkPager. | 
|  | type LinkCollection interface { | 
|  | Collection | 
|  |  | 
|  | // Service returns the client used to make further requests. | 
|  | Service() *ServiceClient | 
|  |  | 
|  | // Links returns the pagination links from a single page. | 
|  | Links() PaginationLinks | 
|  |  | 
|  | // Interpret an arbitrary JSON result as a new LinkCollection. | 
|  | Interpret(interface{}) (LinkCollection, error) | 
|  | } | 
|  |  | 
|  | // LinkPager implements paging for collections that provide a link structure in their response JSON. | 
|  | // It follows explicit `next` links and stops when the `next` link is "null". | 
|  | type LinkPager struct { | 
|  | current LinkCollection | 
|  | } | 
|  |  | 
|  | // NewLinkPager creates and initializes a pager for a LinkCollection. | 
|  | func NewLinkPager(first LinkCollection) *LinkPager { | 
|  | return &LinkPager{current: first} | 
|  | } | 
|  |  | 
|  | // HasNextPage checks the `next` link in the pagination data. | 
|  | func (p *LinkPager) HasNextPage() bool { | 
|  | return p.current.Links().Next != nil | 
|  | } | 
|  |  | 
|  | // NextPage follows the `next` link to construct the next page of data. | 
|  | func (p *LinkPager) NextPage() (Collection, error) { | 
|  | url := p.current.Links().Next | 
|  | if url == nil { | 
|  | return nil, ErrPageNotAvailable | 
|  | } | 
|  |  | 
|  | var response interface{} | 
|  | _, err := perigee.Request("GET", *url, perigee.Options{ | 
|  | MoreHeaders: p.current.Service().Provider.AuthenticatedHeaders(), | 
|  | Results:     &response, | 
|  | OkCodes:     []int{200}, | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | interpreted, err := p.current.Interpret(response) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | p.current = interpreted | 
|  | return interpreted, nil | 
|  | } |