diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 430a95e..a71cb00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,18 +100,29 @@ jobs: - name: Clippy working-directory: src-tauri - run: cargo clippy -- -D warnings + run: cargo clippy --all-targets --all-features -- -D warnings - - name: Install cargo-tarpaulin - run: cargo install cargo-tarpaulin + - name: Run tests + working-directory: src-tauri + run: cargo test --verbose - - name: Run tests with coverage + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Generate coverage working-directory: src-tauri - run: cargo tarpaulin --out Xml --fail-under 80 --verbose + run: cargo llvm-cov --all-features --lcov --output-path ../coverage-rust.lcov + + - name: Upload coverage + uses: codecov/codecov-action@v4 + with: + files: ./coverage-rust.lcov + flags: backend + fail_ci_if_error: false - name: Build check working-directory: src-tauri - run: cargo build + run: cargo build --release # Build verification (ensures app can be built) build-check: diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 053311b..993129d 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -509,11 +509,13 @@ dependencies = [ "env_logger", "glob", "log", + "open", "plist", "rayon", "serde", "serde_json", "sha2", + "sysinfo", "tauri", "tauri-build", "tauri-plugin-opener", @@ -2217,6 +2219,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +dependencies = [ + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2394,6 +2405,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -3642,6 +3663,20 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + [[package]] name = "system-deps" version = "6.2.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f186c65..6d19ecb 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -50,6 +50,8 @@ env_logger = "0.11" # Error handling thiserror = "2" anyhow = "1" +sysinfo = "0.36.1" +open = "5.3.3" [dev-dependencies] tempfile = "3" diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..ac77f63 --- /dev/null +++ b/src-tauri/src/commands/mod.rs @@ -0,0 +1 @@ +pub mod system; diff --git a/src-tauri/src/commands/system.rs b/src-tauri/src/commands/system.rs new file mode 100644 index 0000000..b4bda38 --- /dev/null +++ b/src-tauri/src/commands/system.rs @@ -0,0 +1,107 @@ +use crate::utils::permissions; +use serde::Serialize; +#[cfg(not(target_os = "macos"))] +use std::path::PathBuf; +use sysinfo::Disks; +use tauri::command; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DiskInfo { + pub total_space: u64, + pub available_space: u64, + pub used_space: u64, + pub mount_point: String, + pub name: String, +} + +#[command] +pub fn get_disk_info() -> Result { + let disks = Disks::new_with_refreshed_list(); + + // On macOS, the system disk is usually mounted at "/" + // We'll find the disk mounted at "/" or take the first one + let disk = disks + .list() + .iter() + .find(|d| d.mount_point().to_string_lossy() == "/") + .or_else(|| disks.list().first()) + .ok_or("No disks found")?; + + let total_space = disk.total_space(); + let available_space = disk.available_space(); + let used_space = total_space.saturating_sub(available_space); + + Ok(DiskInfo { + total_space, + available_space, + used_space, + mount_point: disk.mount_point().to_string_lossy().to_string(), + name: disk.name().to_string_lossy().to_string(), + }) +} + +#[command] +pub fn check_full_disk_access() -> bool { + permissions::check_full_disk_access() +} + +#[command] +pub fn open_full_disk_access_settings() -> Result<(), String> { + #[cfg(target_os = "macos")] + { + // Deep link to Full Disk Access settings + // macOS 13+ (Ventura) uses a different URL scheme than older versions, but this usually redirects correctly + // x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles + open::that("x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") + .map_err(|e| e.to_string()) + } + #[cfg(not(target_os = "macos"))] + { + Ok(()) + } +} + +#[command] +pub fn reveal_in_finder(path: String) -> Result<(), String> { + #[cfg(target_os = "macos")] + { + // Use "open -R " to reveal in Finder + std::process::Command::new("open") + .arg("-R") + .arg(path) + .spawn() + .map_err(|e| e.to_string())?; + Ok(()) + } + #[cfg(not(target_os = "macos"))] + { + // Fallback for other OS (open parent dir) + let p = PathBuf::from(path); + if let Some(parent) = p.parent() { + open::that(parent).map_err(|e| e.to_string()) + } else { + Ok(()) + } + } +} + +#[command] +pub fn open_file(path: String) -> Result<(), String> { + open::that(path).map_err(|e| e.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_disk_info() { + // This test mostly checks that the function doesn't panic + // It might return Err if no disk is found (e.g. in some containers), which is handled + let result = get_disk_info(); + println!("Disk info result: {:?}", result); + // We don't assert ok/err because it depends on the environment, + // but we ensure the code path is executable. + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0347484..39d6f34 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,6 @@ // CleanMac - macOS disk cleanup and optimization utility +pub mod commands; pub mod models; pub mod utils; @@ -30,7 +31,12 @@ pub fn run() { .invoke_handler(tauri::generate_handler![ greet, format_size, - get_relative_time + get_relative_time, + commands::system::get_disk_info, + commands::system::check_full_disk_access, + commands::system::open_full_disk_access_settings, + commands::system::reveal_in_finder, + commands::system::open_file ]) .run(tauri::generate_context!()) .expect("error while running CleanMac application");