game_manager_lib/commands/platforms/
legacy.rs1use crate::commands::platforms::core::{format_import_empty, format_import_summary};
4use crate::database::AppState;
5use crate::errors::AppError;
6use crate::sources::legacy::LegacySource;
7use crate::utils::status_logic;
8use chrono::Utc;
9use rusqlite::params;
10use tauri::{AppHandle, Emitter, State};
11use tracing::info;
12use uuid::Uuid;
13
14async fn persist_legacy_games(
19 state: &AppState,
20 games: Vec<crate::sources::legacy::LegacyGame>,
21) -> Result<(u32, u32), AppError> {
22 let mut conn = state.games_db.lock().map_err(|_| AppError::MutexError)?;
23 let tx = conn
24 .transaction()
25 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
26
27 let mut inserted = 0u32;
28 let mut updated = 0u32;
29 let now = Utc::now().to_rfc3339();
30
31 for legacy_game in games {
32 let game = &legacy_game.source;
33
34 let exists: bool = tx
35 .query_row(
36 "SELECT EXISTS(SELECT 1 FROM games WHERE platform = ?1 AND platform_game_id = ?2)",
37 params![&game.platform, &game.platform_game_id],
38 |row| row.get(0),
39 )
40 .unwrap_or(false);
41
42 let status = status_logic::calculate_status(game.playtime_minutes.unwrap_or(0) as i32);
43
44 if !exists {
45 let new_id = Uuid::new_v4().to_string();
46
47 tx.execute(
48 "INSERT INTO games (
49 id, name, cover_url, platform, platform_game_id,
50 installed, status, playtime, last_played, added_at,
51 favorite, user_rating, install_path, executable_path
52 ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, NULL, ?9, 0, NULL, ?10, ?11)",
53 params![
54 new_id,
55 game.name.as_deref().unwrap_or("Unknown"),
56 legacy_game.cover_url, game.platform,
58 game.platform_game_id,
59 game.installed,
60 status,
61 game.playtime_minutes.unwrap_or(0),
62 now,
63 game.install_path,
64 game.executable_path,
65 ],
66 )
67 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
68
69 if legacy_game.description_raw.is_some() {
71 tx.execute(
72 "INSERT OR IGNORE INTO game_details (game_id, description_raw)
73 VALUES (?1, ?2)",
74 params![new_id, legacy_game.description_raw],
75 )
76 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
77 }
78
79 inserted += 1;
80 } else {
81 tx.execute(
82 "UPDATE games SET
83 installed = ?1,
84 status = ?2,
85 install_path = COALESCE(?3, install_path),
86 executable_path = COALESCE(?4, executable_path)
87 WHERE platform = ?5 AND platform_game_id = ?6",
88 params![
89 game.installed,
90 status,
91 game.install_path,
92 game.executable_path,
93 game.platform,
94 game.platform_game_id,
95 ],
96 )
97 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
98
99 updated += 1;
100 }
101 }
102
103 tx.commit()
104 .map_err(|e| AppError::DatabaseError(e.to_string()))?;
105
106 Ok((inserted, updated))
107}
108
109#[tauri::command]
120pub async fn import_legacy_games(
121 app: AppHandle,
122 state: State<'_, AppState>,
123 app_state_path: Option<String>,
124 wine_prefix: Option<String>,
125) -> Result<String, AppError> {
126 let path = app_state_path
127 .filter(|s| !s.trim().is_empty())
128 .map(std::path::PathBuf::from);
129
130 let prefix = wine_prefix
131 .filter(|s| !s.trim().is_empty())
132 .map(std::path::PathBuf::from);
133
134 let source = LegacySource::new_with_wine(path, prefix);
135 let games = source.fetch_games_detailed().await?;
136
137 if games.is_empty() {
138 return Ok(format_import_empty("Legacy Games"));
139 }
140
141 let (inserted, updated) = persist_legacy_games(&state, games).await?;
142 let message = format_import_summary("Legacy Games", inserted, updated);
143 info!("{}", message);
144
145 let _ = app.emit("library_updated", ());
146
147 Ok(message)
148}