package jikan import ( "context" "errors" "fmt" "net/url" "strconv" "strings" ) type ProducerListEntry struct { MalID int `json:"mal_id"` Titles []struct { Type string `json:"type"` Title string `json:"title"` } `json:"titles"` } type ProducersResponse struct { Data []ProducerListEntry `json:"data"` Pagination Pagination `json:"pagination"` } type ProducerListResult struct { Items []ProducerListEntry HasNextPage bool } func (c *Client) GetProducers(ctx context.Context, query string, page int, limit int) (ProducerListResult, error) { if page < 1 { page = 1 } if limit < 1 { limit = 1 } q := strings.TrimSpace(query) if q == "" { return c.fetchProducersPage(ctx, "", page, limit) } result, err := c.fetchProducersPage(ctx, q, page, limit) if err == nil { return result, nil } var apiErr *APIError if !errors.As(err, &apiErr) { return ProducerListResult{}, err } return c.searchProducersFromPages(ctx, q, page, limit) } func (c *Client) fetchProducersPage(ctx context.Context, query string, page int, limit int) (ProducerListResult, error) { q := strings.TrimSpace(query) cacheKey := fmt.Sprintf("producers:%s:%d:%d", q, page, limit) params := url.Values{} params.Set("page", strconv.Itoa(page)) params.Set("limit", strconv.Itoa(limit)) setQueryValue(params, "q", q) reqURL := buildRequestURL(c.baseURL, "/producers", params) var result ProducersResponse if err := c.getWithCache(ctx, cacheKey, producerCacheTTL, reqURL, &result); err != nil { return ProducerListResult{}, err } return ProducerListResult{ Items: result.Data, HasNextPage: result.Pagination.HasNextPage, }, nil } func (c *Client) searchProducersFromPages(ctx context.Context, query string, page int, limit int) (ProducerListResult, error) { const maxPagesToScan = 25 needle := strings.ToLower(strings.TrimSpace(query)) startIndex := (page - 1) * limit endIndex := startIndex + limit matches := make([]ProducerListEntry, 0, endIndex) scannedAll := false for currentPage := 1; currentPage <= maxPagesToScan; currentPage++ { result, err := c.fetchProducersPage(ctx, "", currentPage, limit) if err != nil { return ProducerListResult{}, err } for _, item := range result.Items { name := strings.ToLower(ProducerListEntryName(item)) if strings.Contains(name, needle) { matches = append(matches, item) } } if len(matches) >= endIndex { return ProducerListResult{ Items: matches[startIndex:endIndex], HasNextPage: len(matches) > endIndex || result.HasNextPage, }, nil } if !result.HasNextPage { scannedAll = true break } } if startIndex >= len(matches) { return ProducerListResult{ Items: []ProducerListEntry{}, HasNextPage: !scannedAll, }, nil } if endIndex > len(matches) { endIndex = len(matches) } return ProducerListResult{ Items: matches[startIndex:endIndex], HasNextPage: !scannedAll, }, nil } func ProducerListEntryName(entry ProducerListEntry) string { for _, t := range entry.Titles { if t.Title != "" { return t.Title } } if entry.MalID > 0 { return strconv.Itoa(entry.MalID) } return "" }