| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- // Package ops holds the list operations shared by the CLI and the MCP server.
- //
- // Both surfaces expose the same three modes; keeping the fetch loop here is
- // what keeps them congruent instead of drifting apart.
- package ops
- import (
- "context"
- "errors"
- "fmt"
- "gogs.netdome.biz/paul/cheapo/internal/game"
- )
- // Mode selects what List returns.
- type Mode string
- const (
- // Free returns games that are free right now.
- Free Mode = "free"
- // Upcoming returns announced future giveaways (stores with that capability only).
- Upcoming Mode = "upcoming"
- // Deals returns discounted games (stores with that capability only).
- Deals Mode = "deals"
- )
- // Modes lists the accepted Mode values in a stable order.
- var Modes = []Mode{Free, Upcoming, Deals}
- // ParseMode maps a mode name to a Mode. The empty string means Free.
- func ParseMode(name string) (Mode, error) {
- switch m := Mode(name); m {
- case "", Free:
- return Free, nil
- case Upcoming, Deals:
- return m, nil
- default:
- return "", fmt.Errorf("unknown mode %q (free, upcoming, deals)", name)
- }
- }
- // List queries provs for mode.
- //
- // Stores without the requested capability are skipped silently (Steam has no
- // upcoming window, GOG no deals). A failing store becomes a warning instead of
- // aborting, so one flaky endpoint does not hide the others. The error return
- // is only for an unknown mode.
- func List(ctx context.Context, provs []game.Provider, mode Mode) ([]game.Game, []string, error) {
- m, err := ParseMode(string(mode))
- if err != nil {
- return nil, nil, err
- }
- var (
- all []game.Game
- warnings []string
- )
- for _, p := range provs {
- games, err := fetch(ctx, p, m)
- if errors.Is(err, errUnsupported) {
- continue
- }
- if err != nil {
- warnings = append(warnings, fmt.Sprintf("%s: %v", p.Name(), err))
- continue
- }
- all = append(all, games...)
- }
- return all, warnings, nil
- }
- // errUnsupported marks a provider that cannot answer the requested mode.
- var errUnsupported = errors.New("mode not supported by provider")
- // fetch asks a single provider for the games of mode.
- func fetch(ctx context.Context, p game.Provider, mode Mode) ([]game.Game, error) {
- switch mode {
- case Upcoming:
- u, ok := p.(game.UpcomingProvider)
- if !ok {
- return nil, errUnsupported
- }
- return u.GetUpcomingGames(ctx)
- case Deals:
- d, ok := p.(game.DealProvider)
- if !ok {
- return nil, errUnsupported
- }
- return d.GetDeals(ctx)
- default:
- return p.GetFreeGames(ctx)
- }
- }
|