Skip to main content

game_manager_lib/
models.rs

1//! Modelos de dados do banco de dados.
2//!
3//! Define as structs que representam as tabelas do banco:
4//! - Game: tabela `games`
5//! - GameDetails: tabela `game_details`
6//! - WishlistGame: tabela `wishlist`
7//! - Subscriptions: tabela `subscriptions`
8//! - PcgwData: tabela `pcgw_data`
9
10// Reexportar GameTag do utils para consistência
11pub use crate::utils::tag_utils::GameTag;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::str::FromStr;
15
16// === ENUMS ===
17
18/// Nível de confiança da importação do jogo
19#[derive(Debug, Serialize, Deserialize, Clone)]
20pub enum ImportConfidence {
21    High,
22    Medium,
23    Low,
24}
25
26impl FromStr for ImportConfidence {
27    type Err = ();
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29        match s {
30            "High" => Ok(ImportConfidence::High),
31            "Medium" => Ok(ImportConfidence::Medium),
32            "Low" => Ok(ImportConfidence::Low),
33            _ => Err(()),
34        }
35    }
36}
37
38impl std::fmt::Display for ImportConfidence {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        let s = match self {
41            ImportConfidence::High => "High",
42            ImportConfidence::Medium => "Medium",
43            ImportConfidence::Low => "Low",
44        };
45        write!(f, "{}", s)
46    }
47}
48
49/// Plataformas suportadas
50#[derive(Debug, Serialize, Deserialize, Clone)]
51pub enum Platform {
52    Steam,
53    Epic,
54    GOG,
55    Amazon,
56    Ubisoft,
57    EA,
58    BattleNet,
59    Xbox,
60    LegacyGames,
61    Indiegala,
62    Itch,
63    Indie,
64    Outra,
65}
66
67impl FromStr for Platform {
68    type Err = ();
69    fn from_str(s: &str) -> Result<Self, Self::Err> {
70        match s {
71            "Steam" => Ok(Platform::Steam),
72            "Epic" => Ok(Platform::Epic),
73            "GOG" => Ok(Platform::GOG),
74            "Amazon" => Ok(Platform::Amazon),
75            "Ubisoft" => Ok(Platform::Ubisoft),
76            "EA" => Ok(Platform::EA),
77            "BattleNet" => Ok(Platform::BattleNet),
78            "Xbox" => Ok(Platform::Xbox),
79            "LegacyGames" => Ok(Platform::LegacyGames),
80            "Indiegala" => Ok(Platform::Indiegala),
81            "Itch" => Ok(Platform::Itch),
82            "Indie" => Ok(Platform::Indie),
83            "Outra" => Ok(Platform::Outra),
84            _ => Err(()),
85        }
86    }
87}
88
89impl std::fmt::Display for Platform {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        let s = match self {
92            Platform::Steam => "Steam",
93            Platform::Epic => "Epic",
94            Platform::GOG => "GOG",
95            Platform::Amazon => "Amazon",
96            Platform::EA => "EA",
97            Platform::Ubisoft => "Ubisoft",
98            Platform::BattleNet => "BattleNet",
99            Platform::Xbox => "Xbox",
100            Platform::LegacyGames => "LegacyGames",
101            Platform::Indiegala => "Indiegala",
102            Platform::Itch => "Itch",
103            Platform::Indie => "Indie",
104            Platform::Outra => "Outra",
105        };
106        write!(f, "{}", s)
107    }
108}
109
110#[derive(Debug, Serialize, Deserialize, Clone)]
111#[serde(rename_all = "camelCase")]
112pub enum PlaytimeSource {
113    Platform(Platform), // API oficial (Steam, Itch, Indiegala)
114    Local,              // tracker local via processo
115}
116
117impl PlaytimeSource {
118    /// Serializa para o formato salvo na coluna `playtime_source` (TEXT).
119    pub fn as_db_str(&self) -> String {
120        match self {
121            PlaytimeSource::Local => "local".to_string(),
122            PlaytimeSource::Platform(p) => p.to_string(),
123        }
124    }
125}
126
127impl FromStr for PlaytimeSource {
128    type Err = ();
129    fn from_str(s: &str) -> Result<Self, Self::Err> {
130        if s == "local" {
131            Ok(PlaytimeSource::Local)
132        } else {
133            s.parse::<Platform>().map(PlaytimeSource::Platform)
134        }
135    }
136}
137
138// === Modelos de Dados ===
139
140/// Jogo na biblioteca do usuário.
141///
142/// Representa um jogo adicionado à biblioteca pessoal, com metadados
143/// importados da plataforma e dados de progresso do usuário.
144#[derive(Debug, Serialize, Deserialize, Clone)]
145#[serde(rename_all = "camelCase")]
146pub struct Game {
147    pub id: String,
148    pub name: String,
149    pub slug: String,
150    pub cover_url: Option<String>,
151    pub genres: Option<String>,
152    pub developer: Option<String>,
153    pub platform: Platform,
154    pub platform_game_id: String,
155    pub alternative_names: Option<Vec<String>>,
156
157    // Execução
158    pub installed: bool,
159    pub install_path: Option<String>,
160    pub executable_path: Option<String>,
161    pub launch_args: Option<String>,
162    pub import_confidence: Option<ImportConfidence>,
163
164    // Dados do Usuário
165    pub user_rating: Option<i32>,
166    pub favorite: bool,
167    pub status: Option<String>,
168    pub playtime: Option<i32>,
169    pub playtime_source: Option<PlaytimeSource>,
170
171    // Metadados de Tempo
172    pub last_played: Option<String>,
173    pub added_at: String,
174
175    // Conteúdo Adulto
176    #[serde(default)]
177    pub is_adult: bool,
178}
179
180/// Detalhes adicionais do jogo (Schema v3).
181///
182/// Contém metadados enriquecidos obtidos de APIs externas como RAWG,
183/// substituindo e expandindo os dados anteriores.
184#[derive(Debug, Serialize, Deserialize)]
185#[serde(rename_all = "camelCase")]
186pub struct GameDetails {
187    pub game_id: String,
188    pub steam_app_id: Option<String>,
189
190    // Metadados Básicos
191    pub developer: Option<String>,
192    pub publisher: Option<String>,
193    pub release_date: Option<String>,
194
195    // Categorização
196    pub genres: Option<String>,
197    pub tags: Option<Vec<GameTag>>,
198    pub series: Option<String>,
199
200    // Descrição
201    pub description_raw: Option<String>,
202    pub description_ptbr: Option<String>,
203
204    // Mídia
205    pub background_image: Option<String>,
206
207    // Scores e Avaliações
208    pub critic_score: Option<i32>,
209
210    // Steam Reviews
211    pub steam_review_label: Option<String>,
212    pub steam_review_count: Option<i32>,
213    pub steam_review_score: Option<f32>,
214    pub steam_review_updated_at: Option<String>,
215
216    // Conteúdo Adulto
217    pub esrb_rating: Option<String>,
218    pub is_adult: bool,
219    pub adult_tags: Option<String>,
220
221    // Links Externos
222    pub external_links: Option<HashMap<String, String>>,
223
224    // Tempo de jogo
225    pub median_playtime: Option<i32>, // Alternativa para HLTB (média da Steam)
226    pub estimated_playtime: Option<f32>, // Estimativa de tempo de jogo
227
228    // Timestamps
229    pub updated_at: Option<String>,
230}
231
232/// Jogo na lista de desejos (wishlist) com tracking de preços.
233///
234/// Representa um jogo adicionado à wishlist do usuário,
235/// incluindo informações de preço, disponibilidade e vouchers.
236#[derive(Debug, Serialize, Deserialize)]
237#[serde(rename_all = "camelCase")]
238pub struct WishlistGame {
239    pub id: String,
240    pub name: String,
241    pub cover_url: Option<String>,
242    pub store_url: Option<String>,
243    pub store_platform: Option<String>,
244    pub itad_id: Option<String>,
245    pub current_price: Option<f64>,
246    pub normal_price: Option<f64>,
247    pub lowest_price: Option<f64>,
248    pub currency: Option<String>,
249    pub on_sale: bool,
250    pub voucher: Option<String>,
251    pub added_at: Option<String>,
252}
253
254/// Assinatura de serviço de jogos rastreada pelo usuário.
255///
256/// Representa um serviço como Game Pass, Prime Gaming ou EA Play,
257/// com controle de ativação e timestamp da última sincronização.
258#[derive(Debug, Serialize, Deserialize, Clone)]
259pub struct Subscription {
260    pub service: String, // EX: "game_pass", "prime_gaming", "ea_play", etc.
261    pub enabled: bool,
262    // Timestamp ISO 8601 da última sincronização com a API do serviço. `None` se nunca foi sincronizado.
263    #[serde(rename = "lastSynced")]
264    pub last_synced: Option<String>,
265}
266
267/// Dados técnicos do jogo obtidos do PCGamingWiki.
268///
269/// Tratados como dados estáticos do jogo — não expiram automaticamente.
270/// Atualizados apenas por invalidação explícita.
271///
272/// Tabelas Cargo consultadas: Infobox_game, API, Video, Input, Audio, L10n, Tags.
273#[derive(Debug, Serialize, Deserialize, Default)]
274#[serde(rename_all = "camelCase")]
275pub struct GameExtras {
276    pub steam_app_id: String,
277    pub pcgw_page_id: Option<String>,
278    pub pcgw_page_name: Option<String>,
279    pub engine: Option<String>,
280
281    // Plataformas suportadas — string separada por vírgula, ex: "Windows,Linux"
282    pub available_on: Option<String>,
283
284    // Graphics APIs (tabela API)
285    pub dx_versions: Option<String>, // ex: "11, 12"
286    pub vulkan_versions: Option<String>,
287    pub opengl_versions: Option<String>,
288
289    // Executáveis por OS — confirma suporte real (tabela API)
290    pub win64: Option<String>,
291    pub linux64: Option<String>,
292    #[serde(rename = "macOsArm")]
293    pub macos_arm: Option<String>,
294    #[serde(rename = "macOsIntel64")]
295    pub macos_intel64: Option<String>,
296
297    // Tecnologias gráficas (tabela Video)
298    pub ray_tracing: Option<String>,
299    pub upscaling: Option<String>, // ex: "DLSS 3,FSR 3,XeSS"
300    pub frame_gen: Option<String>, // ex: "DLSS Frame Generation"
301
302    // Display (tabela Video)
303    pub ultrawidescreen: Option<String>,
304    pub four_k_support: Option<String>,
305    pub hdr: Option<String>,
306    pub high_fps: Option<String>, // 120fps+
307    pub fov: Option<String>,      // FOV ajustável
308    pub borderless_windowed: Option<String>,
309    pub color_blind: Option<String>,
310
311    // Controle (tabela Input)
312    pub controller_support: Option<String>,
313    pub full_controller: Option<String>,
314    pub playstation_controllers: Option<String>,
315    pub xinput_controllers: Option<String>,
316
317    // Áudio (tabela Audio)
318    pub surround_sound: Option<String>,
319    pub subtitles: Option<String>,
320    pub closed_captions: Option<String>,
321
322    // Presença de dados (tabela Tags)
323    pub has_save_data: Option<String>,
324    pub has_config_data: Option<String>,
325
326    // Idiomas (tabela L10n) — JSON arrays
327    pub languages_interface: Option<Vec<String>>,
328    pub languages_audio: Option<Vec<String>>,
329    pub languages_subtitles: Option<Vec<String>>,
330
331    pub fetched_at: Option<String>,
332}
333
334/// Requisitos de sistema para um único OS/tier.
335///
336/// Campos `cpu2` e `gpu2` capturam alternativas AMD/Intel quando presentes.
337#[derive(Debug, Clone, Serialize, Deserialize, Default)]
338#[serde(rename_all = "camelCase")]
339pub struct SystemRequirements {
340    pub steam_app_id: String,
341    pub os_family: String, // OS alvo: "Windows", "Linux", "Mac OS", "DOS", etc.
342    pub tier_title: Option<String>, // Rótulo do tier — `None` para padrão (min/rec). Ex: `Some("High")`, `Some("Ultra (1440p)")`.
343    pub target: Option<String>,     // Ex: "1080p, DX11".
344
345    // Mínimo
346    pub min_os: Option<String>,
347    pub min_cpu: Option<String>,
348    pub min_cpu2: Option<String>,
349    pub min_ram: Option<String>,
350    pub min_gpu: Option<String>,
351    pub min_gpu2: Option<String>,
352    pub min_vram: Option<String>,
353    pub min_dx: Option<String>,
354    pub min_storage: Option<String>,
355
356    // Recomendado
357    pub rec_os: Option<String>,
358    pub rec_cpu: Option<String>,
359    pub rec_cpu2: Option<String>,
360    pub rec_ram: Option<String>,
361    pub rec_gpu: Option<String>,
362    pub rec_gpu2: Option<String>,
363    pub rec_vram: Option<String>,
364    pub rec_dx: Option<String>,
365    pub rec_storage: Option<String>,
366}
367
368/// Caminho de dado do jogo (save ou config) para um OS específico.
369#[derive(Debug, Clone, Serialize, Deserialize)]
370#[serde(rename_all = "camelCase")]
371pub struct GameDataPath {
372    pub steam_app_id: String,
373    pub kind: String,                  // Tipo do dado: `"config"` ou `"saves"`.
374    pub os: String,                    // OS alvo: `"Windows"`, `"Linux"`, `"OS X"`, etc.
375    pub raw_path: String, // Caminho bruto preservando `{{p|variavel}}`. Ex: `"{{p|userprofile\\Documents}}\\Reus\\"`.
376    pub expanded_path: Option<String>, // Caminho com variáveis expandidas. `None` até que o frontend expanda `{{p|...}}`.
377}
378
379/// Resultado completo do scraping de uma página do PCGamingWiki.
380#[derive(Debug, Default, Serialize, Deserialize)]
381#[serde(rename_all = "camelCase")]
382pub struct PcgwScrapedData {
383    pub system_requirements: Vec<SystemRequirements>,
384    pub game_data_paths: Vec<GameDataPath>,
385}