-
Notifications
You must be signed in to change notification settings - Fork 17
/
options.go
244 lines (214 loc) · 8.58 KB
/
options.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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package igdb
import (
"fmt"
"strings"
"github.com/Henry-Sarabia/apicalypse"
"github.com/Henry-Sarabia/blank"
"github.com/pkg/errors"
)
// Errors returned by an Option when setting options for an API call.
var (
// ErrEmptyQry occurs when an empty string is used as a query value.
ErrEmptyQry = errors.New("provided option query value is empty")
// ErrEmptyFields occurs when an empty string is used as a field value.
ErrEmptyFields = errors.New("one or more provided option field values are empty")
// ErrExpandedField occurs when a field value tries to access an expanded subfield.
ErrExpandedField = errors.New("one or more provided option field values is an expanded subfield which is not supported")
// ErrEmptyFilterVals occurs when an empty string is used as a filter value.
ErrEmptyFilterVals = errors.New("one or more provided filter option values are empty")
// ErrOutOfRange occurs when a provided number value is out of valid range.
ErrOutOfRange = errors.New("provided option value is out of range")
)
// Option functions are used to set the options for an API call.
// Option is the first-order function returned by the available
// functional options (e.g. SetLimit or SetFilter). This first-order
// function is then passed into a service's Get, List, Index, Search, or
// Count function.
type Option func() (apicalypse.Option, error)
// ComposeOptions composes multiple functional options into a single Option.
// This is primarily used to create a single functional option that can be used
// repeatedly across multiple queries.
func ComposeOptions(opts ...Option) Option {
return func() (apicalypse.Option, error) {
unwrapped, err := unwrapOptions(opts...)
if err != nil {
return nil, errors.Wrap(err, "cannot compose invalid functional options")
}
return apicalypse.ComposeOptions(unwrapped...), nil
}
}
// unwrapOptions executes the provided options to retrieve the apicalypse options
// and check for any errors. The first error encountered will be returned.
func unwrapOptions(opts ...Option) ([]apicalypse.Option, error) {
unwrapped := make([]apicalypse.Option, len(opts))
for i, opt := range opts {
var err error
if unwrapped[i], err = opt(); err != nil {
return nil, errors.Wrap(err, "cannot unwrap invalid option")
}
}
return unwrapped, nil
}
// order specifies the order in which to organize the results from an API call.
// There are three orders in which results are organized: relevance, ascending,
// and descending. Relevance is only available as a default and cannot be
// explicitly specified.
type order string
// Available orders for the functional option SetOrder
const (
// OrderAscending is used as an argument in the SetOrder functional
// option to organize the results from an API call in ascending order.
OrderAscending order = "asc"
// OrderDescending is used as an argument in the SetOrder functional
// option to organize the results from an API call in descending order.
OrderDescending order = "desc"
)
// SetOrder is a functional option used to sort the results from an API call.
// The default order is by relevance.
//
// For more information, visit: https://api-docs.igdb.com/#sorting
func SetOrder(field string, order order) Option {
return func() (apicalypse.Option, error) {
if blank.Is(field) {
return nil, ErrEmptyFields
}
return apicalypse.Sort(field, string(order)), nil
}
}
// SetLimit is a functional option used to limit the number of results from
// an API call. The default limit is 10. The maximum limit is 500.
//
// For more information, visit: https://api-docs.igdb.com/#pagination
func SetLimit(lim int) Option {
return func() (apicalypse.Option, error) {
if lim <= 0 || lim > 500 {
return nil, ErrOutOfRange
}
return apicalypse.Limit(lim), nil
}
}
// SetOffset is a functional option used to offset the results from an API
// call. The default offset is 0.
//
// For more information, visit: https://api-docs.igdb.com/#pagination
func SetOffset(off int) Option {
return func() (apicalypse.Option, error) {
if off < 0 {
return nil, ErrOutOfRange
}
return apicalypse.Offset(off), nil
}
}
// SetFields is a functional option used to specify which fields of the
// requested IGDB object you want the API to provide. Subfields are accessed
// with a dot operator (e.g. cover.url). To select all available fields at
// once, use an asterisk character (i.e. *). Note that the field string must
// match an IGDB object's JSON field tag exactly, not the Go struct field
// name.
//
// For more information, visit: https://api-docs.igdb.com/#fields
func SetFields(fields ...string) Option {
return func() (apicalypse.Option, error) {
if len(fields) <= 0 {
return nil, ErrEmptyFields
}
for _, f := range fields {
if blank.Is(f) {
return nil, ErrEmptyFields
}
if strings.Contains(f, ".") {
return nil, ErrExpandedField
}
}
return apicalypse.Fields(fields...), nil
}
}
// SetExclude is a functional option used to specify which fields of the
// requested IGDB object you want the API to exclude. Note that the field
// string must match an IGDB object's JSON field tag exactly, not the Go struct
// name.
//
// For more information, visit: https://api-docs.igdb.com/#exclude
func SetExclude(fields ...string) Option {
return func() (apicalypse.Option, error) {
if len(fields) <= 0 {
return nil, ErrEmptyFields
}
for _, f := range fields {
if blank.Is(f) {
return nil, ErrEmptyFields
}
if strings.Contains(f, ".") {
return nil, ErrExpandedField
}
}
return apicalypse.Exclude(fields...), nil
}
}
// operator represents the postfix operation used to filter the results from
// an API call using the provided field value. For the list of postfix
// operators, visit: https://api-docs.igdb.com/#filters
type operator string
// Available operators for the functional option SetFilter
const (
// OpEquals checks for equality. Must match exactly.
OpEquals operator = "%s = %s"
// OpNotEquals checks for inequality. Any non-exact match.
OpNotEquals operator = "%s != %s"
// OpGreaterThan checks if a field value is greater than a given value. Only works on numbers.
OpGreaterThan operator = "%s > %s"
// OpGreaterThanEqual checks if a field value is greater than or equal to a given value. Only works on numbers.
OpGreaterThanEqual operator = "%s >= %s"
// OpLessThan checks if a field value is less than a given value. Only works on numbers.
OpLessThan operator = "%s < %s"
// OpLessThanEqual checks if a field value is less than or equal to a given value. Only works on numbers.
OpLessThanEqual operator = "%s <= %s"
// OpContainsAll checks if the given value exists in within the array.
OpContainsAll operator = "%s = [%s]"
// OpNotContainsAll checks if the given value does not exist in within the array.
OpNotContainsAll operator = "%s != [%s]"
// OpContainsAtLeast checks if any of the given values exist within the array.
OpContainsAtLeast operator = "%s = (%s)"
// OpNotContainsAtLeast checks if any of the given values do not exist within the array.
OpNotContainsAtLeast operator = "%s != (%s)"
// OpContainsExactly checks if the the given values exactly match the array.
OpContainsExactly operator = "%s = {%s}"
)
// SetFilter is a functional option used to filter the results from an API
// call. Filtering operations need three different arguments: an operator
// and 2 operands, the field and its value. The provided field and val string
// act as the operands for the provided operator. If multiple values are provided,
// they will be concatenated into a comma separated list. If no values are
// provided, an error is returned.
//
// SetFilter is the only option allowed to be set multiple times in a single
// API call. By default, results are unfiltered.
//
// Note that when filtering a field that consists of an enumerated type (e.g. Gender Code,
// Feed Category, Game Status, etc.), you must provide the number corresponding
// to the intended field value. For your convenience, you may also provide the
// enumerated constant.
//
// For more information, visit: https://api-docs.igdb.com/#filters
func SetFilter(field string, op operator, val ...string) Option {
return func() (apicalypse.Option, error) {
if blank.Is(field) {
return nil, ErrEmptyFields
}
if len(val) <= 0 || blank.Has(val) {
return nil, ErrEmptyFilterVals
}
j := strings.Join(val, ",")
return apicalypse.Where(fmt.Sprintf(string(op), field, j)), nil
}
}
// setSearch is a functional option used to search the IGDB using the
// provided query.
func setSearch(qry string) Option {
return func() (apicalypse.Option, error) {
if blank.Is(qry) {
return nil, ErrEmptyQry
}
return apicalypse.Search("", qry), nil
}
}