diff --git a/Cargo.lock b/Cargo.lock index 033cbbc..a37c265 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -881,9 +881,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -1538,6 +1538,7 @@ dependencies = [ "jpm_common", "miette 5.10.0", "reqwest", + "starbase_archive", "starbase_styles", "starbase_utils", "thiserror", @@ -1664,6 +1665,17 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2069,6 +2081,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "pmutil" version = "0.6.1" @@ -2965,6 +2983,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "starbase_archive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bb40d408fb474e6d0092eba4fa977aa40c85885931c62219bb12d512e515e8" +dependencies = [ + "flate2", + "miette 5.10.0", + "rustc-hash", + "starbase_styles", + "starbase_utils", + "tar", + "thiserror", + "tracing", + "xz2", +] + [[package]] name = "starbase_events" version = "0.2.0" @@ -3976,6 +4011,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.7.0" @@ -4830,6 +4876,24 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index d3f732c..95cddc8 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT" jpm_common = { path = "../common" } miette = { workspace = true } reqwest = { workspace = true } +starbase_archive = { workspace = true, features = ["tar-gz", "tar-xz"] } starbase_styles = { workspace = true } starbase_utils = { workspace = true } thiserror = { workspace = true } diff --git a/crates/store/src/storage_item.rs b/crates/store/src/storage_item.rs index 1b1738e..b714111 100644 --- a/crates/store/src/storage_item.rs +++ b/crates/store/src/storage_item.rs @@ -2,7 +2,8 @@ use jpm_common::{EsTarget, PackageName, Version}; use std::path::PathBuf; pub trait StorageItem { - fn get_archive_ext(&self) -> String; + fn get_archive_ext(&self) -> &str; + fn get_label(&self) -> &str; fn to_file_path(&self) -> PathBuf; fn to_file_prefix(&self) -> String; } @@ -14,8 +15,12 @@ pub struct PackageItem<'app> { } impl<'app> StorageItem for PackageItem<'app> { - fn get_archive_ext(&self) -> String { - "tar.xz".into() + fn get_archive_ext(&self) -> &str { + "tar.xz" + } + + fn get_label(&self) -> &str { + self.package.as_str() } fn to_file_path(&self) -> PathBuf { @@ -42,8 +47,12 @@ pub struct TypeScriptItem<'app> { } impl<'app> StorageItem for TypeScriptItem<'app> { - fn get_archive_ext(&self) -> String { - "tar.gz".into() // What npm uses + fn get_archive_ext(&self) -> &str { + "tar.gz" // What npm uses + } + + fn get_label(&self) -> &str { + "typescript" } fn to_file_path(&self) -> PathBuf { diff --git a/crates/store/src/store.rs b/crates/store/src/store.rs index 90516a3..9a5180f 100644 --- a/crates/store/src/store.rs +++ b/crates/store/src/store.rs @@ -1,8 +1,9 @@ use crate::storage_item::StorageItem; use crate::store_error::StoreError; +use starbase_archive::Archiver; use starbase_utils::fs::{self, FsError}; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use tracing::debug; pub struct Store { @@ -17,22 +18,22 @@ impl Store { url: &str, item: impl StorageItem, ) -> miette::Result { - let filename = format!("{}.{}", item.to_file_prefix(), item.get_archive_ext()); + let archive_file = self.cache_dir.join(format!( + "{}.{}", + item.to_file_prefix(), + item.get_archive_ext() + )); - self.download_archive_with_name(url, &filename).await - } - - pub async fn download_archive_with_name( - &self, - url: &str, - filename: &str, - ) -> miette::Result { - let archive_file = self.cache_dir.join(filename); - - debug!(url = ?url, cache_file = ?archive_file, "Downloading package archive"); + debug!( + item = item.get_label(), + archive_url = ?url, + cache_file = ?archive_file, + "Downloading package archive", + ); if archive_file.exists() { debug!( + item = item.get_label(), cache_file = ?archive_file, "Package archive already exists in local cache, skipping download" ); @@ -74,8 +75,41 @@ impl Store { error, })?; - debug!(cache_file = ?archive_file, "Downloaded package archive"); + debug!( + item = item.get_label(), + cache_file = ?archive_file, + "Downloaded package archive", + ); Ok(archive_file) } + + pub async fn unpack_archive( + &self, + archive_file: &Path, + item: impl StorageItem, + ) -> miette::Result { + let output_dir = self.packages_dir.join(item.to_file_path()); + + debug!( + item = item.get_label(), + archive_file = ?archive_file, + output_dir = ?output_dir, + "Unpacking package archive", + ); + + if output_dir.exists() { + return Ok(output_dir); + } + + Archiver::new(&output_dir, archive_file).unpack_from_ext()?; + + debug!( + item = item.get_label(), + output_dir = ?output_dir, + "Unpacked package archive", + ); + + Ok(output_dir) + } }