assemble_std/dependencies/
web.rs1use assemble_core::cryptography::hash_sha256;
4use assemble_core::dependencies::{
5 AcquisitionError, Dependency, DependencyType, Registry, ResolvedDependency,
6 ResolvedDependencyBuilder,
7};
8use assemble_core::project::buildable::{BuildableObject, GetBuildable};
9
10use std::ffi::{OsStr, OsString};
11use std::fmt::Debug;
12use std::fs::File;
13use std::io::Read;
14use std::path::{Path, PathBuf};
15use std::{fs, io};
16use url::Url;
17
18pub struct WebRegistry {
20 base_url: Url,
21 name: String,
22}
23
24impl WebRegistry {
25 pub fn new(name: &str, url: &str) -> Result<WebRegistry, url::ParseError> {
27 Url::parse(url).map(|url| Self {
28 base_url: url,
29 name: name.to_string(),
30 })
31 }
32}
33
34impl Registry for WebRegistry {
35 fn url(&self) -> Url {
36 self.base_url.clone()
37 }
38
39 fn supported(&self) -> Vec<DependencyType> {
40 vec![remote_file_system_type(&self.name)]
41 }
42}
43
44pub fn remote_file_system_type(name: &str) -> DependencyType {
46 DependencyType::new(name, name, ["*"])
47}
48
49#[derive(Debug)]
51pub struct WebDependency {
52 file_path: PathBuf,
53 from_registry: String,
54 file_name: Option<OsString>,
55}
56
57impl WebDependency {
58 pub fn new<P: AsRef<Path>, S: AsRef<str>>(file_path: P, from_registry: S) -> Self {
60 Self {
61 file_path: file_path.as_ref().to_path_buf(),
62 from_registry: from_registry.as_ref().to_string(),
63 file_name: None,
64 }
65 }
66
67 pub fn with_file_name(mut self, file: impl AsRef<OsStr>) -> Self {
69 self.file_name = Some(file.as_ref().to_os_string());
70 self
71 }
72
73 pub fn file_name(&self) -> OsString {
75 self.file_name
76 .as_deref()
77 .or(self.file_path.file_name())
78 .unwrap_or(OsStr::new("tmp.bin"))
79 .to_os_string()
80 }
81}
82
83impl GetBuildable for WebDependency {
84 fn as_buildable(&self) -> BuildableObject {
85 BuildableObject::None
86 }
87}
88
89impl Dependency for WebDependency {
90 fn id(&self) -> String {
91 self.file_name().to_string_lossy().to_string()
92 }
93
94 fn dep_type(&self) -> DependencyType {
95 remote_file_system_type(&self.from_registry)
96 }
97
98 fn try_resolve(
99 &self,
100 registry: &dyn Registry,
101 cache_path: &Path,
102 ) -> Result<ResolvedDependency, AcquisitionError> {
103 let registry_url = registry.url();
104 let joined = registry_url
105 .join(&self.file_path.to_string_lossy())
106 .map_err(AcquisitionError::custom)?;
107
108 let file_name = self.file_name();
109
110 let file_name_sha = hash_sha256(&format!("{}", joined));
111 let download_location = cache_path
112 .join("downloads")
113 .join(file_name_sha.to_string())
114 .join(file_name);
115
116 fs::create_dir_all(download_location.parent().unwrap())
117 .map_err(AcquisitionError::custom)?;
118
119 let response = reqwest::blocking::get(joined).map_err(AcquisitionError::custom)?;
120
121 let body = response.bytes().map_err(AcquisitionError::custom)?;
122
123 let mut file = File::options()
124 .write(true)
125 .create(true)
126 .open(&download_location)
127 .map_err(AcquisitionError::custom)?;
128 io::copy(&mut body.as_ref(), &mut file).map_err(AcquisitionError::custom)?;
129
130 Ok(ResolvedDependencyBuilder::new(download_location).finish())
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use assemble_core::file_collection::FileCollection;
138 use std::env;
139
140 use tempfile::tempdir_in;
141
142 #[test]
143 fn download_rustup_init_script() {
144 let registry = WebRegistry::new("rust-site", "https://sh.rustup.rs/").unwrap();
145 let web_dependency =
146 WebDependency::new("", "rust-site").with_file_name("rustup-startup.sh");
147
148 let current_dir = env::current_dir().unwrap();
149 let temp_dir = tempdir_in(current_dir).expect("couldn't create temporary directory");
150
151 let dependency = web_dependency
152 .try_resolve(®istry, temp_dir.path())
153 .unwrap();
154
155 println!("dependency = {:#?}", dependency);
156 assert_eq!(dependency.artifact_files().files().len(), 1)
157 }
158}