mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-08-29 01:08:49 +00:00
major: initial release
* initial development * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * pipeline * ci/cd * ci/cd * ci/cd * ci/cd * ci/cd * ci/cd * ci/cd * pokemon * pokemon * pokemon * pokemon * pokemon * pokemon * improvements * improvements * ci/cd * ci/cd * improvements * improvements * improvements * improvements * improvements * improvements * improvements * improvements * improvements --------- Co-authored-by: sdine <sdine@sdine.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
// IGameModule + ISetSource: the seam that lets the UI handle Magic and Pokemon
|
||||
// uniformly. New game support is "implement these interfaces and register the
|
||||
// module in the composition root" - see the Rust `templates/` module for the
|
||||
// pattern this is modeled after.
|
||||
|
||||
#include "ccm/domain/Enums.hpp"
|
||||
#include "ccm/domain/Set.hpp"
|
||||
#include "ccm/ports/ICardPreviewSource.hpp"
|
||||
#include "ccm/util/Result.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class ISetSource {
|
||||
public:
|
||||
virtual ~ISetSource() = default;
|
||||
|
||||
// Fetch the canonical set list from the game's external API.
|
||||
// Implementations return a vector that has already been filtered
|
||||
// (e.g. no digital-only sets) and sorted by release date ascending.
|
||||
virtual Result<std::vector<Set>> fetchAll() = 0;
|
||||
};
|
||||
|
||||
class IGameModule {
|
||||
public:
|
||||
virtual ~IGameModule() = default;
|
||||
|
||||
[[nodiscard]] virtual Game id() const noexcept = 0;
|
||||
// Subdirectory name used inside the data storage root. Matches the Rust
|
||||
// string literals "magic" / "pokemon".
|
||||
[[nodiscard]] virtual std::string dirName() const = 0;
|
||||
[[nodiscard]] virtual std::string displayName() const = 0;
|
||||
|
||||
virtual ISetSource& setSource() = 0;
|
||||
|
||||
// Optional preview source. Returning nullptr signals the game has no
|
||||
// remote preview API (the UI then renders only locally stored images).
|
||||
// The default keeps existing modules compiling without forcing every
|
||||
// game to provide one.
|
||||
virtual ICardPreviewSource* cardPreviewSource() noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
// MagicCardPreviewSource: ICardPreviewSource implementation for Magic the
|
||||
// Gathering. Calls Scryfall's search endpoint at
|
||||
// https://api.scryfall.com/cards/search?q=name:"<name>" AND set:<setCode>
|
||||
// and returns `data[0].image_uris.normal`. Mirrors the established
|
||||
// `src/components/magic/SelectedMtgPanel.tsx::getImage`, including the
|
||||
// `&` -> `and` substitution in the card name.
|
||||
|
||||
#include "ccm/ports/ICardPreviewSource.hpp"
|
||||
#include "ccm/ports/IHttpClient.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class MagicCardPreviewSource final : public ICardPreviewSource {
|
||||
public:
|
||||
explicit MagicCardPreviewSource(IHttpClient& http);
|
||||
|
||||
Result<std::string> fetchImageUrl(std::string_view name,
|
||||
std::string_view setId,
|
||||
std::string_view setNo) override;
|
||||
|
||||
// Build the fully URL-encoded Scryfall search URL for the given card.
|
||||
// Exposed for unit testing and to keep encoding rules in one place.
|
||||
static std::string buildSearchUrl(std::string_view name,
|
||||
std::string_view setId);
|
||||
|
||||
// Parse a Scryfall /cards/search response body and pull out the
|
||||
// `data[0].image_uris.normal` URL. Returns an error result when no
|
||||
// matching printing is found, when the JSON is malformed, or when the
|
||||
// entry has no top-level `image_uris` (double-faced cards expose them
|
||||
// on a face object - no fallback in this compatibility behavior either).
|
||||
static Result<std::string> parseResponse(const std::string& body);
|
||||
|
||||
private:
|
||||
IHttpClient& http_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
// MagicGameModule: IGameModule for Magic the Gathering. Owns its set source
|
||||
// and card preview source, reports the canonical "magic" subdirectory name
|
||||
// used on disk.
|
||||
|
||||
#include "ccm/games/IGameModule.hpp"
|
||||
#include "ccm/games/magic/MagicCardPreviewSource.hpp"
|
||||
#include "ccm/games/magic/MagicSetSource.hpp"
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class MagicGameModule final : public IGameModule {
|
||||
public:
|
||||
explicit MagicGameModule(IHttpClient& http);
|
||||
|
||||
[[nodiscard]] Game id() const noexcept override { return Game::Magic; }
|
||||
[[nodiscard]] std::string dirName() const override { return "magic"; }
|
||||
[[nodiscard]] std::string displayName() const override { return "Magic"; }
|
||||
|
||||
ISetSource& setSource() override { return setSource_; }
|
||||
ICardPreviewSource* cardPreviewSource() noexcept override { return &previewSource_; }
|
||||
|
||||
private:
|
||||
MagicSetSource setSource_;
|
||||
MagicCardPreviewSource previewSource_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// MagicSetSource: ISetSource implementation for Magic the Gathering.
|
||||
// Calls the Scryfall API at https://api.scryfall.com/sets, drops digital-only
|
||||
// sets, maps the response into our `Set` domain type, and sorts by release date
|
||||
// ascending. Behavior matches `magic/set_services.rs::update_sets`.
|
||||
|
||||
#include "ccm/games/IGameModule.hpp"
|
||||
#include "ccm/ports/IHttpClient.hpp"
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class MagicSetSource final : public ISetSource {
|
||||
public:
|
||||
static constexpr const char* kEndpoint = "https://api.scryfall.com/sets";
|
||||
|
||||
explicit MagicSetSource(IHttpClient& http);
|
||||
|
||||
Result<std::vector<Set>> fetchAll() override;
|
||||
|
||||
// Pure parser exposed for unit testing without a network round-trip.
|
||||
static Result<std::vector<Set>> parseResponse(const std::string& body);
|
||||
|
||||
private:
|
||||
IHttpClient& http_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
// PokemonCardPreviewSource: ICardPreviewSource implementation for the Pokemon
|
||||
// TCG. Calls the Pokemon TCG search endpoint at
|
||||
// https://api.pokemontcg.io/v2/cards?q=name:"<name>" set.id:<setId> number:<setNo>
|
||||
// and returns `data[0].images.large` (with `images.small` as a graceful
|
||||
// fallback). Mirrors the established `getImage` flow in
|
||||
// `src/components/pokemon/SelectedPokemonPanel.tsx`.
|
||||
|
||||
#include "ccm/ports/ICardPreviewSource.hpp"
|
||||
#include "ccm/ports/IHttpClient.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class PokemonCardPreviewSource final : public ICardPreviewSource {
|
||||
public:
|
||||
explicit PokemonCardPreviewSource(IHttpClient& http);
|
||||
|
||||
Result<std::string> fetchImageUrl(std::string_view name,
|
||||
std::string_view setId,
|
||||
std::string_view setNo) override;
|
||||
|
||||
// Build the fully URL-encoded Pokemon TCG search URL for the given card.
|
||||
// Exposed for unit testing and to keep encoding rules in one place.
|
||||
static std::string buildSearchUrl(std::string_view name,
|
||||
std::string_view setId,
|
||||
std::string_view setNo);
|
||||
|
||||
// Parse a Pokemon TCG /v2/cards response body and pull out the image URL
|
||||
// for the first matching card. Prefers `images.large`, falls back to
|
||||
// `images.small`, and returns an error result if neither is present, the
|
||||
// data array is empty, or the JSON is malformed.
|
||||
static Result<std::string> parseResponse(const std::string& body);
|
||||
|
||||
private:
|
||||
IHttpClient& http_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// PokemonGameModule: IGameModule for the Pokemon TCG. Owns its set source
|
||||
// and card preview source, both backed by api.pokemontcg.io/v2.
|
||||
|
||||
#include "ccm/games/IGameModule.hpp"
|
||||
#include "ccm/games/pokemon/PokemonCardPreviewSource.hpp"
|
||||
#include "ccm/games/pokemon/PokemonSetSource.hpp"
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class PokemonGameModule final : public IGameModule {
|
||||
public:
|
||||
explicit PokemonGameModule(IHttpClient& http);
|
||||
|
||||
[[nodiscard]] Game id() const noexcept override { return Game::Pokemon; }
|
||||
[[nodiscard]] std::string dirName() const override { return "pokemon"; }
|
||||
[[nodiscard]] std::string displayName() const override { return "Pokemon"; }
|
||||
|
||||
ISetSource& setSource() override { return setSource_; }
|
||||
ICardPreviewSource* cardPreviewSource() noexcept override { return &previewSource_; }
|
||||
|
||||
private:
|
||||
PokemonSetSource setSource_;
|
||||
PokemonCardPreviewSource previewSource_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
// PokemonSetSource: ISetSource implementation for the Pokemon TCG.
|
||||
// Calls the Pokemon TCG API at https://api.pokemontcg.io/v2/sets, maps the
|
||||
// response into our `Set` domain type, and sorts by release date ascending.
|
||||
// The Pokemon TCG API already returns `releaseDate` in `YYYY/MM/DD` format,
|
||||
// so no rewriting is needed (unlike Scryfall's `released_at`).
|
||||
// Behavior matches `pokemon/set_services.rs::update_sets`.
|
||||
|
||||
#include "ccm/games/IGameModule.hpp"
|
||||
#include "ccm/ports/IHttpClient.hpp"
|
||||
|
||||
namespace ccm {
|
||||
|
||||
class PokemonSetSource final : public ISetSource {
|
||||
public:
|
||||
static constexpr const char* kEndpoint = "https://api.pokemontcg.io/v2/sets";
|
||||
|
||||
explicit PokemonSetSource(IHttpClient& http);
|
||||
|
||||
Result<std::vector<Set>> fetchAll() override;
|
||||
|
||||
// Pure parser exposed for unit testing without a network round-trip.
|
||||
static Result<std::vector<Set>> parseResponse(const std::string& body);
|
||||
|
||||
private:
|
||||
IHttpClient& http_;
|
||||
};
|
||||
|
||||
} // namespace ccm
|
||||
Reference in New Issue
Block a user