-
Notifications
You must be signed in to change notification settings - Fork 460
/
iter.go
133 lines (112 loc) · 3 KB
/
iter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package stripe
import (
"reflect"
"github.com/stripe/stripe-go/form"
)
//
// Public types
//
// Iter provides a convenient interface
// for iterating over the elements
// returned from paginated list API calls.
// Successive calls to the Next method
// will step through each item in the list,
// fetching pages of items as needed.
// Iterators are not thread-safe, so they should not be consumed
// across multiple goroutines.
type Iter struct {
cur interface{}
err error
formValues *form.Values
listParams ListParams
meta ListMeta
query Query
values []interface{}
}
// Current returns the most recent item
// visited by a call to Next.
func (it *Iter) Current() interface{} {
return it.cur
}
// Err returns the error, if any,
// that caused the Iter to stop.
// It must be inspected
// after Next returns false.
func (it *Iter) Err() error {
return it.err
}
// Meta returns the list metadata.
func (it *Iter) Meta() *ListMeta {
return &it.meta
}
// Next advances the Iter to the next item in the list,
// which will then be available
// through the Current method.
// It returns false when the iterator stops
// at the end of the list.
func (it *Iter) Next() bool {
if len(it.values) == 0 && it.meta.HasMore && !it.listParams.Single {
// determine if we're moving forward or backwards in paging
if it.listParams.EndingBefore != nil {
it.listParams.EndingBefore = String(listItemID(it.cur))
it.formValues.Set(EndingBefore, *it.listParams.EndingBefore)
} else {
it.listParams.StartingAfter = String(listItemID(it.cur))
it.formValues.Set(StartingAfter, *it.listParams.StartingAfter)
}
it.getPage()
}
if len(it.values) == 0 {
return false
}
it.cur = it.values[0]
it.values = it.values[1:]
return true
}
func (it *Iter) getPage() {
it.values, it.meta, it.err = it.query(it.listParams.GetParams(), it.formValues)
if it.listParams.EndingBefore != nil {
// We are moving backward,
// but items arrive in forward order.
reverse(it.values)
}
}
// Query is the function used to get a page listing.
type Query func(*Params, *form.Values) ([]interface{}, ListMeta, error)
//
// Public functions
//
// GetIter returns a new Iter for a given query and its options.
func GetIter(container ListParamsContainer, query Query) *Iter {
var listParams *ListParams
formValues := &form.Values{}
if container != nil {
reflectValue := reflect.ValueOf(container)
// See the comment on Call in stripe.go.
if reflectValue.Kind() == reflect.Ptr && !reflectValue.IsNil() {
listParams = container.GetListParams()
form.AppendTo(formValues, container)
}
}
if listParams == nil {
listParams = &ListParams{}
}
iter := &Iter{
formValues: formValues,
listParams: *listParams,
query: query,
}
iter.getPage()
return iter
}
//
// Private functions
//
func listItemID(x interface{}) string {
return reflect.ValueOf(x).Elem().FieldByName("ID").String()
}
func reverse(a []interface{}) {
for i := 0; i < len(a)/2; i++ {
a[i], a[len(a)-i-1] = a[len(a)-i-1], a[i]
}
}