diff options
| author | 2026-01-14 05:12:31 +0100 | |
|---|---|---|
| committer | 2026-01-14 05:12:31 +0100 | |
| commit | f093d2a310627aa3ee5a2820339f8a18bd251e81 (patch) | |
| tree | c3d1cdda9f1b8fed6adb5f0dfd17bfa5c81ecb36 /src-tauri/src/utils | |
| parent | f1babdf9a625ddbb661f4e0678e6258511347656 (diff) | |
| download | DropOut-f093d2a310627aa3ee5a2820339f8a18bd251e81.tar.gz DropOut-f093d2a310627aa3ee5a2820339f8a18bd251e81.zip | |
feat(java): integrate Adoptium API for Java runtime download
Add automatic Java (Temurin) download and installation feature:
- Add Adoptium API v3 integration to fetch latest Java releases
- Support JRE and JDK image types with version selection (8/11/17/21)
- Implement platform detection for macOS, Linux, and Windows
- Add SHA256 checksum verification for downloaded archives
- Add tar.gz extraction support with Unix permission preservation
- Handle macOS-specific Java path structure (Contents/Home/bin)
- Add frontend UI with version selector and download progress
- Register Tauri commands: fetch_adoptium_java, download_adoptium_java,
fetch_available_java_versions
Dependencies added: sha2, flate2, tar, dirs
Diffstat (limited to 'src-tauri/src/utils')
| -rw-r--r-- | src-tauri/src/utils/zip.rs | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src-tauri/src/utils/zip.rs b/src-tauri/src/utils/zip.rs index a03c975..dfe1214 100644 --- a/src-tauri/src/utils/zip.rs +++ b/src-tauri/src/utils/zip.rs @@ -1,5 +1,7 @@ +use flate2::read::GzDecoder; use std::fs; use std::path::Path; +use tar::Archive; pub fn extract_zip(zip_path: &Path, extract_to: &Path) -> Result<(), String> { let file = fs::File::open(zip_path) @@ -38,3 +40,76 @@ pub fn extract_zip(zip_path: &Path, extract_to: &Path) -> Result<(), String> { Ok(()) } + +/// Extract a tar.gz archive +/// +/// Adoptium's tar.gz archives usually contain a top-level directory, such as `jdk-21.0.5+11-jre/`. +/// This function returns the name of that directory to facilitate locating `bin/java` afterwards. +pub fn extract_tar_gz(archive_path: &Path, extract_to: &Path) -> Result<String, String> { + let file = fs::File::open(archive_path) + .map_err(|e| format!("Failed to open tar.gz {}: {}", archive_path.display(), e))?; + + let decoder = GzDecoder::new(file); + let mut archive = Archive::new(decoder); + + // Ensure the target directory exists + fs::create_dir_all(extract_to) + .map_err(|e| format!("Failed to create extract directory: {}", e))?; + + // Track the top-level directory name + let mut top_level_dir: Option<String> = None; + + for entry in archive + .entries() + .map_err(|e| format!("Failed to read tar entries: {}", e))? + { + let mut entry = entry.map_err(|e| format!("Failed to read tar entry: {}", e))?; + let entry_path = entry + .path() + .map_err(|e| format!("Failed to get entry path: {}", e))? + .into_owned(); + + // Extract the top-level directory name (the first path component) + if top_level_dir.is_none() { + if let Some(first_component) = entry_path.components().next() { + let component_str = first_component.as_os_str().to_string_lossy().to_string(); + if !component_str.is_empty() && component_str != "." { + top_level_dir = Some(component_str); + } + } + } + + let outpath = extract_to.join(&entry_path); + + if entry.header().entry_type().is_dir() { + fs::create_dir_all(&outpath) + .map_err(|e| format!("Failed to create directory {}: {}", outpath.display(), e))?; + } else { + // Ensure parent directory exists + if let Some(parent) = outpath.parent() { + if !parent.exists() { + fs::create_dir_all(parent) + .map_err(|e| format!("Failed to create parent dir: {}", e))?; + } + } + + let mut outfile = fs::File::create(&outpath) + .map_err(|e| format!("Failed to create file {}: {}", outpath.display(), e))?; + + std::io::copy(&mut entry, &mut outfile) + .map_err(|e| format!("Failed to extract file: {}", e))?; + + // Set executable permissions on Unix systems + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if let Ok(mode) = entry.header().mode() { + let permissions = fs::Permissions::from_mode(mode); + let _ = fs::set_permissions(&outpath, permissions); + } + } + } + } + + top_level_dir.ok_or_else(|| "Archive appears to be empty".to_string()) +} |