Skip to content

Commit b70a0f6

Browse files
committed
feat: run legacy cover migrations at startup
Add a new utils::legacy_migration module to migrate legacy "covers" from the portable resources directory into the current data directory, resolving duplicates by modification time and removing emptied legacy folders. Refactor get_system_data_dir to use a single identifier ("com.reinamanager.dev"). Misc small cleanups
1 parent 3d2dc37 commit b70a0f6

6 files changed

Lines changed: 258 additions & 97 deletions

File tree

src-tauri/reina-path/src/lib.rs

Lines changed: 79 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,79 @@
1-
use std::path::PathBuf;
2-
3-
/// 数据库相关路径常量
4-
pub const DB_DATA_DIR: &str = "data";
5-
pub const DB_FILE_NAME: &str = "reina_manager.db";
6-
pub const DB_BACKUP_SUBDIR: &str = "backups";
7-
pub const RESOURCE_DIR: &str = "resources";
8-
9-
/// 判断是否处于便携模式(纯 Rust 版本)
10-
///
11-
/// 检测逻辑:检查可执行文件同级目录下是否存在 resources/data/reina_manager.db
12-
pub fn is_portable_mode() -> bool {
13-
if let Ok(exe_path) = std::env::current_exe() {
14-
if let Some(exe_dir) = exe_path.parent() {
15-
let portable_data_dir = exe_dir.join(RESOURCE_DIR).join(DB_DATA_DIR);
16-
let portable_db_file = portable_data_dir.join(DB_FILE_NAME);
17-
return portable_data_dir.exists() && portable_db_file.exists();
18-
}
19-
}
20-
false
21-
}
22-
23-
/// 获取基础数据目录(纯 Rust 版本)
24-
pub fn get_base_data_dir() -> Result<PathBuf, String> {
25-
if is_portable_mode() {
26-
// 便携模式:使用可执行文件所在目录的 resources 子目录
27-
let exe_path =
28-
std::env::current_exe().map_err(|e| format!("无法获取可执行文件路径: {}", e))?;
29-
let exe_dir = exe_path
30-
.parent()
31-
.ok_or_else(|| "无法获取可执行文件父目录".to_string())?;
32-
Ok(exe_dir.join(RESOURCE_DIR))
33-
} else {
34-
// 标准模式:使用系统应用数据目录
35-
get_system_data_dir()
36-
}
37-
}
38-
39-
/// 获取系统数据目录(跨平台)
40-
fn get_system_data_dir() -> Result<PathBuf, String> {
41-
use directories::BaseDirs;
42-
43-
let base_dirs = BaseDirs::new().ok_or_else(|| "无法获取系统目录信息".to_string())?;
44-
45-
#[cfg(target_os = "windows")]
46-
{
47-
Ok(base_dirs.data_dir().join("com.reinamanager.dev"))
48-
}
49-
50-
#[cfg(target_os = "macos")]
51-
{
52-
Ok(base_dirs.data_dir().join("com.reinamanager.dev"))
53-
}
54-
55-
#[cfg(target_os = "linux")]
56-
{
57-
Ok(base_dirs.data_dir().join("reina-manager"))
58-
}
59-
}
60-
61-
/// 获取数据库文件路径
62-
pub fn get_db_path() -> Result<PathBuf, String> {
63-
Ok(get_base_data_dir()?.join(DB_DATA_DIR).join(DB_FILE_NAME))
64-
}
65-
66-
/// 获取指定模式的数据库目录
67-
pub fn get_base_data_dir_for_mode(portable: bool) -> Result<PathBuf, String> {
68-
if portable {
69-
let exe_path =
70-
std::env::current_exe().map_err(|e| format!("无法获取可执行文件路径: {}", e))?;
71-
let exe_dir = exe_path
72-
.parent()
73-
.ok_or_else(|| "无法获取可执行文件父目录".to_string())?;
74-
Ok(exe_dir.join(RESOURCE_DIR))
75-
} else {
76-
get_system_data_dir()
77-
}
78-
}
79-
80-
/// 获取默认的数据库备份路径
81-
pub fn get_default_db_backup_path() -> Result<PathBuf, String> {
82-
Ok(get_base_data_dir()?
83-
.join(DB_DATA_DIR)
84-
.join(DB_BACKUP_SUBDIR))
85-
}
86-
87-
/// 获取默认的存档备份路径
88-
pub fn get_default_savedata_backup_path() -> Result<PathBuf, String> {
89-
Ok(get_base_data_dir()?.join("backups"))
90-
}
1+
use std::path::PathBuf;
2+
3+
/// 数据库相关路径常量
4+
pub const DB_DATA_DIR: &str = "data";
5+
pub const DB_FILE_NAME: &str = "reina_manager.db";
6+
pub const DB_BACKUP_SUBDIR: &str = "backups";
7+
pub const RESOURCE_DIR: &str = "resources";
8+
9+
/// 判断是否处于便携模式(纯 Rust 版本)
10+
///
11+
/// 检测逻辑:检查可执行文件同级目录下是否存在 resources/data/reina_manager.db
12+
pub fn is_portable_mode() -> bool {
13+
if let Ok(exe_path) = std::env::current_exe() {
14+
if let Some(exe_dir) = exe_path.parent() {
15+
let portable_data_dir = exe_dir.join(RESOURCE_DIR).join(DB_DATA_DIR);
16+
let portable_db_file = portable_data_dir.join(DB_FILE_NAME);
17+
return portable_data_dir.exists() && portable_db_file.exists();
18+
}
19+
}
20+
false
21+
}
22+
23+
/// 获取基础数据目录(纯 Rust 版本)
24+
pub fn get_base_data_dir() -> Result<PathBuf, String> {
25+
if is_portable_mode() {
26+
// 便携模式:使用可执行文件所在目录的 resources 子目录
27+
let exe_path =
28+
std::env::current_exe().map_err(|e| format!("无法获取可执行文件路径: {}", e))?;
29+
let exe_dir = exe_path
30+
.parent()
31+
.ok_or_else(|| "无法获取可执行文件父目录".to_string())?;
32+
Ok(exe_dir.join(RESOURCE_DIR))
33+
} else {
34+
// 标准模式:使用系统应用数据目录
35+
get_system_data_dir()
36+
}
37+
}
38+
39+
/// 获取系统数据目录(跨平台)
40+
fn get_system_data_dir() -> Result<PathBuf, String> {
41+
use directories::BaseDirs;
42+
43+
let identifier = "com.reinamanager.dev";
44+
45+
let base_dirs = BaseDirs::new().ok_or_else(|| "无法获取系统目录信息".to_string())?;
46+
47+
Ok(base_dirs.data_dir().join(identifier))
48+
}
49+
50+
/// 获取数据库文件路径
51+
pub fn get_db_path() -> Result<PathBuf, String> {
52+
Ok(get_base_data_dir()?.join(DB_DATA_DIR).join(DB_FILE_NAME))
53+
}
54+
55+
/// 获取指定模式的数据库目录
56+
pub fn get_base_data_dir_for_mode(portable: bool) -> Result<PathBuf, String> {
57+
if portable {
58+
let exe_path =
59+
std::env::current_exe().map_err(|e| format!("无法获取可执行文件路径: {}", e))?;
60+
let exe_dir = exe_path
61+
.parent()
62+
.ok_or_else(|| "无法获取可执行文件父目录".to_string())?;
63+
Ok(exe_dir.join(RESOURCE_DIR))
64+
} else {
65+
get_system_data_dir()
66+
}
67+
}
68+
69+
/// 获取默认的数据库备份路径
70+
pub fn get_default_db_backup_path() -> Result<PathBuf, String> {
71+
Ok(get_base_data_dir()?
72+
.join(DB_DATA_DIR)
73+
.join(DB_BACKUP_SUBDIR))
74+
}
75+
76+
/// 获取默认的存档备份路径
77+
pub fn get_default_savedata_backup_path() -> Result<PathBuf, String> {
78+
Ok(get_base_data_dir()?.join("backups"))
79+
}

