6acae54f80
Game-independent owned-inventory query (services::inventory::{OwnedItemQuery,
apply_query} + a Quality tier) that filters (AND) -> orders deterministically
(effective_overall desc, owned_card_id asc) -> paginates, wired into
GET /collection. Fixes the FIFA17 My Squad search: the Python oracle applied
only league+team and ignored level/rare/position/nation/start/count (proven by
response sha256 identity across pages -> the request-amplification bug); Core
now applies all proven filters and paginates. rare=SP left UNKNOWN.
Tests: +9 inventory unit, +14 /collection integration (full matrix incl. the
repeated-first-page regression); 9 mutations killed.
Isolated from an unrelated dirty working tree via a clean worktree at eab522a;
touches only the 5 slice files, no unrelated reformatting.
90 lines
2.3 KiB
Rust
90 lines
2.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum Rarity {
|
|
#[default]
|
|
Bronze,
|
|
Silver,
|
|
Gold,
|
|
RareGold,
|
|
Totw,
|
|
Hero,
|
|
Icon,
|
|
}
|
|
|
|
impl Rarity {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Rarity::Bronze => "bronze",
|
|
Rarity::Silver => "silver",
|
|
Rarity::Gold => "gold",
|
|
Rarity::RareGold => "raregold",
|
|
Rarity::Totw => "totw",
|
|
Rarity::Hero => "hero",
|
|
Rarity::Icon => "icon",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Visual card quality tier (gold/silver/bronze).
|
|
///
|
|
/// Game-independent semantic dimension, kept distinct from `Rarity` (which also
|
|
/// carries special-card programs like TOTW/Hero/Icon). Derived from a card's base
|
|
/// overall using FIFA 17's proven tier convention: gold >= 75, silver >= 65,
|
|
/// otherwise bronze (evidence: `fifa17-recon/tools/fut_cards.py` `tier()`).
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum Quality {
|
|
Bronze,
|
|
Silver,
|
|
Gold,
|
|
}
|
|
|
|
impl Quality {
|
|
/// Classify a base overall rating into its quality tier.
|
|
pub fn from_overall(overall: u8) -> Self {
|
|
if overall >= 75 {
|
|
Quality::Gold
|
|
} else if overall >= 65 {
|
|
Quality::Silver
|
|
} else {
|
|
Quality::Bronze
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A card definition loaded from JSON data files.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CardDefinition {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub overall: u8,
|
|
pub position: String,
|
|
pub nation: String,
|
|
pub league: String,
|
|
pub club: String,
|
|
pub pace: u8,
|
|
pub shooting: u8,
|
|
pub passing: u8,
|
|
pub dribbling: u8,
|
|
pub defending: u8,
|
|
pub physical: u8,
|
|
pub rarity: Rarity,
|
|
pub image_path: Option<String>,
|
|
}
|
|
|
|
/// A card instance owned by a club (stored in DB).
|
|
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
|
pub struct OwnedCard {
|
|
pub id: String,
|
|
pub club_id: String,
|
|
pub card_id: String,
|
|
pub is_loan: bool,
|
|
pub loan_matches_remaining: Option<i64>,
|
|
pub acquired_at: String,
|
|
pub chemistry_style: String,
|
|
pub position_override: Option<String>,
|
|
pub training_bonus: i64,
|
|
}
|