The cross-platform version manager toolset crates.io/crates/hyper-jump
at main 90 lines 2.6 kB view raw
1use std::fs; 2 3use anyhow::anyhow; 4use anyhow::Result; 5use liblzma::read::XzDecoder; 6use zip::ZipArchive as ZipReader; 7 8use crate::domain::package::Package; 9use crate::domain::version::LocalVersion; 10use crate::ports::Archive; 11 12pub struct LocalArchive; 13 14impl Archive for LocalArchive { 15 async fn extract(&self, package: Package, file: LocalVersion) -> anyhow::Result<()> { 16 unarchive(package, file).await 17 } 18} 19 20async fn unarchive(package: Package, file: LocalVersion) -> Result<()> { 21 let path = format!("{}/{}.{}", file.path, file.file_name, file.file_format); 22 tokio::task::spawn_blocking(move || expand(package, file)) 23 .await? 24 .map_err(|e| anyhow!(e))?; 25 26 tokio::fs::remove_file(path).await?; 27 28 Ok(()) 29} 30 31fn expand(package: Package, tmp: LocalVersion) -> Result<()> { 32 use std::fs::File; 33 use std::os::unix::fs::PermissionsExt; 34 35 use anyhow::Context; 36 use flate2::read::GzDecoder; 37 use tar::Archive; 38 39 if fs::metadata(&tmp.file_name).is_ok() { 40 fs::remove_dir_all(&tmp.file_name)?; 41 } 42 43 let file_path = format!("{}/{}.{}", tmp.path, tmp.file_name, tmp.file_format); 44 let file = File::open(&file_path).map_err(|error| { 45 anyhow!( 46 "Failed to open file {}.{}, file doesn't exist. additional info: {error}", 47 tmp.file_name, 48 tmp.file_format, 49 ) 50 })?; 51 52 let output = format!("{}/{}", tmp.path, tmp.file_name); 53 54 let context_msg = format!( 55 "Failed to decompress or extract file {}.{}", 56 tmp.file_name, tmp.file_format 57 ); 58 59 match tmp.file_format.as_str() { 60 "tar.gz" => { 61 let decompress_stream = GzDecoder::new(file); 62 let mut archive = Archive::new(decompress_stream); 63 archive.unpack(&output).with_context(|| context_msg)?; 64 } 65 "tar.xz" => { 66 let decompress_stream = XzDecoder::new(file); 67 let mut archive = Archive::new(decompress_stream); 68 archive.unpack(&output).with_context(|| context_msg)?; 69 } 70 "zip" => { 71 let mut archive = ZipReader::new(file) 72 .map_err(|error| anyhow!("{context_msg}. additional info: {error}"))?; 73 archive.extract(&output).with_context(|| context_msg)?; 74 } 75 _ => return Err(anyhow!("Unsupported file format")), 76 } 77 78 let binary = &format!( 79 "{}/{}/{}", 80 tmp.file_name, 81 package.binary_path(), 82 package.binary_name() 83 ); 84 85 let mut perms = fs::metadata(binary)?.permissions(); 86 perms.set_mode(0o551); 87 fs::set_permissions(binary, perms)?; 88 89 Ok(()) 90}