game_manager_lib/commands/metadata/
pcgamingwiki.rs1use crate::database::AppState;
8use crate::errors::AppError;
9use crate::models::{GameDataPath, GameExtras, SystemRequirements};
10use crate::services::integration::pcgamingwiki::client::{search_pcgw_by_name, PcgwSearchResult};
11use crate::services::integration::pcgamingwiki::db::{
12 get_game_data_paths, get_pcgw_data, get_system_requirements, invalidate_pcgw_data,
13 save_pcgw_data, save_scraped_data,
14};
15use crate::services::integration::pcgamingwiki::fetch::fetch_pcgw_data;
16use chrono::Utc;
17use tracing::warn;
18
19#[derive(Debug, serde::Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct PcgwScrapedResponse {
28 pub system_requirements: Vec<SystemRequirements>,
29 pub config_paths: Vec<GameDataPath>,
30 pub save_paths: Vec<GameDataPath>,
31}
32
33#[tauri::command]
45pub async fn get_or_fetch_pcgw_data(
46 steam_app_id: String,
47 conn: tauri::State<'_, AppState>,
48) -> Result<Option<GameExtras>, String> {
49 {
50 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
51 if let Some(data) = get_pcgw_data(&db, &steam_app_id) {
52 return Ok(Some(data));
53 }
54 }
55
56 match fetch_pcgw_data(&steam_app_id).await {
57 Ok((data, scraped)) => {
58 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
59 save_pcgw_data(&db, &data).map_err(|e| e.to_string())?;
60
61 if let Some(scraped_data) = scraped {
62 save_scraped_data(&db, &data.steam_app_id, &scraped_data)
63 .map_err(|e| e.to_string())?;
64 }
65
66 Ok(Some(data))
67 }
68 Err(AppError::NotFound(msg)) => {
70 warn!(
71 "pcgw_data: jogo {} não encontrado na PCGW — {}",
72 steam_app_id, msg
73 );
74 let empty = GameExtras {
75 steam_app_id: steam_app_id.clone(),
76 fetched_at: Some(Utc::now().to_rfc3339()),
77 ..Default::default()
78 };
79 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
80 save_pcgw_data(&db, &empty).map_err(|e| e.to_string())?;
81 Ok(None)
82 }
83 Err(AppError::NetworkError(msg)) => {
85 warn!("pcgw_data: falha de rede para {} — {}", steam_app_id, msg);
86 Ok(None)
87 }
88 Err(e) => Err(e.to_string()),
89 }
90}
91
92#[tauri::command]
97pub async fn refresh_pcgw_data(
98 steam_app_id: String,
99 conn: tauri::State<'_, AppState>,
100) -> Result<Option<GameExtras>, String> {
101 {
102 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
103 invalidate_pcgw_data(&db, &steam_app_id).map_err(|e| e.to_string())?;
104 }
105
106 get_or_fetch_pcgw_data(steam_app_id, conn).await
108}
109
110#[tauri::command]
119pub async fn search_pcgw_games(game_name: String) -> Result<Vec<PcgwSearchResult>, String> {
120 search_pcgw_by_name(&game_name)
121 .await
122 .map_err(|e| e.to_string())
123}
124
125#[tauri::command]
136pub async fn get_pcgw_scraped_data(
137 steam_app_id: String,
138 conn: tauri::State<'_, AppState>,
139) -> Result<Option<PcgwScrapedResponse>, String> {
140 if steam_app_id.trim().is_empty() {
141 return Ok(None);
142 }
143
144 {
146 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
147
148 if get_pcgw_data(&db, &steam_app_id).is_some() {
150 let sysreqs = get_system_requirements(&db, &steam_app_id);
151 let all_paths = get_game_data_paths(&db, &steam_app_id);
152 let (config_paths, save_paths) = split_paths(all_paths);
153 return Ok(Some(PcgwScrapedResponse {
154 system_requirements: sysreqs,
155 config_paths,
156 save_paths,
157 }));
158 }
159 }
160
161 match fetch_pcgw_data(&steam_app_id).await {
163 Ok((pcgw_data, scraped)) => {
164 let db = conn.games_db.lock().map_err(|e| e.to_string())?;
165
166 save_pcgw_data(&db, &pcgw_data).map_err(|e| e.to_string())?;
168
169 if let Some(scraped_data) = scraped {
170 save_scraped_data(&db, &steam_app_id, &scraped_data).map_err(|e| e.to_string())?;
171
172 let (config_paths, save_paths) = split_paths(scraped_data.game_data_paths);
173
174 return Ok(Some(PcgwScrapedResponse {
175 system_requirements: scraped_data.system_requirements,
176 config_paths,
177 save_paths,
178 }));
179 }
180
181 Ok(None)
183 }
184 Err(AppError::NotFound(_)) => Ok(None),
185 Err(AppError::NetworkError(msg)) => {
186 warn!("get_pcgw_scraped_data: falha de rede — {}", msg);
187 Ok(None)
188 }
189 Err(e) => Err(e.to_string()),
190 }
191}
192
193fn split_paths(paths: Vec<GameDataPath>) -> (Vec<GameDataPath>, Vec<GameDataPath>) {
197 let config = paths
198 .iter()
199 .filter(|p| p.kind == "config")
200 .cloned()
201 .collect();
202 let saves = paths
203 .iter()
204 .filter(|p| p.kind == "saves")
205 .cloned()
206 .collect();
207 (config, saves)
208}