diff --git a/src-tauri/src/core/java/detection.rs b/src-tauri/src/core/java/detection.rs index 95e7803..08dcebb 100644 --- a/src-tauri/src/core/java/detection.rs +++ b/src-tauri/src/core/java/detection.rs @@ -1,4 +1,5 @@ use std::io::Read; +use std::path::Path; use std::path::PathBuf; use std::process::{Command, Stdio}; use std::time::Duration; @@ -6,25 +7,78 @@ use std::time::Duration; #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; -use super::strip_unc_prefix; +use crate::core::java::strip_unc_prefix; const WHICH_TIMEOUT: Duration = Duration::from_secs(2); +/// Scans a directory for Java installations, filtering out symlinks +/// +/// # Arguments +/// * `base_dir` - Base directory to scan (e.g., mise or SDKMAN java dir) +/// * `should_skip` - Predicate to determine if an entry should be skipped +/// +/// # Returns +/// First valid Java installation found, or `None` +fn scan_java_dir(base_dir: &Path, should_skip: F) -> Option +where + F: Fn(&std::fs::DirEntry) -> bool, +{ + std::fs::read_dir(base_dir) + .ok()? + .flatten() + .filter(|entry| { + let path = entry.path(); + // Only consider real directories, not symlinks + path.is_dir() && !path.is_symlink() && !should_skip(entry) + }) + .find_map(|entry| { + let java_path = entry.path().join("bin/java"); + if java_path.exists() && java_path.is_file() { + Some(java_path) + } else { + None + } + }) +} + /// Finds Java installation from SDKMAN! if available /// -/// Checks the standard SDKMAN! installation path: -/// `~/.sdkman/candidates/java/current/bin/java` +/// Scans the SDKMAN! candidates directory and returns the first valid Java installation found. +/// Skips the 'current' symlink to avoid duplicates. +/// +/// Path: `~/.sdkman/candidates/java/` /// /// # Returns -/// `Some(PathBuf)` if SDKMAN! Java is found and exists, `None` otherwise +/// `Some(PathBuf)` pointing to `bin/java` if found, `None` otherwise pub fn find_sdkman_java() -> Option { let home = std::env::var("HOME").ok()?; - let sdkman_path = PathBuf::from(&home).join(".sdkman/candidates/java/current/bin/java"); - if sdkman_path.exists() { - Some(sdkman_path) - } else { - None + let sdkman_base = PathBuf::from(&home).join(".sdkman/candidates/java/"); + + if !sdkman_base.exists() { + return None; } + + scan_java_dir(&sdkman_base, |entry| entry.file_name() == "current") +} + +/// Finds Java installation from mise if available +/// +/// Scans the mise Java installation directory and returns the first valid installation found. +/// Skips version alias symlinks (e.g., `21`, `21.0`, `latest`, `lts`) to avoid duplicates. +/// +/// Path: `~/.local/share/mise/installs/java/` +/// +/// # Returns +/// `Some(PathBuf)` pointing to `bin/java` if found, `None` otherwise +pub fn find_mise_java() -> Option { + let home = std::env::var("HOME").ok()?; + let mise_base = PathBuf::from(&home).join(".local/share/mise/installs/java/"); + + if !mise_base.exists() { + return None; + } + + scan_java_dir(&mise_base, |_| false) // mise: no additional filtering needed } /// Runs `which` (Unix) or `where` (Windows) command to find Java in PATH with timeout @@ -150,6 +204,11 @@ pub fn get_java_candidates() -> Vec { if let Some(sdkman_java) = find_sdkman_java() { candidates.push(sdkman_java); } + + // Check common mise java candidates + if let Some(mise_java) = find_mise_java() { + candidates.push(mise_java); + } } #[cfg(target_os = "macos")] @@ -196,6 +255,11 @@ pub fn get_java_candidates() -> Vec { if let Some(sdkman_java) = find_sdkman_java() { candidates.push(sdkman_java); } + + // Check common mise java candidates + if let Some(mise_java) = find_mise_java() { + candidates.push(mise_java); + } } #[cfg(target_os = "windows")] diff --git a/src-tauri/src/core/java/persistence.rs b/src-tauri/src/core/java/persistence.rs index d5b446c..6720696 100644 --- a/src-tauri/src/core/java/persistence.rs +++ b/src-tauri/src/core/java/persistence.rs @@ -1,4 +1,4 @@ -use super::error::JavaError; +use crate::core::java::error::JavaError; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use tauri::{AppHandle, Manager}; diff --git a/src-tauri/src/core/java/priority.rs b/src-tauri/src/core/java/priority.rs index e456680..b2e9fb4 100644 --- a/src-tauri/src/core/java/priority.rs +++ b/src-tauri/src/core/java/priority.rs @@ -1,8 +1,8 @@ use tauri::AppHandle; -use super::JavaInstallation; use crate::core::java::persistence; use crate::core::java::validation; +use crate::core::java::JavaInstallation; pub async fn resolve_java_for_launch( app_handle: &AppHandle, diff --git a/src-tauri/src/core/java/validation.rs b/src-tauri/src/core/java/validation.rs index 48782f6..b56ad59 100644 --- a/src-tauri/src/core/java/validation.rs +++ b/src-tauri/src/core/java/validation.rs @@ -5,7 +5,7 @@ use std::process::Command; #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; -use super::JavaInstallation; +use crate::core::java::JavaInstallation; pub async fn check_java_installation(path: &PathBuf) -> Option { let path = path.clone();