package wire import ( "encoding/json" "net/url" "strings" ) // UnmarshalRows unpacks a list from an object (trying keys in order) or a JSON array. // next is next_cursor, else the cursor query param of next, else next itself when it // is not a URL. func UnmarshalRows[T any](raw json.RawMessage, keys ...string) (rows []T, next string, err error) { data := Unwrap(raw) var obj map[string]json.RawMessage if json.Unmarshal(data, &obj) != nil { var list []T if err := json.Unmarshal(data, &list); err != nil { return nil, "", err } return list, "", nil } next = cursorFromMap(obj) for _, k := range keys { item, ok := obj[k] if !ok || len(item) == 0 || string(item) == "null" { continue } var list []T if json.Unmarshal(item, &list) == nil { return list, next, nil } var one T if json.Unmarshal(item, &one) == nil { return []T{one}, next, nil } } return nil, next, nil } func cursorFromMap(obj map[string]json.RawMessage) string { if c := stringField(obj, "next_cursor"); c != "" { return c } return Cursor(stringField(obj, "next"), "") } func stringField(obj map[string]json.RawMessage, key string) string { raw, ok := obj[key] if !ok || len(raw) == 0 || string(raw) == "null" { return "" } var s string if json.Unmarshal(raw, &s) == nil { return s } return "" } // Cursor prefers nextCursor; otherwise extracts cursor from a next URL. func Cursor(next, nextCursor string) string { if nextCursor != "" { return nextCursor } next = strings.TrimSpace(next) if next == "" { return "" } u, err := url.Parse(next) if err != nil || (u.Scheme == "" && u.Host == "" && !strings.Contains(next, "?")) { return next } if v := u.Query().Get("cursor"); v != "" { return v } return "" }