mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-09-04 17:23:29 +00:00
minor: yugioh support added
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user