minor: yugioh support added

This commit is contained in:
Sebastian Dine
2026-05-09 19:32:18 +02:00
committed by GitHub
parent 6f575f4cec
commit 6ff4406638
68 changed files with 4994 additions and 134 deletions
+57 -3
View File
@@ -12,9 +12,38 @@
#include <string>
#include <string_view>
#include <vector>
namespace ccm {
struct AutoDetectedPrint {
std::string setNo;
std::string rarity;
};
// Classified error returned by ICardPreviewSource::fetchImageUrl. The kind
// drives caching policy in CardPreviewService:
//
// NotFound -- the upstream answered cleanly that the card has no image
// (or no matching record at all). Safe to remember: the
// answer will not change until the user edits the card
// record itself, which automatically invalidates the cache
// key. Negative-cached so subsequent selections show the
// fallback card-back instantly without another HTTP call.
//
// Transient -- the upstream did not answer cleanly (HTTP / network /
// timeout failure, malformed response, parse error). The
// record may well have an image; we just couldn't see it
// this time. NOT cached, so the next selection retries.
//
// The `message` is opaque to the service and is forwarded to the UI as
// the existing free-form `Result<std::string>::error()` string.
struct PreviewLookupError {
enum class Kind { NotFound, Transient };
Kind kind{Kind::Transient};
std::string message;
};
class ICardPreviewSource {
public:
virtual ~ICardPreviewSource() = default;
@@ -22,9 +51,34 @@ public:
// Resolve the preview image URL for a single card. `setNo` is optional
// (empty string is fine); some game APIs (e.g. Pokemon TCG) can use it as
// a more precise lookup key, others (Magic/Scryfall) ignore it.
virtual Result<std::string> fetchImageUrl(std::string_view name,
std::string_view setId,
std::string_view setNo) = 0;
//
// Errors carry a classification (`PreviewLookupError::Kind`) so
// CardPreviewService can decide whether to remember the miss
// (`NotFound`) or retry on the next call (`Transient`). See the doc
// comment on PreviewLookupError above for the exact contract.
virtual Result<std::string, PreviewLookupError>
fetchImageUrl(std::string_view name,
std::string_view setId,
std::string_view setNo) = 0;
// Opt-in switch for per-game print metadata detection.
[[nodiscard]] virtual bool supportsAutoDetectPrint() const noexcept { return false; }
// Optional metadata lookup used by game-specific edit dialogs. The default
// implementation returns an explicit "unsupported" error so games without
// print metadata APIs do not need to override it.
virtual Result<AutoDetectedPrint> detectFirstPrint(std::string_view /*name*/,
std::string_view /*setId*/) {
return Result<AutoDetectedPrint>::err("Auto-detect not supported by this game.");
}
// Optional listing of every distinct `(set_code, rarity)` print returned by
// the upstream for an exact card name inside the chosen display set.
virtual Result<std::vector<AutoDetectedPrint>>
detectPrintVariants(std::string_view /*name*/, std::string_view /*setId*/) {
return Result<std::vector<AutoDetectedPrint>>::err(
"Print variant listing not supported by this game.");
}
};
} // namespace ccm
@@ -0,0 +1,75 @@
#pragma once
// IPreviewByteCache - persistent byte cache used by CardPreviewService to
// keep preview images alive across app restarts.
//
// The cache is keyed by an opaque string. CardPreviewService composes the
// key from `(game, name, setId, setNo)` (preview lookups) or directly from
// the URL (per-game card-back fallback fetches); the cache itself does not
// interpret the key, only stores the byte payload behind it.
//
// Two kinds of entries are persisted:
//
// * Positive entries hold raw image bytes. Stored via `store(key, payload)`,
// returned as `LoadResult{HitKind::Hit, payload}`.
// * Negative entries record "we tried to resolve this exact card and the
// upstream answered cleanly that it has no preview image" - i.e. the
// `NotFound` half of `PreviewLookupError`. Stored via
// `storeNegative(key)`, returned as `LoadResult{HitKind::NegativeHit, {}}`.
// `Transient` errors (HTTP / network / parse failures) must NEVER reach
// this cache: we cannot tell whether the record genuinely has no image
// or just couldn't be reached, and persisting the miss would leave the
// user staring at the card-back placeholder until they edit the card.
//
// A negative entry is implicitly invalidated when the cache key changes -
// since the key includes `(game, name, setId, setNo)` (with game-specific
// disambiguators packed into setNo), any edit that affects a lookup-relevant
// field will hit a fresh key and re-attempt the network lookup automatically.
//
// Implementations must be thread-safe with respect to concurrent load/store
// calls because CardPreviewService is invoked from a worker thread spawned
// by `BaseSelectedCardPanel`.
//
// Errors are intentionally swallowed (load returns Miss; store and
// storeNegative are fire-and-forget). A flaky or full disk must never break
// the preview path - in the worst case the user sees the same speed as a
// fresh app install.
#include <string>
#include <string_view>
namespace ccm {
class IPreviewByteCache {
public:
enum class HitKind {
Miss, // no entry for this key (or unrecoverable I/O error)
Hit, // positive entry; bytes are in `payload`
NegativeHit, // negative entry; `payload` is empty by contract
};
struct LoadResult {
HitKind kind{HitKind::Miss};
std::string payload; // only meaningful when kind == Hit
};
virtual ~IPreviewByteCache() = default;
// Returns the cached entry for `key`. On any error - missing files,
// sidecar mismatch, malformed metadata, I/O failure - implementations
// must report `HitKind::Miss` rather than surfacing the error.
[[nodiscard]] virtual LoadResult load(std::string_view key) = 0;
// Best-effort persist of `payload` under `key`. Empty payloads are not
// stored as positive entries. If a negative entry already exists for
// this key it is replaced. Errors are swallowed.
virtual void store(std::string_view key, const std::string& payload) = 0;
// Best-effort persist of "we tried, upstream cleanly said no image".
// If a positive entry already exists for this key it is replaced.
// Errors are swallowed. Must be invoked ONLY for `NotFound`-class
// outcomes; never for transient failures.
virtual void storeNegative(std::string_view key) = 0;
};
} // namespace ccm