mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 20:51:29 +11:00
173 lines
5.5 KiB
Rust
173 lines
5.5 KiB
Rust
use std::{
|
|
io::Seek,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use anyhow::{bail, Context, Result};
|
|
use byte_unit::Byte;
|
|
use clap::Args;
|
|
use std::io::Read;
|
|
mod default_downloads;
|
|
|
|
#[derive(Args, Debug)]
|
|
pub(crate) struct Download {
|
|
#[clap(long)]
|
|
/// Directory to download the files into
|
|
#[clap(default_value_os_t = default_directory())]
|
|
pub directory: PathBuf,
|
|
/// Set of files to download. Use `name@url` format to specify a file prefix
|
|
downloads: Option<Vec<String>>,
|
|
/// Whether to automatically install the default set of files
|
|
#[clap(long)]
|
|
auto: bool,
|
|
/// The size limit for each individual file (ignored if the default files are downloaded)
|
|
#[clap(long, default_value = "10 MB")]
|
|
size_limit: Byte,
|
|
}
|
|
|
|
fn default_directory() -> PathBuf {
|
|
let mut result = Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.unwrap()
|
|
.join("assets");
|
|
result.push("downloads");
|
|
result
|
|
}
|
|
|
|
impl Download {
|
|
pub fn action(&self) -> Result<()> {
|
|
let mut to_download = vec![];
|
|
if let Some(downloads) = &self.downloads {
|
|
to_download = downloads
|
|
.iter()
|
|
.map(|it| Self::parse_download(&it))
|
|
.collect();
|
|
} else {
|
|
let mut accepted = self.auto;
|
|
let downloads = default_downloads::default_downloads()
|
|
.into_iter()
|
|
.filter(|it| {
|
|
let file = it.file_path(&self.directory);
|
|
!file.exists()
|
|
})
|
|
.collect::<Vec<_>>();
|
|
if !accepted {
|
|
if downloads.len() != 0 {
|
|
println!(
|
|
"Would you like to download a set of default svg files? These files are:"
|
|
);
|
|
for download in &downloads {
|
|
let builtin = download.builtin.as_ref().unwrap();
|
|
println!(
|
|
"{} ({}) under license {} from {}",
|
|
download.name,
|
|
byte_unit::Byte::from_bytes(builtin.expected_size.into())
|
|
.get_appropriate_unit(false),
|
|
builtin.license,
|
|
builtin.info
|
|
);
|
|
}
|
|
|
|
// For rustfmt, split prompt into its own line
|
|
const PROMPT: &str =
|
|
"Would you like to download a set of default svg files, as explained above?";
|
|
accepted = dialoguer::Confirm::new()
|
|
.with_prompt(PROMPT)
|
|
.wait_for_newline(true)
|
|
.interact()?;
|
|
} else {
|
|
println!("Nothing to download! All default downloads already created");
|
|
}
|
|
}
|
|
if accepted {
|
|
to_download = downloads;
|
|
}
|
|
}
|
|
for (index, download) in to_download.iter().enumerate() {
|
|
println!(
|
|
"{index}: Downloading {} from {}",
|
|
download.name, download.url
|
|
);
|
|
download.fetch(&self.directory, self.size_limit)?
|
|
}
|
|
println!("{} downloads complete", to_download.len());
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_download(value: &str) -> SVGDownload {
|
|
if let Some(at_index) = value.find('@') {
|
|
let name = &value[0..at_index];
|
|
let url = &value[at_index + 1..];
|
|
SVGDownload {
|
|
name: name.to_string(),
|
|
url: url.to_string(),
|
|
builtin: None,
|
|
}
|
|
} else {
|
|
let end_index = value.rfind(".svg").unwrap_or(value.len());
|
|
let url_with_name = &value[0..end_index];
|
|
let name = url_with_name
|
|
.rfind('/')
|
|
.map(|v| &url_with_name[v + 1..])
|
|
.unwrap_or(url_with_name);
|
|
SVGDownload {
|
|
name: name.to_string(),
|
|
url: value.to_string(),
|
|
builtin: None,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SVGDownload {
|
|
name: String,
|
|
url: String,
|
|
builtin: Option<BuiltinSvgProps>,
|
|
}
|
|
|
|
impl SVGDownload {
|
|
fn file_path(&self, directory: &Path) -> PathBuf {
|
|
directory.join(&self.name).with_extension("svg")
|
|
}
|
|
|
|
fn fetch(&self, directory: &Path, size_limit: Byte) -> Result<()> {
|
|
let mut size_limit = size_limit.get_bytes().try_into()?;
|
|
let mut limit_exact = false;
|
|
if let Some(builtin) = &self.builtin {
|
|
size_limit = builtin.expected_size;
|
|
limit_exact = true;
|
|
}
|
|
let mut file = std::fs::OpenOptions::new()
|
|
.create_new(true)
|
|
.write(true)
|
|
.open(&self.file_path(directory))
|
|
.context("Creating file")?;
|
|
let mut reader = ureq::get(&self.url).call()?.into_reader();
|
|
|
|
std::io::copy(
|
|
// ureq::into_string() has a limit of 10MiB so we must use the reader
|
|
&mut (&mut reader).take(size_limit),
|
|
&mut file,
|
|
)?;
|
|
if reader.read_exact(&mut [0]).is_ok() {
|
|
bail!("Size limit exceeded");
|
|
}
|
|
if limit_exact {
|
|
if file
|
|
.seek(std::io::SeekFrom::Current(0))
|
|
.context("Checking file limit")?
|
|
!= size_limit
|
|
{
|
|
bail!("Builtin downloaded file was not as expected");
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
struct BuiltinSvgProps {
|
|
expected_size: u64,
|
|
license: &'static str,
|
|
info: &'static str,
|
|
}
|