use assemble_core::cryptography::hash_sha256;
use assemble_core::dependencies::{
AcquisitionError, Dependency, DependencyType, Registry, ResolvedDependency,
ResolvedDependencyBuilder,
};
use assemble_core::project::buildable::{BuildableObject, GetBuildable};
use std::ffi::{OsStr, OsString};
use std::fmt::Debug;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::{fs, io};
use url::Url;
pub struct WebRegistry {
base_url: Url,
name: String,
}
impl WebRegistry {
pub fn new(name: &str, url: &str) -> Result<WebRegistry, url::ParseError> {
Url::parse(url).map(|url| Self {
base_url: url,
name: name.to_string(),
})
}
}
impl Registry for WebRegistry {
fn url(&self) -> Url {
self.base_url.clone()
}
fn supported(&self) -> Vec<DependencyType> {
vec![remote_file_system_type(&self.name)]
}
}
pub fn remote_file_system_type(name: &str) -> DependencyType {
DependencyType::new(name, name, ["*"])
}
#[derive(Debug)]
pub struct WebDependency {
file_path: PathBuf,
from_registry: String,
file_name: Option<OsString>,
}
impl WebDependency {
pub fn new<P: AsRef<Path>, S: AsRef<str>>(file_path: P, from_registry: S) -> Self {
Self {
file_path: file_path.as_ref().to_path_buf(),
from_registry: from_registry.as_ref().to_string(),
file_name: None,
}
}
pub fn with_file_name(mut self, file: impl AsRef<OsStr>) -> Self {
self.file_name = Some(file.as_ref().to_os_string());
self
}
pub fn file_name(&self) -> OsString {
self.file_name
.as_deref()
.or(self.file_path.file_name())
.unwrap_or(OsStr::new("tmp.bin"))
.to_os_string()
}
}
impl GetBuildable for WebDependency {
fn as_buildable(&self) -> BuildableObject {
BuildableObject::None
}
}
impl Dependency for WebDependency {
fn id(&self) -> String {
self.file_name().to_string_lossy().to_string()
}
fn dep_type(&self) -> DependencyType {
remote_file_system_type(&self.from_registry)
}
fn try_resolve(
&self,
registry: &dyn Registry,
cache_path: &Path,
) -> Result<ResolvedDependency, AcquisitionError> {
let registry_url = registry.url();
let joined = registry_url
.join(&self.file_path.to_string_lossy())
.map_err(AcquisitionError::custom)?;
let file_name = self.file_name();
let file_name_sha = hash_sha256(&format!("{}", joined));
let download_location = cache_path
.join("downloads")
.join(file_name_sha.to_string())
.join(file_name);
fs::create_dir_all(download_location.parent().unwrap())
.map_err(AcquisitionError::custom)?;
let response = reqwest::blocking::get(joined).map_err(AcquisitionError::custom)?;
let body = response.bytes().map_err(AcquisitionError::custom)?;
let mut file = File::options()
.write(true)
.create(true)
.open(&download_location)
.map_err(AcquisitionError::custom)?;
io::copy(&mut body.as_ref(), &mut file).map_err(AcquisitionError::custom)?;
Ok(ResolvedDependencyBuilder::new(download_location).finish())
}
}
#[cfg(test)]
mod tests {
use super::*;
use assemble_core::file_collection::FileCollection;
use std::env;
use tempfile::tempdir_in;
#[test]
fn download_rustup_init_script() {
let registry = WebRegistry::new("rust-site", "https://sh.rustup.rs/").unwrap();
let web_dependency =
WebDependency::new("", "rust-site").with_file_name("rustup-startup.sh");
let current_dir = env::current_dir().unwrap();
let temp_dir = tempdir_in(current_dir).expect("couldn't create temporary directory");
let dependency = web_dependency
.try_resolve(®istry, temp_dir.path())
.unwrap();
println!("dependency = {:#?}", dependency);
assert_eq!(dependency.artifact_files().files().len(), 1)
}
}