forked from sgt-kabukiman/srapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuser_collection.go
226 lines (183 loc) · 4.95 KB
/
user_collection.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// This file has been generated by `make gen`
// Do not edit this file, edit generate/collection.got and re-run make.
package srapi
// UserCollection is list of User structs. It possible represents
// a slice of the entire dataset and has links to navigate through the pages.
type UserCollection struct {
Data []User
Pagination Pagination
limit int
}
// UserWalkerFunc is a function that can be used in Walk(). If it returns
// true, walking continues, else the walk stops.
type UserWalkerFunc func(u *User) bool
// Limit returns a copy of the collection that is limited to a maximum amount
// of items in it. This is useful because the Cursor type does *not* affect
// how many items are in a collection, but only how many are fetched per
// request.
func (c *UserCollection) Limit(limit int) *UserCollection {
return &UserCollection{
Data: c.Data,
Pagination: c.Pagination,
limit: limit,
}
}
// Users returns a list of pointers to the structs; used for cases where
// there is no pagination and the caller wants to return a flat slice of items
// instead of a collection (which would be misleading, as collections imply
// pagination).
func (c *UserCollection) Users() []*User {
var result []*User
c.Walk(func(item *User) bool {
result = append(result, item)
return true
})
return result
}
// Walk applies a function to all items in the collection, in order. If the
// function returns false, iterating will be stopped.
func (c *UserCollection) Walk(f UserWalkerFunc) {
it := c.Iterator()
for item := range it.Output() {
if !f(item) {
it.Stop()
}
}
}
// Size returns the number of elements in the collection; returns -1 if the total
// number cannot be determined without iterating over additional pages (which
// requires network roundtrips) and fetchAllPages is set to false
func (c *UserCollection) Size(fetchAllPages bool) int {
length := len(c.Data)
if c.limit > 0 && length > c.limit {
length = c.limit
}
// we have a simple collection if no pagination information is set
if len(c.Pagination.Links) == 0 && c.Pagination.Max == 0 {
return length
}
// we have only one page
if c.Pagination.Size < c.Pagination.Max {
return length
}
if !fetchAllPages {
return -1
}
count := 0
c.Walk(func(item *User) bool {
count++
return true
})
return count
}
// Get returns the n-th element (the first one has idx 0) and nil if there is
// no such index.
func (c *UserCollection) Get(idx int) *User {
cur := 0
it := c.Iterator()
for item := range it.Output() {
if cur == idx {
return item
}
cur++
}
return nil
}
// First returns the first element, if any, otherwise nil.
func (c *UserCollection) First() *User {
if len(c.Data) == 0 {
return nil
}
return &c.Data[0]
}
// ScanForID searches through the collection and looks for an item with the given ID.
func (c *UserCollection) ScanForID(id string) *User {
it := c.Iterator()
for item := range it.Output() {
if item.ID == id {
return item
}
}
return nil
}
// Iterator returns an interator for a UserCollection. There can be many
// independent iterators starting from the same collection.
func (c *UserCollection) Iterator() UserIterator {
it := UserIterator{
output: make(chan *User),
killSwitch: make(chan struct{}),
origin: c,
limit: c.limit,
}
go it.work()
return it
}
// UserIterator represents a list of users.
type UserIterator struct {
output chan *User
killSwitch chan struct{}
origin *UserCollection
limit int
}
// Output returns a channel that can be used to read all users
// from the iterator.
func (i *UserIterator) Output() <-chan *User {
return i.output
}
// Stop interrupts the iterator and cancels all further pending action. After
// calling this, the iterator returns no more users and becomes
// unusable.
func (i *UserIterator) Stop() {
close(i.killSwitch)
// drain the remaining element(s)
for _ = range i.output {
}
}
// work is the goroutine that reads items from the current page and
// fetches new pages until all pages are fetched or the iteration is stopped.
func (i *UserIterator) work() {
page := i.origin
first := true
remaining := i.limit
defer close(i.output)
for {
select {
case <-i.killSwitch:
return
default:
// if this is not the first iteration, fetch the next page to work on
if !first {
// is there another one?
nextLink := firstLink(&page.Pagination, "next")
if nextLink == nil {
return
}
// fetch the next page
p, err := fetchUsers(nextLink.request(nil, nil, NoEmbeds))
if err != nil {
return
}
// is this page empty?
if len(p.Data) == 0 {
return
}
// use this page from now on
page = p
}
for idx := 0; idx < len(page.Data); idx++ {
select {
case <-i.killSwitch:
return
default:
i.output <- &page.Data[idx]
remaining--
}
// stop we we exhausted all allowed elements
if i.limit > 0 && remaining <= 0 {
return
}
}
first = false
}
}
}