]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/google.golang.org/api/iterator/iterator.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / google.golang.org / api / iterator / iterator.go
1 // Copyright 2016 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Package iterator provides support for standard Google API iterators.
16 // See https://github.com/GoogleCloudPlatform/gcloud-golang/wiki/Iterator-Guidelines.
17 package iterator
18
19 import (
20 "errors"
21 "fmt"
22 "reflect"
23 )
24
25 // Done is returned by an iterator's Next method when the iteration is
26 // complete; when there are no more items to return.
27 var Done = errors.New("no more items in iterator")
28
29 // We don't support mixed calls to Next and NextPage because they play
30 // with the paging state in incompatible ways.
31 var errMixed = errors.New("iterator: Next and NextPage called on same iterator")
32
33 // PageInfo contains information about an iterator's paging state.
34 type PageInfo struct {
35 // Token is the token used to retrieve the next page of items from the
36 // API. You may set Token immediately after creating an iterator to
37 // begin iteration at a particular point. If Token is the empty string,
38 // the iterator will begin with the first eligible item.
39 //
40 // The result of setting Token after the first call to Next is undefined.
41 //
42 // After the underlying API method is called to retrieve a page of items,
43 // Token is set to the next-page token in the response.
44 Token string
45
46 // MaxSize is the maximum number of items returned by a call to the API.
47 // Set MaxSize as a hint to optimize the buffering behavior of the iterator.
48 // If zero, the page size is determined by the underlying service.
49 //
50 // Use Pager to retrieve a page of a specific, exact size.
51 MaxSize int
52
53 // The error state of the iterator. Manipulated by PageInfo.next and Pager.
54 // This is a latch: it starts as nil, and once set should never change.
55 err error
56
57 // If true, no more calls to fetch should be made. Set to true when fetch
58 // returns an empty page token. The iterator is Done when this is true AND
59 // the buffer is empty.
60 atEnd bool
61
62 // Function that fetches a page from the underlying service. It should pass
63 // the pageSize and pageToken arguments to the service, fill the buffer
64 // with the results from the call, and return the next-page token returned
65 // by the service. The function must not remove any existing items from the
66 // buffer. If the underlying RPC takes an int32 page size, pageSize should
67 // be silently truncated.
68 fetch func(pageSize int, pageToken string) (nextPageToken string, err error)
69
70 // Function that returns the number of currently buffered items.
71 bufLen func() int
72
73 // Function that returns the buffer, after setting the buffer variable to nil.
74 takeBuf func() interface{}
75
76 // Set to true on first call to PageInfo.next or Pager.NextPage. Used to check
77 // for calls to both Next and NextPage with the same iterator.
78 nextCalled, nextPageCalled bool
79 }
80
81 // NewPageInfo exposes internals for iterator implementations.
82 // It is not a stable interface.
83 var NewPageInfo = newPageInfo
84
85 // If an iterator can support paging, its iterator-creating method should call
86 // this (via the NewPageInfo variable above).
87 //
88 // The fetch, bufLen and takeBuf arguments provide access to the
89 // iterator's internal slice of buffered items. They behave as described in
90 // PageInfo, above.
91 //
92 // The return value is the PageInfo.next method bound to the returned PageInfo value.
93 // (Returning it avoids exporting PageInfo.next.)
94 func newPageInfo(fetch func(int, string) (string, error), bufLen func() int, takeBuf func() interface{}) (*PageInfo, func() error) {
95 pi := &PageInfo{
96 fetch: fetch,
97 bufLen: bufLen,
98 takeBuf: takeBuf,
99 }
100 return pi, pi.next
101 }
102
103 // Remaining returns the number of items available before the iterator makes another API call.
104 func (pi *PageInfo) Remaining() int { return pi.bufLen() }
105
106 // next provides support for an iterator's Next function. An iterator's Next
107 // should return the error returned by next if non-nil; else it can assume
108 // there is at least one item in its buffer, and it should return that item and
109 // remove it from the buffer.
110 func (pi *PageInfo) next() error {
111 pi.nextCalled = true
112 if pi.err != nil { // Once we get an error, always return it.
113 // TODO(jba): fix so users can retry on transient errors? Probably not worth it.
114 return pi.err
115 }
116 if pi.nextPageCalled {
117 pi.err = errMixed
118 return pi.err
119 }
120 // Loop until we get some items or reach the end.
121 for pi.bufLen() == 0 && !pi.atEnd {
122 if err := pi.fill(pi.MaxSize); err != nil {
123 pi.err = err
124 return pi.err
125 }
126 if pi.Token == "" {
127 pi.atEnd = true
128 }
129 }
130 // Either the buffer is non-empty or pi.atEnd is true (or both).
131 if pi.bufLen() == 0 {
132 // The buffer is empty and pi.atEnd is true, i.e. the service has no
133 // more items.
134 pi.err = Done
135 }
136 return pi.err
137 }
138
139 // Call the service to fill the buffer, using size and pi.Token. Set pi.Token to the
140 // next-page token returned by the call.
141 // If fill returns a non-nil error, the buffer will be empty.
142 func (pi *PageInfo) fill(size int) error {
143 tok, err := pi.fetch(size, pi.Token)
144 if err != nil {
145 pi.takeBuf() // clear the buffer
146 return err
147 }
148 pi.Token = tok
149 return nil
150 }
151
152 // Pageable is implemented by iterators that support paging.
153 type Pageable interface {
154 // PageInfo returns paging information associated with the iterator.
155 PageInfo() *PageInfo
156 }
157
158 // Pager supports retrieving iterator items a page at a time.
159 type Pager struct {
160 pageInfo *PageInfo
161 pageSize int
162 }
163
164 // NewPager returns a pager that uses iter. Calls to its NextPage method will
165 // obtain exactly pageSize items, unless fewer remain. The pageToken argument
166 // indicates where to start the iteration. Pass the empty string to start at
167 // the beginning, or pass a token retrieved from a call to Pager.NextPage.
168 //
169 // If you use an iterator with a Pager, you must not call Next on the iterator.
170 func NewPager(iter Pageable, pageSize int, pageToken string) *Pager {
171 p := &Pager{
172 pageInfo: iter.PageInfo(),
173 pageSize: pageSize,
174 }
175 p.pageInfo.Token = pageToken
176 if pageSize <= 0 {
177 p.pageInfo.err = errors.New("iterator: page size must be positive")
178 }
179 return p
180 }
181
182 // NextPage retrieves a sequence of items from the iterator and appends them
183 // to slicep, which must be a pointer to a slice of the iterator's item type.
184 // Exactly p.pageSize items will be appended, unless fewer remain.
185 //
186 // The first return value is the page token to use for the next page of items.
187 // If empty, there are no more pages. Aside from checking for the end of the
188 // iteration, the returned page token is only needed if the iteration is to be
189 // resumed a later time, in another context (possibly another process).
190 //
191 // The second return value is non-nil if an error occurred. It will never be
192 // the special iterator sentinel value Done. To recognize the end of the
193 // iteration, compare nextPageToken to the empty string.
194 //
195 // It is possible for NextPage to return a single zero-length page along with
196 // an empty page token when there are no more items in the iteration.
197 func (p *Pager) NextPage(slicep interface{}) (nextPageToken string, err error) {
198 p.pageInfo.nextPageCalled = true
199 if p.pageInfo.err != nil {
200 return "", p.pageInfo.err
201 }
202 if p.pageInfo.nextCalled {
203 p.pageInfo.err = errMixed
204 return "", p.pageInfo.err
205 }
206 if p.pageInfo.bufLen() > 0 {
207 return "", errors.New("must call NextPage with an empty buffer")
208 }
209 // The buffer must be empty here, so takeBuf is a no-op. We call it just to get
210 // the buffer's type.
211 wantSliceType := reflect.PtrTo(reflect.ValueOf(p.pageInfo.takeBuf()).Type())
212 if slicep == nil {
213 return "", errors.New("nil passed to Pager.NextPage")
214 }
215 vslicep := reflect.ValueOf(slicep)
216 if vslicep.Type() != wantSliceType {
217 return "", fmt.Errorf("slicep should be of type %s, got %T", wantSliceType, slicep)
218 }
219 for p.pageInfo.bufLen() < p.pageSize {
220 if err := p.pageInfo.fill(p.pageSize - p.pageInfo.bufLen()); err != nil {
221 p.pageInfo.err = err
222 return "", p.pageInfo.err
223 }
224 if p.pageInfo.Token == "" {
225 break
226 }
227 }
228 e := vslicep.Elem()
229 e.Set(reflect.AppendSlice(e, reflect.ValueOf(p.pageInfo.takeBuf())))
230 return p.pageInfo.Token, nil
231 }