feat: add storage_info Rust command - returns data path, file count, total size"
Browse files- src-tauri/src/persistence.rs +114 -24
src-tauri/src/persistence.rs
CHANGED
|
@@ -1,24 +1,114 @@
|
|
| 1 |
-
use std::fs;
|
| 2 |
-
use std::path::PathBuf;
|
| 3 |
-
use tauri::{AppHandle, Manager};
|
| 4 |
-
|
| 5 |
-
pub fn app_file_path(app: &AppHandle, name: &str) -> Result<PathBuf, String> {
|
| 6 |
-
let dir = app.path().app_data_dir().map_err(|e| e.to_string())?.join("musealpha");
|
| 7 |
-
fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
| 8 |
-
Ok(dir.join(name))
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
pub fn save_json<T: serde::Serialize>(app: &AppHandle, name: &str, value: &T) -> Result<(), String> {
|
| 12 |
-
let path = app_file_path(app, name)?;
|
| 13 |
-
let json = serde_json::to_string_pretty(value).map_err(|e| e.to_string())?;
|
| 14 |
-
fs::write(path, json).map_err(|e| e.to_string())
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
pub fn load_json<T: serde::de::DeserializeOwned + Default>(app: &AppHandle, name: &str) -> Result<T, String> {
|
| 18 |
-
let path = app_file_path(app, name)?;
|
| 19 |
-
if !path.exists() {
|
| 20 |
-
return Ok(T::default());
|
| 21 |
-
}
|
| 22 |
-
let json = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
| 23 |
-
serde_json::from_str(&json).map_err(|e| e.to_string())
|
| 24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use std::fs;
|
| 2 |
+
use std::path::PathBuf;
|
| 3 |
+
use tauri::{AppHandle, Manager};
|
| 4 |
+
|
| 5 |
+
pub fn app_file_path(app: &AppHandle, name: &str) -> Result<PathBuf, String> {
|
| 6 |
+
let dir = app.path().app_data_dir().map_err(|e| e.to_string())?.join("musealpha");
|
| 7 |
+
fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
| 8 |
+
Ok(dir.join(name))
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
pub fn save_json<T: serde::Serialize>(app: &AppHandle, name: &str, value: &T) -> Result<(), String> {
|
| 12 |
+
let path = app_file_path(app, name)?;
|
| 13 |
+
let json = serde_json::to_string_pretty(value).map_err(|e| e.to_string())?;
|
| 14 |
+
fs::write(path, json).map_err(|e| e.to_string())
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
pub fn load_json<T: serde::de::DeserializeOwned + Default>(app: &AppHandle, name: &str) -> Result<T, String> {
|
| 18 |
+
let path = app_file_path(app, name)?;
|
| 19 |
+
if !path.exists() {
|
| 20 |
+
return Ok(T::default());
|
| 21 |
+
}
|
| 22 |
+
let json = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
| 23 |
+
serde_json::from_str(&json).map_err(|e| e.to_string())
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
/// Storage info for the settings panel
|
| 27 |
+
#[derive(serde::Serialize)]
|
| 28 |
+
pub struct StorageInfo {
|
| 29 |
+
pub data_path: String,
|
| 30 |
+
pub total_size_bytes: u64,
|
| 31 |
+
pub file_count: usize,
|
| 32 |
+
pub project_count: usize,
|
| 33 |
+
pub library_count: usize,
|
| 34 |
+
pub library_size_bytes: u64,
|
| 35 |
+
pub projects_size_bytes: u64,
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
#[tauri::command]
|
| 39 |
+
pub fn storage_info(app: AppHandle) -> Result<StorageInfo, String> {
|
| 40 |
+
let dir = app.path().app_data_dir().map_err(|e| e.to_string())?.join("musealpha");
|
| 41 |
+
let data_path = dir.to_string_lossy().to_string();
|
| 42 |
+
|
| 43 |
+
if !dir.exists() {
|
| 44 |
+
return Ok(StorageInfo { data_path, total_size_bytes: 0, file_count: 0, project_count: 0, library_count: 0, library_size_bytes: 0, projects_size_bytes: 0 });
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
let mut total_size: u64 = 0;
|
| 48 |
+
let mut file_count: usize = 0;
|
| 49 |
+
let mut projects_size: u64 = 0;
|
| 50 |
+
let mut project_count: usize = 0;
|
| 51 |
+
let mut library_size: u64 = 0;
|
| 52 |
+
|
| 53 |
+
if let Ok(entries) = fs::read_dir(&dir) {
|
| 54 |
+
for entry in entries.flatten() {
|
| 55 |
+
if let Ok(meta) = entry.metadata() {
|
| 56 |
+
if meta.is_file() {
|
| 57 |
+
let size = meta.len();
|
| 58 |
+
total_size += size;
|
| 59 |
+
file_count += 1;
|
| 60 |
+
let name = entry.file_name().to_string_lossy().to_string();
|
| 61 |
+
if name.starts_with("project_") && name.ends_with(".json") {
|
| 62 |
+
projects_size += size;
|
| 63 |
+
project_count += 1;
|
| 64 |
+
}
|
| 65 |
+
if name == "library.json" {
|
| 66 |
+
library_size = size;
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
// Count library items
|
| 74 |
+
let library_count = load_json::<Vec<serde_json::Value>>(&app, "library.json").map(|v| v.len()).unwrap_or(0);
|
| 75 |
+
|
| 76 |
+
Ok(StorageInfo { data_path, total_size_bytes: total_size, file_count, project_count, library_count, library_size_bytes: library_size, projects_size_bytes: projects_size })
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/// Clear all library data
|
| 80 |
+
#[tauri::command]
|
| 81 |
+
pub fn storage_clear_library(app: AppHandle) -> Result<(), String> {
|
| 82 |
+
let path = app_file_path(&app, "library.json")?;
|
| 83 |
+
if path.exists() { fs::write(&path, "[]").map_err(|e| e.to_string())?; }
|
| 84 |
+
// Also clear in-memory state
|
| 85 |
+
let state = app.state::<crate::library::LibraryState>();
|
| 86 |
+
let mut items = state.items.lock().map_err(|_| "lock")?;
|
| 87 |
+
items.clear();
|
| 88 |
+
Ok(())
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
/// Delete all project files and reset index
|
| 92 |
+
#[tauri::command]
|
| 93 |
+
pub fn storage_clear_projects(app: AppHandle) -> Result<(), String> {
|
| 94 |
+
let dir = app.path().app_data_dir().map_err(|e| e.to_string())?.join("musealpha");
|
| 95 |
+
if let Ok(entries) = fs::read_dir(&dir) {
|
| 96 |
+
for entry in entries.flatten() {
|
| 97 |
+
let name = entry.file_name().to_string_lossy().to_string();
|
| 98 |
+
if name.starts_with("project_") && name.ends_with(".json") {
|
| 99 |
+
let _ = fs::remove_file(entry.path());
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
// Reset index
|
| 104 |
+
save_json(&app, "projects_index.json", &serde_json::json!({"projects": [], "active_id": null}))?;
|
| 105 |
+
Ok(())
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
/// Reveal app data folder in OS file manager
|
| 109 |
+
#[tauri::command]
|
| 110 |
+
pub fn storage_reveal_folder(app: AppHandle) -> Result<(), String> {
|
| 111 |
+
let dir = app.path().app_data_dir().map_err(|e| e.to_string())?.join("musealpha");
|
| 112 |
+
fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
| 113 |
+
opener::open(dir.to_string_lossy().as_ref()).map_err(|e| format!("Could not open folder: {e}"))
|
| 114 |
+
}
|