0
0
mirror of https://github.com/TeFiLeDo/listload.git synced 2024-11-23 20:56:17 +01:00

implement downloading

This commit is contained in:
Adrian Wannenmacher 2023-06-27 23:11:26 +02:00
parent d10022fec8
commit 55c60fbb84
Signed by: tfld
GPG Key ID: 19D986ECB1E492D5
5 changed files with 83 additions and 12 deletions

View File

@ -12,6 +12,7 @@ clap = { version = "4.3.8", features = ["cargo", "derive", "env", "wrap_help"] }
directories = "5.0.1" directories = "5.0.1"
downloader = { version = "0.2.7", default-features = false, features = ["rustls-tls"] } downloader = { version = "0.2.7", default-features = false, features = ["rustls-tls"] }
indoc = "2.0.1" indoc = "2.0.1"
rand = "0.8.5"
regex = "1.8.4" regex = "1.8.4"
serde = { version = "1.0.164", features = ["derive"] } serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.99" serde_json = "1.0.99"

View File

@ -1,6 +1,6 @@
use std::{path::PathBuf, sync::OnceLock}; use std::{collections::HashMap, fs, path::PathBuf, sync::OnceLock};
use anyhow::{ensure, Context}; use anyhow::Context;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use config::Config; use config::Config;
use directories::{ProjectDirs, UserDirs}; use directories::{ProjectDirs, UserDirs};
@ -29,10 +29,7 @@ fn main() -> anyhow::Result<()> {
.set(user_dirs) .set(user_dirs)
.ok() .ok()
.context("failed to initialize user directories")?; .context("failed to initialize user directories")?;
PROJ_DIRS let proj_dirs = PROJ_DIRS.get_or_init(|| proj_dirs);
.set(proj_dirs)
.ok()
.context("failed to initialize program directories")?;
if let CMD::License = cli.command { if let CMD::License = cli.command {
println!("{}", include_str!("../LICENSE")); println!("{}", include_str!("../LICENSE"));
@ -41,7 +38,7 @@ fn main() -> anyhow::Result<()> {
// prepare for operation // prepare for operation
let cfg = Config::read_from_default_file().context("failed to load config")?; let cfg = Config::read_from_default_file().context("failed to load config")?;
let downloader = cfg.downloader().context("failed to create downloader")?; let mut downloader = cfg.downloader().context("failed to create downloader")?;
let mut persistent = let mut persistent =
PersistentState::read_from_default_file().context("failed to load persistent state")?; PersistentState::read_from_default_file().context("failed to load persistent state")?;
@ -52,6 +49,71 @@ fn main() -> anyhow::Result<()> {
CMD::PersistentState => { CMD::PersistentState => {
println!("{persistent}"); println!("{persistent}");
} }
CMD::Download { name } => {
let mut cache = proj_dirs.cache_dir().to_path_buf();
cache.push(&format!("{:0>16x}", rand::random::<u64>()));
fs::create_dir_all(&cache).context("failed to create cache dir")?;
let name = name
.as_ref()
.map(|n| n.as_str())
.or(persistent.list())
.context("no list specified or selected")?;
let list = TargetList::load(&name).context("failed to load list")?;
let mut downloads = list.downloads();
let mut mapping = HashMap::with_capacity(downloads.len());
let mut counter = 0;
for d in &mut downloads {
let mut cache_path = cache.clone();
cache_path.push(format!("{counter:0>16x}"));
let prev = std::mem::replace(&mut d.file_name, cache_path.clone());
mapping.insert(cache_path, prev);
counter += 1;
}
let results = downloader
.download(&downloads)
.context("all downloads failed")?;
for res in results {
if res.is_err() {
eprintln!("{:?}", res.context("download_failed").unwrap_err());
continue;
}
let res = res.unwrap();
let res = mapping
.remove(&res.file_name)
.context("target file name missing")
.map(|target| {
if target.is_absolute() {
target
} else {
let mut path = cfg.base_directory.clone();
path.push(target);
path
}
})
.and_then(|target| {
fs::rename(&res.file_name, target).context("failed to move cached result")
});
if let Err(err) = res {
eprintln!("{:?}", err);
}
}
for (leftover, _) in mapping {
if let Err(err) =
fs::remove_file(leftover).context("failed to delete leftover cache file")
{
eprintln!("{err:?}");
}
}
fs::remove_dir(cache).context("failed to delete cache directory")?;
}
CMD::List { cmd } => match cmd { CMD::List { cmd } => match cmd {
ListCommand::Create { ListCommand::Create {
name, name,
@ -132,6 +194,11 @@ enum CMD {
License, License,
/// Print the current persistent state. /// Print the current persistent state.
PersistentState, PersistentState,
/// Download a target list.
Download {
/// The name of the target list. Defaults to the selected list.
name: Option<String>,
},
/// Target list operations. /// Target list operations.
List { List {
#[clap(subcommand)] #[clap(subcommand)]

View File

@ -9,7 +9,7 @@ use anyhow::{bail, ensure, Context};
use indoc::writedoc; use indoc::writedoc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{target_list::TargetList, PROJ_DIRS}; use crate::PROJ_DIRS;
const PERSISTENT_FILE: &str = "persistent_state.json"; const PERSISTENT_FILE: &str = "persistent_state.json";

View File

@ -50,7 +50,7 @@ impl Display for Target {
} }
} }
impl Into<Download> for Target { impl Into<Download> for &Target {
fn into(self) -> Download { fn into(self) -> Download {
match self.urls.len() { match self.urls.len() {
0 => panic!("target without url"), 0 => panic!("target without url"),

View File

@ -4,7 +4,8 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use anyhow::{ensure, Context, Result}; use anyhow::{ensure, Context};
use downloader::Download;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -28,6 +29,10 @@ impl TargetList {
pub fn len_targets(&self) -> usize { pub fn len_targets(&self) -> usize {
self.inner.targets.len() self.inner.targets.len()
} }
pub fn downloads(&self) -> Vec<Download> {
self.inner.targets.iter().map(|t| t.into()).collect()
}
} }
impl TargetList { impl TargetList {
@ -54,8 +59,6 @@ impl TargetList {
} }
pub fn new(name: &str, comment: Option<&str>) -> anyhow::Result<Self> { pub fn new(name: &str, comment: Option<&str>) -> anyhow::Result<Self> {
let dirs = PROJ_DIRS.get().expect("directories not initialized");
let dir = Self::list_dir(); let dir = Self::list_dir();
ensure!( ensure!(
dir.is_dir() || !dir.exists(), dir.is_dir() || !dir.exists(),