diff --git a/airingschedule.go b/airingschedule.go index 18984d9..e58f03c 100644 --- a/airingschedule.go +++ b/airingschedule.go @@ -1,8 +1,10 @@ package anilist -func (api *Api) GetAiringSchedule(vars AiringScheduleQuery, onError func(error)) <-chan *AiringSchedule { +import "context" + +func (api *Api) GetAiringSchedule(ctx context.Context, vars AiringScheduleQuery, onError func(error)) Cursor[AiringSchedule] { resp := responseObj[*page[AiringSchedule]]{} - return requestPaged(api, getAiringScheduleQuery, vars.toMap(), &resp, onError) + return requestPaged(api, ctx, getAiringScheduleQuery, vars.toMap(), &resp, onError) } const ( diff --git a/api.go b/api.go index 144c1dd..89037b6 100644 --- a/api.go +++ b/api.go @@ -2,6 +2,7 @@ package anilist import ( "bytes" + "context" "encoding/json" "net/http" ) @@ -60,15 +61,19 @@ func request[T any](api *Api, query string, vars map[string]interface{}, respObj return nil } -func requestPaged[R any](api *Api, query string, vars map[string]interface{}, respObj *responseObj[*page[R]], onError func(error)) <-chan *R { +func requestPaged[R any](api *Api, ctx context.Context, query string, vars map[string]interface{}, respObj *responseObj[*page[R]], onError func(error)) Cursor[R] { resp := responseObj[struct { Page *page[R] `json:"Page"` }]{} + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithCancel(ctx) + out := make(chan *R, 50) go func() { defer close(out) + defer cancelFunc() vars["page"] = 0 @@ -87,7 +92,11 @@ func requestPaged[R any](api *Api, query string, vars map[string]interface{}, re for _, value := range resp.Data.Page.Data() { value := value - out <- &value + select { + case out <- &value: + case <-ctx.Done(): + return + } } if resp.Data.Page.PageInfo.CurrentPage == resp.Data.Page.PageInfo.LastPage { @@ -96,5 +105,9 @@ func requestPaged[R any](api *Api, query string, vars map[string]interface{}, re } }() - return out + return Cursor[R]{ + Chan: out, + ctx: ctx, + cancelFunc: cancelFunc, + } } diff --git a/cursor.go b/cursor.go new file mode 100644 index 0000000..4eccd7e --- /dev/null +++ b/cursor.go @@ -0,0 +1,31 @@ +package anilist + +import "context" + +type Cursor[T any] struct { + ctx context.Context + cancelFunc context.CancelFunc + Chan <-chan *T +} + +func (c Cursor[T]) First() *T { + defer c.cancelFunc() + return <-c.Chan +} + +func (c Cursor[T]) Next() (*T, bool) { + if c.ctx.Err() == nil { + value, ok := <-c.Chan + return value, ok + } + + return nil, false +} + +func (c Cursor[T]) Slice() []T { + s := make([]T, 0) + for value, ok := c.Next(); ok; value, ok = c.Next() { + s = append(s, *value) + } + return s +} diff --git a/media.go b/media.go index 918769e..e0d4911 100644 --- a/media.go +++ b/media.go @@ -1,8 +1,10 @@ package anilist -func (api *Api) GetMedia(vars MediaQuery, onError func(error)) <-chan *Media { +import "context" + +func (api *Api) GetMedia(ctx context.Context, vars MediaQuery, onError func(error)) Cursor[Media] { resp := responseObj[*page[Media]]{} - return requestPaged(api, getMediaQuery, vars.toMap(), &resp, onError) + return requestPaged(api, ctx, getMediaQuery, vars.toMap(), &resp, onError) } const ( diff --git a/medialist.go b/medialist.go index 917dc95..e0547cb 100644 --- a/medialist.go +++ b/medialist.go @@ -1,8 +1,10 @@ package anilist -func (api *Api) GetMediaList(vars MediaListQuery, onError func(error)) <-chan *MediaList { +import "context" + +func (api *Api) GetMediaList(ctx context.Context, vars MediaListQuery, onError func(error)) Cursor[MediaList] { resp := responseObj[*page[MediaList]]{} - return requestPaged(api, getMediaListQuery, vars.toMap(), &resp, onError) + return requestPaged(api, ctx, getMediaListQuery, vars.toMap(), &resp, onError) } const ( diff --git a/sub_selections.go b/sub_selections.go index 5e59ec7..fb5ac7f 100644 --- a/sub_selections.go +++ b/sub_selections.go @@ -7,7 +7,6 @@ const ( airingAt timeUntilAiring episode - media ` + subSelectionMedia + ` }` subSelectionMediaList = `{ @@ -93,7 +92,7 @@ const ( isAdult userId } - nextAiringEpisode + nextAiringEpisode ` + subSelectionAiringSchedule + ` }` subSelectionFuzzyDate = `{ diff --git a/types.go b/types.go index 998a5e2..f41ac7e 100644 --- a/types.go +++ b/types.go @@ -211,5 +211,4 @@ type AiringSchedule struct { AiringAt UnixTime `json:"airingAt"` TimeUntilAiring Seconds `json:"timeUntilAiring"` Episode int `json:"episode"` - Media *Media `json:"media"` } diff --git a/user.go b/user.go index f7b9903..10812cf 100644 --- a/user.go +++ b/user.go @@ -1,5 +1,7 @@ package anilist +import "context" + func (api *Api) GetUserByName(name string) (*User, error) { query := `query ($name: String) { User (name: $name) ` + subSelectionUser + ` @@ -42,10 +44,10 @@ func (api *Api) GetUserByID(id int) (*User, error) { return resp.Data.User, nil } -func (api *Api) SearchUsers(search string, onError func(error)) <-chan *User { +func (api *Api) SearchUsers(ctx context.Context, search string, onError func(error)) Cursor[User] { vars := map[string]interface{}{"search": search} resp := responseObj[*page[User]]{} - return requestPaged(api, searchUsersQuery, vars, &resp, onError) + return requestPaged(api, ctx, searchUsersQuery, vars, &resp, onError) } const (