pesde/src/download.rs

178 lines
6.1 KiB
Rust
Raw Normal View History

2024-07-28 18:19:54 +02:00
use crate::{
lockfile::{DependencyGraph, DownloadedDependencyGraphNode, DownloadedGraph},
2024-09-04 19:48:37 +02:00
manifest::DependencyType,
2024-11-10 16:43:25 +01:00
refresh_sources,
2024-07-28 18:19:54 +02:00
source::{
traits::{PackageRef, PackageSource},
PackageSources,
},
Project, PACKAGES_CONTAINER_NAME,
};
2024-11-05 20:44:24 +01:00
use fs_err::tokio as fs;
2024-07-17 19:38:01 +02:00
use std::{
collections::HashSet,
2024-11-05 20:44:24 +01:00
sync::{Arc, Mutex},
2024-07-17 19:38:01 +02:00
};
type MultithreadedGraph = Arc<Mutex<DownloadedGraph>>;
pub(crate) type MultithreadDownloadJob = (
tokio::sync::mpsc::Receiver<Result<String, errors::DownloadGraphError>>,
MultithreadedGraph,
);
2024-07-17 19:38:01 +02:00
impl Project {
2024-08-03 22:18:38 +02:00
/// Downloads a graph of dependencies
2024-11-05 20:44:24 +01:00
pub async fn download_graph(
2024-07-17 19:38:01 +02:00
&self,
graph: &DependencyGraph,
refreshed_sources: &mut HashSet<PackageSources>,
2024-11-05 20:44:24 +01:00
reqwest: &reqwest::Client,
2024-09-04 19:48:37 +02:00
prod: bool,
write: bool,
wally: bool,
) -> Result<MultithreadDownloadJob, errors::DownloadGraphError> {
2024-11-05 20:44:24 +01:00
let manifest = self.deser_manifest().await?;
2024-11-10 16:43:25 +01:00
let manifest_target_kind = manifest.target.kind();
let downloaded_graph: MultithreadedGraph = Arc::new(Mutex::new(Default::default()));
2024-07-17 19:38:01 +02:00
2024-11-10 16:43:25 +01:00
let (tx, rx) = tokio::sync::mpsc::channel(
graph
.iter()
.map(|(_, versions)| versions.len())
.sum::<usize>()
.max(1),
);
refresh_sources(
self,
graph
.iter()
.flat_map(|(_, versions)| versions.iter())
.map(|(_, node)| node.pkg_ref.source()),
refreshed_sources,
)
.await?;
2024-07-17 19:38:01 +02:00
let project = Arc::new(self.clone());
2024-07-17 19:38:01 +02:00
for (name, versions) in graph {
2024-07-22 23:16:04 +02:00
for (version_id, node) in versions {
// we need to download pesde packages first, since scripts (for target finding for example) can depend on them
if node.pkg_ref.like_wally() != wally {
continue;
}
let tx = tx.clone();
2024-07-17 19:38:01 +02:00
let name = name.clone();
let version_id = version_id.clone();
let node = node.clone();
let project = project.clone();
let reqwest = reqwest.clone();
let downloaded_graph = downloaded_graph.clone();
2024-11-10 16:43:25 +01:00
let package_dir = self.package_dir().to_path_buf();
2024-11-05 20:44:24 +01:00
tokio::spawn(async move {
2024-11-10 16:43:25 +01:00
let source = node.pkg_ref.source();
let container_folder = node.container_folder(
&package_dir
.join(manifest_target_kind.packages_folder(version_id.target()))
2024-11-10 16:43:25 +01:00
.join(PACKAGES_CONTAINER_NAME),
&name,
version_id.version(),
);
match fs::create_dir_all(&container_folder).await {
Ok(_) => {}
Err(e) => {
tx.send(Err(errors::DownloadGraphError::Io(e)))
.await
.unwrap();
return;
}
}
let project = project.clone();
2024-07-24 00:53:34 +02:00
log::debug!("downloading {name}@{version_id}");
2024-11-05 20:44:24 +01:00
let (fs, target) =
match source.download(&node.pkg_ref, &project, &reqwest).await {
Ok(target) => target,
Err(e) => {
tx.send(Err(Box::new(e).into())).await.unwrap();
return;
}
};
2024-07-24 00:53:34 +02:00
log::debug!("downloaded {name}@{version_id}");
2024-09-04 19:48:37 +02:00
if write {
if !prod || node.resolved_ty != DependencyType::Dev {
2024-11-05 20:44:24 +01:00
match fs.write_to(container_folder, project.cas_dir(), true).await {
2024-09-04 19:48:37 +02:00
Ok(_) => {}
Err(e) => {
tx.send(Err(errors::DownloadGraphError::WriteFailed(e)))
2024-11-05 20:44:24 +01:00
.await
2024-09-04 19:48:37 +02:00
.unwrap();
return;
}
};
} else {
log::debug!("skipping writing {name}@{version_id} to disk, dev dependency in prod mode");
2024-07-28 18:19:54 +02:00
}
2024-09-04 19:48:37 +02:00
}
2024-07-28 18:19:54 +02:00
let display_name = format!("{name}@{version_id}");
2024-11-05 20:44:24 +01:00
{
let mut downloaded_graph = downloaded_graph.lock().unwrap();
downloaded_graph
.entry(name)
.or_default()
.insert(version_id, DownloadedDependencyGraphNode { node, target });
}
tx.send(Ok(display_name)).await.unwrap();
});
2024-07-17 19:38:01 +02:00
}
}
Ok((rx, downloaded_graph))
2024-07-17 19:38:01 +02:00
}
}
2024-08-03 22:18:38 +02:00
/// Errors that can occur when downloading a graph
2024-07-17 19:38:01 +02:00
pub mod errors {
use thiserror::Error;
2024-08-03 22:18:38 +02:00
/// Errors that can occur when downloading a graph
2024-07-17 19:38:01 +02:00
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DownloadGraphError {
2024-08-08 17:59:59 +02:00
/// An error occurred deserializing the project manifest
2024-07-17 19:38:01 +02:00
#[error("error deserializing project manifest")]
ManifestDeserializationFailed(#[from] crate::errors::ManifestReadError),
2024-08-08 17:59:59 +02:00
/// An error occurred refreshing a package source
2024-07-17 19:38:01 +02:00
#[error("failed to refresh package source")]
RefreshFailed(#[from] Box<crate::source::errors::RefreshError>),
2024-08-03 22:18:38 +02:00
/// Error interacting with the filesystem
2024-11-01 20:57:32 +01:00
#[error("error interacting with the filesystem")]
2024-07-17 19:38:01 +02:00
Io(#[from] std::io::Error),
2024-08-03 22:18:38 +02:00
/// Error downloading a package
2024-07-17 19:38:01 +02:00
#[error("failed to download package")]
2024-08-11 16:16:25 +02:00
DownloadFailed(#[from] Box<crate::source::errors::DownloadError>),
2024-07-28 18:19:54 +02:00
2024-08-03 22:18:38 +02:00
/// Error writing package contents
2024-07-28 18:19:54 +02:00
#[error("failed to write package contents")]
WriteFailed(#[source] std::io::Error),
2024-07-17 19:38:01 +02:00
}
}