From 9c06ca94cbcee9ef55be7f6c210ed3991e8e6b75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fe=CC=81lix=20Saparelli?= <felix@passcod.name>
Date: Sat, 6 Mar 2021 22:35:05 +1300
Subject: [PATCH] Add support for Txz archives

---
 Cargo.lock     | 21 +++++++++++++++++++++
 Cargo.toml     |  1 +
 README.md      |  7 ++++---
 src/helpers.rs | 15 +++++++++++++--
 src/lib.rs     |  6 ++++--
 5 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index d3b7a9cc..0a41707f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -119,6 +119,7 @@ dependencies = [
  "tempdir",
  "tinytemplate",
  "tokio",
+ "xz2",
 ]
 
 [[package]]
@@ -796,6 +797,17 @@ dependencies = [
  "cfg-if 0.1.10",
 ]
 
+[[package]]
+name = "lzma-sys"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
 [[package]]
 name = "matches"
 version = "0.1.8"
@@ -1931,3 +1943,12 @@ checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
 dependencies = [
  "libc",
 ]
+
+[[package]]
+name = "xz2"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c"
+dependencies = [
+ "lzma-sys",
+]
diff --git a/Cargo.toml b/Cargo.toml
index b8e278a1..5d35dc5d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ dirs = "3.0.1"
 serde_derive = "1.0.118"
 crates-index = "0.16.2"
 semver = "0.11.0"
+xz2 = "0.1.6"
 
 [dev-dependencies]
 env_logger = "0.8.2"
diff --git a/README.md b/README.md
index 5e464e40..de8f5aab 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,7 @@ yes
   - [ ] Unofficial packaging
 - Package formats
   - [x] Tgz
+  - [x] Txz
   - [x] Tar
   - [x] Bin
 - Extraction / Transformation
@@ -51,7 +52,7 @@ yes
 
 ## Supporting Binary Installation
 
-`binstall` works with existing CI-built binary outputs, with configuration via `[package.metadata.binstall]` keys in the relevant crate manifest. 
+`binstall` works with existing CI-built binary outputs, with configuration via `[package.metadata.binstall]` keys in the relevant crate manifest.
 When configuring `binstall` you can test against a local manifest with `--manifest-path=PATH` argument to use the crate and manifest at the provided `PATH`, skipping crate discovery and download.
 
 To get started, add a `[package.metadata.binstall]` section to your `Cargo.toml. As an example, the default configuration would be:
@@ -102,7 +103,7 @@ For example, the default configuration (as shown above) for a crate called `radi
 - Installed to`$HOME/.cargo/bin/rust-radio-sx128x-v0.14.1-alpha.5`
 - With a symlink from `$HOME/.cargo/bin/rust-radio-sx128x`
 
-####  If the package name does not match the crate name 
+####  If the package name does not match the crate name
 
 As is common with libraries / utilities (and the `radio-sx128x` example), this can be overridden by specifying the `pkg-url`:
 
@@ -131,7 +132,7 @@ Which provides a binary path of: `sx128x-util-x86_64-unknown-linux-gnu[.exe]`. I
   - Because `wget`-ing releases is frustrating, `cargo install` takes a not inconsequential portion of forever on constrained devices,
     and often putting together actual _packages_ is overkill.
 - Why use the cargo manifest?
-  - Crates already have these, and they already contain a significant portion of the required information. 
+  - Crates already have these, and they already contain a significant portion of the required information.
     Also there's this great and woefully underused (imo) `[package.metadata]` field.
 - Why not use a binary repository instead?
   - Then we'd need to _host_ a binary repository, and worry about publishing and all the other fun things that come with releasing software.
diff --git a/src/helpers.rs b/src/helpers.rs
index 153cedf3..c2f019b8 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -6,6 +6,7 @@ use log::{debug, info, error};
 use cargo_toml::{Manifest};
 use flate2::read::GzDecoder;
 use tar::Archive;
+use xz2::read::XzDecoder;
 
 
 use crate::{Meta};
@@ -66,6 +67,16 @@ pub fn extract<S: AsRef<Path>, P: AsRef<Path>>(source: S, fmt: PkgFmt, path: P)
 
             tgz.unpack(path)?;
         },
+        PkgFmt::Txz => {
+            // Extract to install dir
+            debug!("Decompressing from archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref());
+
+            let dat = std::fs::File::open(source)?;
+            let tar = XzDecoder::new(dat);
+            let mut txz = Archive::new(tar);
+
+            txz.unpack(path)?;
+        },
         PkgFmt::Bin => {
             debug!("Copying data from archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref());
             // Copy to install dir
@@ -117,10 +128,10 @@ pub fn get_install_path<P: AsRef<Path>>(install_path: Option<P>) -> Option<PathB
 
 pub fn confirm() -> Result<bool, anyhow::Error> {
     info!("Do you wish to continue? yes/no");
-    
+
     let mut input = String::new();
     std::io::stdin().read_line(&mut input)?;
-   
+
     match input.as_str().trim() {
         "yes" => Ok(true),
         "no" => Ok(false),
diff --git a/src/lib.rs b/src/lib.rs
index 97b16383..55271832 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,6 +30,8 @@ pub enum PkgFmt {
     Tar,
     /// Download format is TGZ (TAR + GZip)
     Tgz,
+    /// Download format is TAR + XZ
+    Txz,
     /// Download format is raw / binary
     Bin,
 }
@@ -50,7 +52,7 @@ pub struct Meta {
 }
 
 /// Metadata for binary installation use.
-/// 
+///
 /// Exposed via `[package.metadata]` in `Cargo.toml`
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "kebab-case", default)]
@@ -146,7 +148,7 @@ mod test {
         assert_eq!(
             manifest.bin.as_slice(),
             &[
-                Product{ 
+                Product{
                     name: Some("cargo-binstall".to_string()),
                     path: Some("src/main.rs".to_string()),
                     edition: Some(cargo_toml::Edition::E2018),