game_manager_lib/commands/platforms/
core.rs1use crate::constants;
6use crate::database::AppState;
7use crate::errors::AppError;
8use crate::sources::scanner::GameDiscovery;
9use crate::utils::status_logic;
10use chrono::{TimeZone, Utc};
11use rusqlite::params;
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15#[derive(Serialize, Deserialize, Debug)]
18pub struct ScanResult {
19 pub success: bool,
20 pub message: String,
21 pub discoveries: Vec<GameDiscovery>,
22}
23
24#[derive(Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct ScanGameInput {
27 pub name: String,
28 pub executable_path: String,
29 pub base_path: String,
30}
31
32#[derive(Debug, Clone)]
34pub struct NewlyImportedGame {
35 pub game_id: String,
36 pub name: String,
37 pub platform: String,
38 pub platform_game_id: String,
39}
40
41pub(crate) async fn persist_source_games(
47 state: &AppState,
48 games: Vec<crate::sources::providers::SourceGame>,
49) -> Result<(u32, u32, Vec<NewlyImportedGame>), AppError> {
50 let mut conn = state.games_db.lock().map_err(|_| AppError::MutexError)?;
51
52 let tx = conn
54 .transaction()
55 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
56
57 let mut inserted = 0;
58 let mut updated = 0;
59 let mut newly_imported = Vec::new();
60 let now = Utc::now().to_rfc3339();
61
62 for game in games {
63 let exists: bool = tx
65 .query_row(
66 "SELECT EXISTS(SELECT 1 FROM games WHERE platform = ?1 AND platform_game_id = ?2)",
67 params![&game.platform, &game.platform_game_id],
68 |row| row.get(0),
69 )
70 .unwrap_or(false);
71
72 let status = status_logic::calculate_status(game.playtime_minutes.unwrap_or(0) as i32);
73
74 let last_played_iso = game.last_played.and_then(|ts| {
75 if ts > 0 {
76 Some(Utc.timestamp_opt(ts, 0).single().map(|dt| dt.to_rfc3339()))
77 } else {
78 None
79 }
80 });
81
82 if !exists {
83 let new_id = Uuid::new_v4().to_string();
84 let display_name = game.name.clone().unwrap_or_else(|| "Unknown".to_string());
85
86 let cover_url = if game.platform == "Steam" {
88 Some(format!(
89 "{}/{}",
90 constants::STEAM_CDN_URL,
91 constants::STEAM_LIBRARY_IMAGE_PATH.replace("{}", &game.platform_game_id)
92 ))
93 } else {
94 None
95 };
96
97 tx.execute(
98 "INSERT INTO games (
99 id, name, cover_url, platform, platform_game_id,
100 installed, status, playtime, last_played, added_at,
101 favorite, user_rating, install_path, executable_path
102 ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, 0, NULL, ?11, ?12)",
103 params![
104 new_id,
105 game.name.unwrap_or_else(|| "Unknown".to_string()),
106 cover_url,
107 game.platform,
108 game.platform_game_id,
109 game.installed,
110 status,
111 game.playtime_minutes.unwrap_or(0),
112 last_played_iso,
113 now,
114 game.install_path,
115 game.executable_path,
116 ],
117 )
118 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
119
120 newly_imported.push(NewlyImportedGame {
121 game_id: new_id,
122 name: display_name,
123 platform: game.platform.clone(),
124 platform_game_id: game.platform_game_id.clone(),
125 });
126
127 inserted += 1;
128 } else {
129 tx.execute(
130 "UPDATE games SET
131 installed = ?1,
132 status = ?2,
133 playtime = ?3,
134 last_played = ?4,
135 install_path = COALESCE(?5, install_path),
136 executable_path = COALESCE(?6, executable_path)
137 WHERE platform = ?7 AND platform_game_id = ?8",
138 params![
139 game.installed,
140 status,
141 game.playtime_minutes.unwrap_or(0),
142 last_played_iso,
143 game.install_path,
144 game.executable_path,
145 game.platform,
146 game.platform_game_id
147 ],
148 )
149 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
150
151 updated += 1;
152 }
153 }
154
155 tx.commit()
157 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
158
159 Ok((inserted, updated, newly_imported))
160}
161
162pub fn format_import_summary(platform: &str, inserted: u32, updated: u32) -> String {
166 format!("{platform}: {inserted} adicionados, {updated} atualizados")
167}
168
169pub fn format_import_empty(platform: &str) -> String {
171 format!("Nenhum jogo {platform} encontrado.")
172}
173
174pub fn format_login_success(platform: &str) -> String {
176 format!("Conta {platform} conectada com sucesso!")
177}