use std::{ any::Any, collections::VecDeque, fmt::Debug, sync::{ mpsc::{SendError, Sender}, Mutex, MutexGuard, }, thread::JoinHandle, }; use log::{debug, info}; use serde::Serialize; use crate::{ database::models::data::DownloadableMetadata, error::application_download_error::ApplicationDownloadError, }; use super::{ download_manager_builder::{CurrentProgressObject, DownloadAgent}, util::queue::Queue, }; pub enum DownloadManagerSignal { /// Resumes (or starts) the `DownloadManager` Go, /// Pauses the `DownloadManager` Stop, /// Called when a `DownloadAgent` has fully completed a download. Completed(DownloadableMetadata), /// Generates and appends a `DownloadAgent` /// to the registry and queue Queue(DownloadAgent), /// Tells the Manager to stop the current /// download, sync everything to disk, and /// then exit Finish, /// Stops, removes, and tells a download to cleanup Cancel(DownloadableMetadata), /// Any error which occurs in the agent Error(ApplicationDownloadError), /// Pushes UI update UpdateUIQueue, UpdateUIStats(usize, usize), //kb/s and seconds } #[derive(Debug)] pub enum DownloadManagerStatus { Downloading, Paused, Empty, Error, } impl Serialize for DownloadManagerStatus { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(&format!["{self:?}"]) } } #[derive(Serialize, Clone, Debug, PartialEq)] pub enum DownloadStatus { Queued, Downloading, Validating, Error, } /// Accessible front-end for the `DownloadManager` /// /// The system works entirely through signals, both internally and externally, /// all of which are accessible through the `DownloadManagerSignal` type, but /// should not be used directly. Rather, signals are abstracted through this /// interface. /// /// The actual download queue may be accessed through the .`edit()` function, /// which provides raw access to the underlying queue. /// THIS EDITING IS BLOCKING!!! pub struct DownloadManager { terminator: Mutex>>>, download_queue: Queue, progress: CurrentProgressObject, command_sender: Sender, } #[allow(dead_code)] impl DownloadManager { pub fn new( terminator: JoinHandle>, download_queue: Queue, progress: CurrentProgressObject, command_sender: Sender, ) -> Self { Self { terminator: Mutex::new(Some(terminator)), download_queue, progress, command_sender, } } pub fn queue_download( &self, download: DownloadAgent, ) -> Result<(), SendError> { info!("creating download with meta {:?}", download.metadata()); self.command_sender .send(DownloadManagerSignal::Queue(download))?; self.command_sender.send(DownloadManagerSignal::Go) } pub fn edit(&self) -> MutexGuard<'_, VecDeque> { self.download_queue.edit() } pub fn read_queue(&self) -> VecDeque { self.download_queue.read() } pub fn get_current_download_progress(&self) -> Option { let progress_object = (*self.progress.lock().unwrap()).clone()?; Some(progress_object.get_progress()) } pub fn rearrange_string(&self, meta: &DownloadableMetadata, new_index: usize) { let mut queue = self.edit(); let current_index = get_index_from_id(&mut queue, meta).unwrap(); let to_move = queue.remove(current_index).unwrap(); queue.insert(new_index, to_move); self.command_sender .send(DownloadManagerSignal::UpdateUIQueue) .unwrap(); } pub fn cancel(&self, meta: DownloadableMetadata) { self.command_sender .send(DownloadManagerSignal::Cancel(meta)) .unwrap(); } pub fn rearrange(&self, current_index: usize, new_index: usize) { if current_index == new_index { return; } let needs_pause = current_index == 0 || new_index == 0; if needs_pause { self.command_sender .send(DownloadManagerSignal::Stop) .unwrap(); } debug!("moving download at index {current_index} to index {new_index}"); let mut queue = self.edit(); let to_move = queue.remove(current_index).unwrap(); queue.insert(new_index, to_move); drop(queue); if needs_pause { self.command_sender.send(DownloadManagerSignal::Go).unwrap(); } self.command_sender .send(DownloadManagerSignal::UpdateUIQueue) .unwrap(); self.command_sender.send(DownloadManagerSignal::Go).unwrap(); } pub fn pause_downloads(&self) { self.command_sender .send(DownloadManagerSignal::Stop) .unwrap(); } pub fn resume_downloads(&self) { self.command_sender.send(DownloadManagerSignal::Go).unwrap(); } pub fn ensure_terminated(&self) -> Result, Box> { self.command_sender .send(DownloadManagerSignal::Finish) .unwrap(); let terminator = self.terminator.lock().unwrap().take(); terminator.unwrap().join() } pub fn get_sender(&self) -> Sender { self.command_sender.clone() } } /// Takes in the locked value from .`edit()` and attempts to /// get the index of whatever id is passed in fn get_index_from_id( queue: &mut MutexGuard<'_, VecDeque>, meta: &DownloadableMetadata, ) -> Option { queue .iter() .position(|download_agent| download_agent == meta) }