use crate::helpers::{
data_source_helper::{get_data_source, get_file_name_from_url},
file_system_helpers::{ensure_folder_exists, WORKSPACE_PATHS},
};
use crate::{
core::models::{
callback_progress::{CallbackType, ProgressCallback, ProgressInfo, SharedProgressCallback},
mame_data_types::{get_data_type_details, MameDataType},
},
helpers::callback_progress_helper::get_progress_info,
};
use reqwest::blocking::Client;
use std::error::Error;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread;
#[doc = docify::embed!("examples/download_file.rs", main)]
pub fn download_file(
data_type: MameDataType,
workspace_path: &Path,
progress_callback: ProgressCallback,
) -> Result<PathBuf, Box<dyn Error + Send + Sync>> {
let destination_folder = workspace_path.join(WORKSPACE_PATHS.download_path);
let folder_created = ensure_folder_exists(&destination_folder);
if let Err(err) = folder_created {
return Err(Box::new(err));
}
let data_type_details = get_data_type_details(data_type);
progress_callback(get_progress_info(
format!("Searching URL for {}", data_type_details.name).as_str(),
));
let download_url =
match get_data_source(&data_type_details.source, &data_type_details.source_match) {
Ok(url) => url,
Err(err) => {
progress_callback(ProgressInfo {
progress: 0,
total: 0,
message: format!("Couldn't find URL for {}", data_type_details.name),
callback_type: CallbackType::Error,
});
return Err(err.into());
}
};
let file_name = get_file_name_from_url(&download_url);
let file_path = destination_folder.join(file_name.clone());
progress_callback(get_progress_info(
format!("Checking if file {} already exists", file_name).as_str(),
));
if Path::new(&file_path).exists() {
progress_callback(ProgressInfo {
progress: 0,
total: 0,
message: format!("{} already exists", file_name),
callback_type: CallbackType::Finish,
});
return Ok(file_path);
}
progress_callback(get_progress_info(
format!("Downloading {} file", data_type_details.name).as_str(),
));
download(&download_url, &destination_folder, progress_callback)
}
#[doc = docify::embed!("examples/download_files.rs", main)]
pub fn download_files(
workspace_path: &Path,
progress_callback: SharedProgressCallback,
) -> Vec<thread::JoinHandle<Result<PathBuf, Box<dyn Error + Send + Sync>>>> {
let progress_callback = Arc::clone(&progress_callback);
MameDataType::all_variants()
.iter()
.map(|&data_type| {
let workspace_path = workspace_path.to_path_buf();
let progress_callback = Arc::clone(&progress_callback);
thread::spawn(move || {
download_file(
data_type,
&workspace_path,
Box::new(move |progress_info| {
progress_callback(data_type, progress_info);
}),
)
})
})
.collect()
}
fn download(
url: &str,
destination_folder: &Path,
progress_callback: ProgressCallback,
) -> Result<PathBuf, Box<dyn Error + Send + Sync>> {
let file_name = get_file_name_from_url(url);
let mut response = Client::new().get(url).send()?;
let total_size = response.content_length().unwrap_or(0);
let mut downloaded: u64 = 0;
let mut buffer = [0; 4096];
let file_path = destination_folder.join(file_name.clone());
let mut file = File::create(&file_path)?;
while let Ok(bytes_read) = response.read(&mut buffer) {
if bytes_read == 0 {
break;
}
file.write_all(&buffer[..bytes_read])?;
downloaded += bytes_read as u64;
progress_callback(ProgressInfo {
progress: downloaded,
total: total_size,
message: String::from(""),
callback_type: CallbackType::Progress,
});
}
progress_callback(ProgressInfo {
progress: downloaded,
total: downloaded,
message: format!("{} downloaded successfully", file_name),
callback_type: CallbackType::Progress,
});
Ok(file_path)
}