use std::path::PathBuf;
use miette::{Context, IntoDiagnostic};
use url::Url;
#[derive(Debug, clap::Parser)]
pub struct Opt {
#[clap(required = true)]
package: String,
#[clap(short, long)]
destination: Option<PathBuf>,
}
fn strip_package_extension(filename: &str) -> String {
if let Some(stripped) = filename.strip_suffix(".tar.bz2") {
stripped.to_string()
} else if let Some(stripped) = filename.strip_suffix(".conda") {
stripped.to_string()
} else {
filename.to_string()
}
}
fn determine_destination_from_url(url: &Url) -> miette::Result<PathBuf> {
let filename = url
.path_segments()
.and_then(Iterator::last)
.ok_or_else(|| miette::miette!("Could not extract package name from URL"))?;
let package_name = strip_package_extension(filename);
Ok(PathBuf::from(package_name))
}
async fn extract_from_url(
url: Url,
destination: Option<PathBuf>,
package_display: &str,
) -> miette::Result<(PathBuf, rattler_package_streaming::ExtractResult)> {
let destination = destination.map_or_else(|| determine_destination_from_url(&url), Ok)?;
println!(
"Extracting {} to {}",
package_display,
destination.display()
);
let client = super::client::create_client_with_middleware()?;
let result =
rattler_package_streaming::reqwest::tokio::extract(client, url, &destination, None, None)
.await
.into_diagnostic()
.with_context(|| format!("Failed to extract package from URL: {package_display}"))?;
Ok((destination, result))
}
fn determine_destination_from_path(package_path: &str) -> miette::Result<PathBuf> {
let path = PathBuf::from(package_path);
let package_name = path
.file_stem()
.and_then(|s| s.to_str())
.ok_or_else(|| miette::miette!("Invalid package filename"))?
.to_string();
Ok(PathBuf::from(package_name))
}
fn extract_from_path(
package_path: &str,
destination: Option<PathBuf>,
) -> miette::Result<(PathBuf, rattler_package_streaming::ExtractResult)> {
let destination =
destination.map_or_else(|| determine_destination_from_path(package_path), Ok)?;
println!("Extracting {} to {}", package_path, destination.display());
let result = rattler_package_streaming::fs::extract(&PathBuf::from(package_path), &destination)
.into_diagnostic()
.with_context(|| format!("Failed to extract package: {package_path}"))?;
Ok((destination, result))
}
pub async fn extract(opt: Opt) -> miette::Result<()> {
let (destination, result) = if let Ok(url) = Url::parse(&opt.package) {
extract_from_url(url, opt.destination, &opt.package).await?
} else {
extract_from_path(&opt.package, opt.destination)?
};
println!(
"{} Successfully extracted package",
console::style("✓").green(),
);
println!(" Destination: {}", destination.display());
println!(" SHA256: {:x}", result.sha256);
println!(" MD5: {:x}", result.md5);
println!(" Size: {} bytes", result.total_size);
Ok(())
}