#![allow(dead_code)]
use super::chunk::ChunkVec;
use super::downloader::join_all;
use crate::ManicError;
use crate::Result;
use crate::{Downloader, Hash};
#[cfg(feature = "progress")]
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use tokio::sync::{Mutex, MutexGuard};
#[derive(Clone, Debug)]
pub struct Map(Arc<Mutex<HashMap<String, Downloader>>>);
impl Default for Map {
fn default() -> Self {
Self(Arc::new(Mutex::new(HashMap::new())))
}
}
impl Map {
pub(crate) fn new() -> Self {
Self(Arc::new(Mutex::new(HashMap::new())))
}
pub(crate) async fn lock(&self) -> MutexGuard<'_, HashMap<String, Downloader>> {
self.0.lock().await
}
pub(crate) fn as_inner(&self) -> &Arc<Mutex<HashMap<String, Downloader>>> {
&self.0
}
pub(crate) fn into_inner(self) -> Arc<Mutex<HashMap<String, Downloader>>> {
self.0
}
pub(crate) async fn insert(&self, k: String, v: Downloader) -> Option<Downloader> {
let mut lock = self.lock().await;
lock.insert(k, v)
}
pub(crate) async fn get(&self, k: &str) -> Result<Downloader> {
let lock = self.lock().await;
let res = lock.get(k);
res.cloned().ok_or(ManicError::NotFound)
}
}
#[derive(Debug, Clone)]
pub struct Downloaded {
url: String,
name: String,
data: ChunkVec,
}
impl Downloaded {
pub(crate) fn new(url: String, name: String, data: ChunkVec) -> Self {
Self { url, name, data }
}
pub(crate) async fn save<T: AsRef<Path>>(&self, output_dir: T) -> Result<()> {
let output_path = output_dir.as_ref().join(Path::new(&self.name));
self.data.save_to_file(output_path).await
}
}
#[derive(Clone, Builder)]
pub struct MultiDownloader {
#[builder(default)]
downloaders: Map,
#[builder(default, setter(skip))]
#[cfg(feature = "progress")]
progress: Option<Arc<MultiProgress>>,
#[cfg(feature = "progress")]
progress_style: Option<ProgressStyle>,
}
impl MultiDownloader {
pub async fn new(#[cfg(feature = "progress")] progress: bool) -> MultiDownloader {
#[cfg(feature = "progress")]
let pb = if progress {
Some(Arc::new(MultiProgress::new()))
} else {
None
};
Self {
downloaders: Map::new(),
#[cfg(feature = "progress")]
progress: pb,
#[cfg(feature = "progress")]
progress_style: None,
}
}
pub async fn add(&mut self, url: String, workers: u8) -> Result<()> {
#[allow(unused_mut)]
let mut client = Downloader::new(&url, workers).await?;
#[cfg(feature = "progress")]
if let Some(pb) = &self.progress {
let mpb = ProgressBar::new(client.get_len());
let to_add = pb.add(mpb);
client.connect_progress(to_add);
}
self.downloaders.insert(url, client).await;
Ok(())
}
pub async fn verify(&mut self, url: String, hash: Hash) -> Result<()> {
let mut lock = self.downloaders.lock().await;
let chosen: &mut Downloader = lock.get_mut(&url).ok_or(ManicError::NotFound)?;
let modified = chosen.verify(hash);
lock.insert(url, modified).ok_or(ManicError::NotFound)?;
Ok(())
}
pub async fn download_all(&self) -> Result<Vec<Downloaded>> {
let mut fut_vec = Vec::new();
let lock = self.downloaders.lock().await;
for v in lock.values() {
let c = v.clone();
fut_vec.push(tokio::spawn(c.multi_download()));
}
Ok(join_all(fut_vec).await?.to_vec())
}
pub async fn download_one(&self, url: String) -> Result<ChunkVec> {
let chosen = self.downloaders.get(&url).await?;
chosen.download().await
}
}