Skip to main content

game_manager_lib/database/backup/
export_queries.rs

1//! Modulo para exportação de dados
2//!
3//! Este módulo concentra as funções responsáveis por ler os dados do SQLite e transformá-los nas estruturas para o backup.
4
5use crate::database::{current_schema_version, AppState};
6use crate::errors::AppError;
7use crate::models::{
8    Game, GameDataPath, GameDetails, GameExtras, Platform, SystemRequirements, WishlistGame,
9};
10use rusqlite::Connection;
11use tauri::State;
12
13/// Type alias para dados de backup
14pub type BackupDataTuple = (
15    Vec<Game>,
16    Vec<GameDetails>,
17    Vec<WishlistGame>,
18    Vec<GameExtras>,
19    Vec<SystemRequirements>,
20    Vec<GameDataPath>,
21    u32,
22);
23
24/// Função auxiliar interna para buscar dados do backup com transação ACID
25pub fn fetch_backup_data(state: &State<AppState>) -> Result<BackupDataTuple, AppError> {
26    let conn = state.games_db.lock()?;
27
28    // Inicia transação READ para consistência
29    conn.execute("BEGIN TRANSACTION", [])?;
30
31    let games = fetch_games(&conn)?;
32    let game_details = fetch_game_details(&conn)?;
33    let wishlist_game = fetch_wishlist(&conn)?;
34    let game_extras = fetch_game_extras(&conn)?;
35    let system_requirements = fetch_system_requirements(&conn)?;
36    let game_data_paths = fetch_game_data_paths(&conn)?;
37    let schema_version = current_schema_version(&conn)?;
38
39    conn.execute("COMMIT", [])?;
40
41    Ok((
42        games,
43        game_details,
44        wishlist_game,
45        game_extras,
46        system_requirements,
47        game_data_paths,
48        schema_version,
49    ))
50}
51
52/// Busca todos os jogos na biblioteca
53fn fetch_games(conn: &Connection) -> Result<Vec<Game>, AppError> {
54    let mut stmt = conn.prepare(
55        "SELECT id, name, slug, cover_url, platform, platform_game_id, installed, import_confidence, install_path, executable_path, launch_args, user_rating, favorite, status, playtime, playtime_source, last_played, added_at, alternative_names FROM games"
56    )?;
57
58    let game_iter = stmt.query_map([], |row| {
59        let alt_names_json: Option<String> = row.get(18)?;
60        let alternative_names = alt_names_json.and_then(|s| serde_json::from_str(&s).ok());
61
62        Ok(Game {
63            id: row.get(0)?,
64            name: row.get(1)?,
65            slug: row.get(2)?,
66            cover_url: row.get(3)?,
67            genres: None,
68            developer: None,
69            platform: row.get::<_, String>(4)?.parse().unwrap_or(Platform::Outra),
70            platform_game_id: row.get(5)?,
71            alternative_names,
72            installed: row.get(6)?,
73            import_confidence: row
74                .get::<_, Option<String>>(7)?
75                .and_then(|s| s.parse().ok()),
76            install_path: row.get(8)?,
77            executable_path: row.get(9)?,
78            launch_args: row.get(10)?,
79            user_rating: row.get(11)?,
80            favorite: row.get(12)?,
81            status: row.get(13)?,
82            playtime: row.get(14)?,
83            playtime_source: row
84                .get::<_, Option<String>>(15)?
85                .and_then(|s| s.parse().ok()),
86            last_played: row.get(16)?,
87            added_at: row.get(17)?,
88            is_adult: false,
89        })
90    })?;
91
92    Ok(game_iter.collect::<Result<Vec<_>, _>>()?)
93}
94
95/// Busca todos os detalhes dos jogos na biblioteca
96fn fetch_game_details(conn: &Connection) -> Result<Vec<GameDetails>, AppError> {
97    let mut stmt = conn.prepare(
98        "SELECT
99        game_id, steam_app_id, developer, publisher, release_date, genres, tags, series,
100        description_raw, description_ptbr, background_image, critic_score,
101        steam_review_label, steam_review_count, steam_review_score, steam_review_updated_at,
102        esrb_rating, is_adult, adult_tags, external_links, median_playtime,
103        estimated_playtime, updated_at
104     FROM game_details",
105    )?;
106
107    let details_iter = stmt.query_map([], |row| {
108        let links_json: Option<String> = row.get(19)?;
109        let external_links = links_json.and_then(|s| serde_json::from_str(&s).ok());
110
111        let tags_json: Option<String> = row.get(6)?;
112        let tags = tags_json.map(|s| crate::database::deserialize_tags(&s));
113
114        Ok(GameDetails {
115            game_id: row.get(0)?,
116            steam_app_id: row.get(1)?,
117            developer: row.get(2)?,
118            publisher: row.get(3)?,
119            release_date: row.get(4)?,
120            genres: row.get(5)?,
121            tags,
122            series: row.get(7)?,
123            description_raw: row.get(8)?,
124            description_ptbr: row.get(9)?,
125            background_image: row.get(10)?,
126            critic_score: row.get(11)?,
127            steam_review_label: row.get(12)?,
128            steam_review_count: row.get(13)?,
129            steam_review_score: row.get(14)?,
130            steam_review_updated_at: row.get(15)?,
131            esrb_rating: row.get(16)?,
132            is_adult: row.get(17).unwrap_or(false),
133            adult_tags: row.get(18)?,
134            external_links,
135            median_playtime: row.get(20)?,
136            estimated_playtime: row.get(21)?,
137            updated_at: row.get(22)?,
138        })
139    })?;
140
141    Ok(details_iter.collect::<Result<Vec<_>, _>>()?)
142}
143
144/// Busca todos os jogos da wishlist
145fn fetch_wishlist(conn: &Connection) -> Result<Vec<WishlistGame>, AppError> {
146    let mut stmt = conn.prepare(
147        "SELECT id, name, cover_url, store_url, store_platform, itad_id, current_price, normal_price, lowest_price, currency, on_sale, voucher, added_at FROM wishlist"
148    )?;
149
150    let wishlist_iter = stmt.query_map([], |row| {
151        Ok(WishlistGame {
152            id: row.get(0)?,
153            name: row.get(1)?,
154            cover_url: row.get(2)?,
155            store_url: row.get(3)?,
156            store_platform: row.get(4)?,
157            itad_id: row.get(5)?,
158            current_price: row.get(6)?,
159            normal_price: row.get(7)?,
160            lowest_price: row.get(8)?,
161            currency: row.get(9)?,
162            on_sale: row.get(10)?,
163            voucher: row.get(11)?,
164            added_at: row.get(12)?,
165        })
166    })?;
167
168    Ok(wishlist_iter.collect::<Result<Vec<_>, _>>()?)
169}
170
171/// Busca todos os dados técnicos do PCGamingWiki para backup
172fn fetch_game_extras(conn: &Connection) -> Result<Vec<GameExtras>, AppError> {
173    let mut stmt = conn.prepare(
174        "SELECT
175            steam_app_id, pcgw_page_id, pcgw_page_name, engine,
176            available_on,
177            dx_versions, vulkan_versions, opengl_versions,
178            win64, linux64, macos_arm, macos_intel64,
179            ray_tracing, upscaling, frame_gen,
180            ultrawidescreen, four_k_support, hdr, high_fps, fov, borderless_windowed, color_blind,
181            controller_support, full_controller, playstation_controllers, xinput_controllers,
182            surround_sound, subtitles, closed_captions,
183            has_save_data, has_config_data,
184            languages_interface, languages_audio, languages_subtitles,
185            fetched_at
186         FROM game_extras
187         WHERE fetched_at IS NOT NULL",
188    )?;
189
190    let parse_json_vec = |s: Option<String>| -> Option<Vec<String>> {
191        s.and_then(|v| serde_json::from_str(&v).ok())
192    };
193
194    let iter = stmt.query_map([], |row| {
195        Ok(GameExtras {
196            steam_app_id: row.get(0)?,
197            pcgw_page_id: row.get(1)?,
198            pcgw_page_name: row.get(2)?,
199            engine: row.get(3)?,
200            available_on: row.get(4)?,
201            dx_versions: row.get(5)?,
202            vulkan_versions: row.get(6)?,
203            opengl_versions: row.get(7)?,
204            win64: row.get(8)?,
205            linux64: row.get(9)?,
206            macos_arm: row.get(10)?,
207            macos_intel64: row.get(11)?,
208            ray_tracing: row.get(12)?,
209            upscaling: row.get(13)?,
210            frame_gen: row.get(14)?,
211            ultrawidescreen: row.get(15)?,
212            four_k_support: row.get(16)?,
213            hdr: row.get(17)?,
214            high_fps: row.get(18)?,
215            fov: row.get(19)?,
216            borderless_windowed: row.get(20)?,
217            color_blind: row.get(21)?,
218            controller_support: row.get(22)?,
219            full_controller: row.get(23)?,
220            playstation_controllers: row.get(24)?,
221            xinput_controllers: row.get(25)?,
222            surround_sound: row.get(26)?,
223            subtitles: row.get(27)?,
224            closed_captions: row.get(28)?,
225            has_save_data: row.get(29)?,
226            has_config_data: row.get(30)?,
227            languages_interface: parse_json_vec(row.get(31)?),
228            languages_audio: parse_json_vec(row.get(32)?),
229            languages_subtitles: parse_json_vec(row.get(33)?),
230            fetched_at: row.get(34)?,
231        })
232    })?;
233
234    Ok(iter.collect::<Result<Vec<_>, _>>()?)
235}
236
237/// Busca todos os requisitos de sistema para backup
238fn fetch_system_requirements(conn: &Connection) -> Result<Vec<SystemRequirements>, AppError> {
239    let mut stmt = conn.prepare(
240        "SELECT
241            steam_app_id, os_family, tier_title, target,
242            min_os, min_cpu, min_cpu2, min_ram, min_gpu, min_gpu2, min_vram, min_dx, min_storage,
243            rec_os, rec_cpu, rec_cpu2, rec_ram, rec_gpu, rec_gpu2, rec_vram, rec_dx, rec_storage
244         FROM system_requirements
245         ORDER BY steam_app_id, id ASC",
246    )?;
247
248    let iter = stmt.query_map([], |row| {
249        Ok(SystemRequirements {
250            steam_app_id: row.get(0)?,
251            os_family: row.get(1)?,
252            tier_title: row.get(2)?,
253            target: row.get(3)?,
254            min_os: row.get(4)?,
255            min_cpu: row.get(5)?,
256            min_cpu2: row.get(6)?,
257            min_ram: row.get(7)?,
258            min_gpu: row.get(8)?,
259            min_gpu2: row.get(9)?,
260            min_vram: row.get(10)?,
261            min_dx: row.get(11)?,
262            min_storage: row.get(12)?,
263            rec_os: row.get(13)?,
264            rec_cpu: row.get(14)?,
265            rec_cpu2: row.get(15)?,
266            rec_ram: row.get(16)?,
267            rec_gpu: row.get(17)?,
268            rec_gpu2: row.get(18)?,
269            rec_vram: row.get(19)?,
270            rec_dx: row.get(20)?,
271            rec_storage: row.get(21)?,
272        })
273    })?;
274
275    Ok(iter.collect::<Result<Vec<_>, _>>()?)
276}
277
278/// Busca todos os caminhos de game data para backup
279fn fetch_game_data_paths(conn: &Connection) -> Result<Vec<GameDataPath>, AppError> {
280    let mut stmt = conn.prepare(
281        "SELECT steam_app_id, kind, os, raw_path
282         FROM game_data_paths
283         ORDER BY steam_app_id, id ASC",
284    )?;
285
286    let iter = stmt.query_map([], |row| {
287        Ok(GameDataPath {
288            steam_app_id: row.get(0)?,
289            kind: row.get(1)?,
290            os: row.get(2)?,
291            raw_path: row.get(3)?,
292            expanded_path: None,
293        })
294    })?;
295
296    Ok(iter.collect::<Result<Vec<_>, _>>()?)
297}