src-tauri/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use utils::{
1515
},
1616
game_cover::{delete_cloud_cache, register_game_cover_protocol},
1717
launch::{launch_game, stop_game},
18+
legacy_migration::run_startup_migrations,
1819
logs::{get_reina_log_level, set_reina_log_level},
1920
scan::scan_directory_for_games,
2021
};
@@ -165,6 +166,25 @@ pub fn run() {
165166
let path_manager = PathManager::new();
166167
app.manage(path_manager);
167168

169+
match run_startup_migrations() {
170+
Ok(result) if result.executed == 0 => {
171+
log::debug!("启动迁移检查完成,无需执行");
172+
}
173+
Ok(result) => {
174+
log::info!(
175+
"启动迁移完成: executed={}, skipped={}, moved={}, replaced={}, removed_legacy={}",
176+
result.executed,
177+
result.skipped,
178+
result.migrated_files,
179+
result.replaced_files,
180+
result.removed_legacy_files
181+
);
182+
}
183+
Err(err) => {
184+
log::error!("启动迁移失败: {}", err);
185+
}
186+
}
187+
168188
// 执行 SeaORM 数据库迁移并注册到状态管理
169189
let app_handle = app.handle().clone();
170190
tauri::async_runtime::block_on(async move {

src-tauri/src/utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
#[cfg(target_os = "windows")]
12
pub mod command_ext;
3+
24
pub mod fs;
35
pub mod game_cover;
46
pub mod game_monitor;
57
pub mod launch;
8+
pub mod legacy_migration;
69
pub mod logs;
710
pub mod scan;

src-tauri/src/utils/command_ext.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#[cfg(target_os = "windows")]
12
use std::process::{Command, Stdio};
23

34
/// 为 GUI 环境下启动子进程提供统一的标准流处理。
@@ -8,16 +9,10 @@ pub trait CommandGuiExt {
89

910
impl CommandGuiExt for Command {
1011
fn gui_safe(&mut self) -> &mut Self {
11-
#[cfg(target_os = "windows")]
1212
{
1313
self.stdin(Stdio::null())
1414
.stdout(Stdio::null())
1515
.stderr(Stdio::null())
1616
}
17-
18-
#[cfg(not(target_os = "windows"))]
19-
{
20-
self
21-
}
2217
}
2318
}

src-tauri/src/utils/fs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[cfg(target_os = "windows")]
22
use crate::utils::command_ext::CommandGuiExt;
3+
34
use sea_orm::DatabaseConnection;
45
use serde::{Deserialize, Serialize};
56
use std::fs;
@@ -8,7 +9,7 @@ use std::process::Command;
89
use std::sync::Mutex;
910
use tauri::command;
1011

11-
// ==================== 路径相关常量(重导出) ====================
12+
// ==================== 路径相关常量 ====================
1213

1314
pub use reina_path::{DB_BACKUP_SUBDIR, DB_DATA_DIR};
1415

0 commit comments

Comments
 (0)