99 "reflect"
1010 "strconv"
1111 "strings"
12+ "time"
1213
1314 "github.com/google/go-github/v65/github"
1415)
@@ -123,7 +124,10 @@ func (it *Iterator[T, O]) All() iter.Seq[T] {
123124 vals [k ] = v [0 ]
124125 }
125126
126- updateOptions (it .opt , vals )
127+ if err := updateOptions (it .opt , vals ); err != nil {
128+ it .err = err
129+ return
130+ }
127131 }
128132 }
129133 }
@@ -200,10 +204,17 @@ func (it *Iterator[T, O]) do() ([]T, *github.Response, error) {
200204 return nil , nil , errors .New ("no func provided" )
201205}
202206
207+ var (
208+ stringTypePtr * string
209+ intTypePtr * int
210+ int64TypePtr * int64
211+ boolTypePtr * bool
212+ )
213+
203214// updateOptions will update the github options based on the provided map and the `url` tag.
204215// If the field in the struct has a `url` tag it tries to set the value of the field from the one
205216// found in the map, if any.
206- func updateOptions (v any , m map [string ]string ) {
217+ func updateOptions (v any , m map [string ]string ) error {
207218 valueOf := reflect .ValueOf (v )
208219 typeOf := reflect .TypeOf (v )
209220
@@ -219,29 +230,120 @@ func updateOptions(v any, m map[string]string) {
219230 // if field is of type struct then iterate over the pointer
220231 if structField .Type .Kind () == reflect .Struct {
221232 if fieldValue .CanAddr () {
222- updateOptions (fieldValue .Addr ().Interface (), m )
233+ if err := updateOptions (fieldValue .Addr ().Interface (), m ); err != nil {
234+ return err
235+ }
223236 }
224237 }
225238
226239 // otherwise check if it has a 'url' tag
227240 urlTag := structField .Tag .Get ("url" )
228- if urlTag != "" {
229- urlParam := strings .Split (urlTag , "," )[0 ]
230-
231- if fieldValue .IsValid () && fieldValue .CanSet () {
232- if v , found := m [urlParam ]; found {
233- switch fieldValue .Kind () {
234- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
235- if i , err := strconv .Atoi (v ); err == nil {
236- fieldValue .SetInt (int64 (i ))
237- }
238- case reflect .Ptr :
239- fieldValue .Set (reflect .ValueOf (& v ))
240- default :
241- fieldValue .Set (reflect .ValueOf (v ))
242- }
241+ if urlTag == "" {
242+ continue
243+ }
244+
245+ if ! fieldValue .IsValid () || ! fieldValue .CanSet () {
246+ continue
247+ }
248+
249+ urlParam := strings .Split (urlTag , "," )[0 ]
250+ v , found := m [urlParam ]
251+ if ! found {
252+ continue
253+ }
254+
255+ switch fieldValue .Kind () {
256+
257+ // handle string
258+ case reflect .String :
259+ fieldValue .Set (reflect .ValueOf (v ))
260+
261+ // handle numeric types (int, int8, int16, int32, int64)
262+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
263+ if i , err := strconv .Atoi (v ); err == nil {
264+ fieldValue .SetInt (int64 (i ))
265+ }
266+
267+ // handle bool
268+ case reflect .Bool :
269+ parsedBool , err := strconv .ParseBool (v )
270+ if err != nil {
271+ return fmt .Errorf ("error while parsing string '%s' as bool: %s" , v , err )
272+ }
273+ fieldValue .Set (reflect .ValueOf (parsedBool ))
274+
275+ // handle pointers (*string, *int, *int64, *bool, *time.Time)
276+ case reflect .Pointer :
277+ switch fieldValue .Type () {
278+
279+ // handle *string
280+ case reflect .TypeOf (stringTypePtr ):
281+ fieldValue .Set (reflect .ValueOf (& v ))
282+
283+ // handle *int
284+ case reflect .TypeOf (intTypePtr ):
285+ parsedInt , err := strconv .Atoi (v )
286+ if err != nil {
287+ return fmt .Errorf ("error while parsing string '%s' as int: %s" , v , err )
243288 }
289+ fieldValue .Set (reflect .ValueOf (& parsedInt ))
290+
291+ // handle *int64
292+ case reflect .TypeOf (int64TypePtr ):
293+ parsedInt64 , err := strconv .ParseInt (v , 10 , 64 )
294+ if err != nil {
295+ return fmt .Errorf ("error while parsing string '%s' as int64: %s" , v , err )
296+ }
297+ fieldValue .Set (reflect .ValueOf (& parsedInt64 ))
298+
299+ // handle *bool
300+ case reflect .TypeOf (boolTypePtr ):
301+ parsedBool , err := strconv .ParseBool (v )
302+ if err != nil {
303+ return fmt .Errorf ("error while parsing string '%s' as bool: %s" , v , err )
304+ }
305+ fieldValue .Set (reflect .ValueOf (& parsedBool ))
306+
307+ // handle *time.Time
308+ case reflect .TypeOf (& time.Time {}):
309+ layout := time .RFC3339
310+ if len (v ) == len (time .DateOnly ) {
311+ layout = time .DateOnly
312+ }
313+
314+ result , err := time .Parse (layout , v )
315+ if err != nil {
316+ return fmt .Errorf ("error while parsing string '%s' as time.Time: %s" , v , err )
317+ }
318+
319+ fieldValue .Set (reflect .ValueOf (& result ))
320+
321+ default :
322+ return fmt .Errorf ("cannot set '%s' value to unknown pointer of '%s'" , v , fieldValue .Type ())
244323 }
324+
325+ case reflect .Struct :
326+ // handle time.Time
327+ if fieldValue .Type () == reflect .TypeOf (time.Time {}) {
328+ layout := time .RFC3339
329+ if len (v ) == len (time .DateOnly ) {
330+ layout = time .DateOnly
331+ }
332+
333+ result , err := time .Parse (layout , v )
334+ if err != nil {
335+ return fmt .Errorf ("error while parsing string '%s' as time.Time: %s" , v , err )
336+ }
337+
338+ fieldValue .Set (reflect .ValueOf (result ))
339+ } else {
340+ return fmt .Errorf ("cannot set '%s' value to unknown struct '%s'" , v , fieldValue .Type ())
341+ }
342+
343+ default :
344+ return fmt .Errorf ("cannot set '%s' value to unknown type '%s'" , v , fieldValue .Type ())
245345 }
246346 }
347+
348+ return nil
247349}
0 commit comments