use std::fs::canonicalize;
use cargo::{
core::{
package_id::PackageId,
source::{MaybePackage, Source},
Verbosity, Workspace,
},
sources::registry::RegistrySource,
util::{config::Config as CargoConfig, Filesystem},
};
use log::debug;
use crate::{args::CargoSideloadFetchArgs, utils};
pub fn fetch(args: CargoSideloadFetchArgs) -> anyhow::Result<()> {
let cargo_config = CargoConfig::default()?;
if args.common.quiet {
cargo_config.shell().set_verbosity(Verbosity::Quiet);
}
let manifest_path = canonicalize(args.common.path.join("Cargo.toml"))?;
let workspace = Workspace::new(&manifest_path, &cargo_config)?;
let mut downloader = Downloader::new(&cargo_config, &args)?;
for package_id in utils::workspace_packages(&cargo_config, &args.common, &workspace)? {
downloader.download(&package_id.name(), &package_id.version().to_string())?;
}
Ok(())
}
struct Downloader<'cfg> {
config: &'cfg CargoConfig,
registry: RegistrySource<'cfg>,
client: reqwest::blocking::Client,
args: CargoSideloadFetchArgs,
}
impl<'cfg> Downloader<'cfg> {
pub fn new(config: &'cfg CargoConfig, args: &CargoSideloadFetchArgs) -> anyhow::Result<Self> {
let registry = utils::create_registry(&config, &args.common.registry)?;
let client = reqwest::blocking::Client::new();
Ok(Self {
config,
registry,
client,
args: args.clone(),
})
}
pub fn download(&mut self, name: &str, version: &str) -> anyhow::Result<()> {
let source_id = self.registry.source_id();
let package_id = PackageId::new(name, version, source_id)?;
if self.args.force {
self.delete_existing(package_id)?;
}
match self.package_status(package_id)? {
MaybePackage::Ready(_) => println!(
"{}-{} is already cached.",
package_id.name(),
package_id.version()
),
MaybePackage::Download { url, .. } => self.download_package(package_id, &url)?,
}
Ok(())
}
fn package_status(&mut self, package_id: PackageId) -> anyhow::Result<MaybePackage> {
let _package_cache_lock = self.config.acquire_package_cache_lock()?;
let result = self.registry.download(package_id);
if result.is_err() {
println!(
"Failed to unpack crate file for {}. Double check your download url and headers.",
package_id.name()
);
self.delete_existing(package_id)?;
}
result
}
fn download_package(&mut self, package_id: PackageId, url: &str) -> anyhow::Result<()> {
debug!("Downloading: {}", url);
let mut request_builder = self.client.get(url);
for header in &self.args.headers {
request_builder = request_builder.header(&header.name, &header.value);
}
let request = request_builder.build()?;
debug!("{:#?}", request);
let response = self.client.execute(request)?;
debug!("{:#?}", response);
let body = response.error_for_status()?.bytes()?;
debug!("BODY");
debug!("{}", String::from_utf8_lossy(&body));
let file_name = format!("{}-{}.crate", package_id.name(), package_id.version());
{
let file_lock =
self.target_dir()
.open_rw(file_name, &self.config, "Waiting for file lock...")?;
let file_path = file_lock.path();
std::fs::write(file_path, body)?;
println!(
"{}-{} added to package cache",
package_id.name(),
package_id.version()
);
debug!("Download path: {:?}", file_path);
}
self.package_status(package_id)?;
Ok(())
}
fn target_dir(&self) -> Filesystem {
let registry_directory = utils::registry_directory(self.registry.source_id());
self.config.registry_cache_path().join(®istry_directory)
}
fn delete_existing(&self, package_id: PackageId) -> anyhow::Result<()> {
let name = package_id.name();
let version = package_id.version().to_string();
let file_name = format!("{}-{}.crate", name, version);
let file_lock =
self.target_dir()
.open_rw(file_name, &self.config, "Waiting for file lock...")?;
let file_path = file_lock.path();
std::fs::remove_file(file_path)?;
debug!("Removed: {:?}", file_path);
Ok(())
}
}