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