-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathuser.go
211 lines (174 loc) · 5.07 KB
/
user.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
// Copyright (c) 2015, Sgt. Kabukiman | MIT licensed
package srapi
import (
"net/url"
"time"
)
// User represents a user.
type User struct {
ID string
Names struct {
International string
Japanese string
}
Weblink string
NameStyle struct {
Style string
Color *NameColor
ColorFrom *NameColor `json:"color-from"`
ColorTo *NameColor `json:"color-to"`
} `json:"name-style"`
Role string
Signup *time.Time
Location struct {
Country Location
Region *Location
}
Twitch *SocialLink
Hitbox *SocialLink
YouTube *SocialLink
Twitter *SocialLink
SpeedRunsLive *SocialLink
Links []Link
}
// SocialLink is a minimal link that points to an external website.
type SocialLink struct {
URI string
}
// Location is a country/region code with names.
type Location struct {
Code string
Names struct {
International string
Japanese string
}
}
// NameColor contains hex color codes for light and dark backgrounds, used
// to display usernames on speedrun.com.
type NameColor struct {
Light string
Dark string
}
// toUser transforms a data blob to a User struct, if possible.
// Returns nil if casting the data was not successful or if data was nil.
func toUser(data interface{}, isResponse bool) *User {
if data == nil {
return nil
}
if isResponse {
dest := userResponse{}
if recast(data, &dest) == nil {
return &dest.Data
}
} else {
dest := User{}
if recast(data, &dest) == nil {
return &dest
}
}
return nil
}
// toUserCollection transforms a data blob to a UserCollection.
// If data is nil or casting was unsuccessful, an empty UserCollection
// is returned.
func toUserCollection(data interface{}) *UserCollection {
tmp := &UserCollection{}
recast(data, tmp)
return tmp
}
// userResponse models the actual API response from the server
type userResponse struct {
// the one user contained in the response
Data User
}
// UserByID tries to fetch a single user, identified by their ID.
// When an error is returned, the returned user is nil.
func UserByID(id string) (*User, *Error) {
return fetchUser(request{"GET", "/users/" + id, nil, nil, nil, ""})
}
// Runs fetches a list of runs done by the user, optionally filtered
// and sorted. This function always returns a RunCollection.
func (u *User) Runs(filter *RunFilter, sort *Sorting, embeds string) (*RunCollection, *Error) {
return fetchRunsLink(firstLink(u, "runs"), filter, sort, embeds)
}
// ModeratedGames fetches a list of games moderated by the user, optionally
// filtered and sorted. This function always returns a GameCollection.
func (u *User) ModeratedGames(filter *GameFilter, sort *Sorting, embeds string) (*GameCollection, *Error) {
return fetchGamesLink(firstLink(u, "games"), filter, sort, embeds)
}
// PersonalBests fetches a list of PBs by the user, optionally filtered and
// sorted.
func (u *User) PersonalBests(filter *PersonalBestFilter, embeds string) (*PersonalBestCollection, *Error) {
return fetchPersonalBestsLink(firstLink(u, "personal-bests"), filter, embeds)
}
// for the 'hasLinks' interface
func (u *User) links() []Link {
return u.Links
}
// UserFilter represents the possible filtering options when fetching a list
// of users.
type UserFilter struct {
Lookup string
Name string
Twitch string
Hitbox string
Twitter string
SpeedRunsLive string
}
// applyToURL merged the filter into a URL.
func (uf *UserFilter) applyToURL(u *url.URL) {
if uf == nil {
return
}
values := u.Query()
if len(uf.Lookup) > 0 {
values.Set("lookup", uf.Lookup)
}
if len(uf.Name) > 0 {
values.Set("name", uf.Name)
}
if len(uf.Twitch) > 0 {
values.Set("twitch", uf.Twitch)
}
if len(uf.Hitbox) > 0 {
values.Set("hitbox", uf.Hitbox)
}
if len(uf.Twitter) > 0 {
values.Set("twitter", uf.Twitter)
}
if len(uf.SpeedRunsLive) > 0 {
values.Set("speedrunslive", uf.SpeedRunsLive)
}
u.RawQuery = values.Encode()
}
// Users retrieves a collection of users from speedrun.com. In most cases, you
// will filter the game, as paging through *all* users takes A LOT of requests.
func Users(f *UserFilter, s *Sorting, c *Cursor) (*UserCollection, *Error) {
return fetchUsers(request{"GET", "/users", f, s, c, ""})
}
// fetchUser fetches a single user from the network. If the request failed,
// the returned user is nil. Otherwise, the error is nil.
func fetchUser(request request) (*User, *Error) {
result := &userResponse{}
err := httpClient.do(request, result)
if err != nil {
return nil, err
}
return &result.Data, nil
}
// fetchUserLink tries to fetch a given link and interpret the response as
// a single user. If the link is nil or the user could not be fetched,
// nil is returned.
func fetchUserLink(link requestable) (*User, *Error) {
if !link.exists() {
return nil, nil
}
return fetchUser(link.request(nil, nil, ""))
}
// fetchUsers fetches a list of users from the network. It always
// returns a collection, even when an error is returned.
func fetchUsers(request request) (*UserCollection, *Error) {
result := &UserCollection{}
err := httpClient.do(request, result)
return result, err
}