use crate::parser;
use crate::{constant, squire};
use plist::Value;
use rusqlite::{Connection, Result};
use std::fs::{create_dir_all, File};
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use threadpool::ThreadPool;
use tqdm;
pub fn get_plist_key(info: &Option<Value>, key: &str, default: &str) -> String {
match info.as_ref() {
Some(val) => match val.as_dictionary() {
Some(dict) => match dict.get(key) {
Some(value) => match value.as_string() {
Some(str) => str.to_string(),
None => default.to_string(),
},
None => default.to_string(),
},
None => default.to_string(),
},
None => default.to_string(),
}
}
pub fn parse_manifest_db(
manifest_db_path: &Path,
backup: &constant::Backup,
arguments: &parser::ArgConfig,
) -> Result<(), Box<dyn std::error::Error>> {
let conn = Connection::open(manifest_db_path)?;
let mut count_stmt = conn.prepare(&format!(
"SELECT COUNT(*) FROM Files {}",
squire::media_filter()
))?;
let count: usize = count_stmt.query_row([], |row| row.get(0))?;
let progress_bar_base = Arc::new(Mutex::new(
tqdm::tqdm(0..count)
.desc(Some("Extracting"))
.style(tqdm::Style::Block),
));
let mut stmt = conn.prepare(&format!(
"SELECT fileID, relativePath FROM Files {}",
squire::media_filter()
))?;
let rows = stmt.query_map([], |row| {
let file_id: String = row.get(0)?;
let relative_path: String = row.get(1)?;
Ok((file_id, relative_path))
})?;
let pool = ThreadPool::new(arguments.workers);
let (sender, receiver) = channel();
for file in rows {
match file {
Ok((file_id, relative_path)) => {
let backup_cloned = backup.path.clone();
let output_dir_cloned = arguments
.output_dir
.join(format!("{} - {}", backup.device_name, backup.serial_number));
let sender_cloned = sender.clone();
let organize_cloned = arguments.organize;
let progress_bar = Arc::clone(&progress_bar_base);
pool.execute(move || {
let result = extract_files(
&backup_cloned,
&output_dir_cloned,
file_id,
&PathBuf::from(relative_path),
organize_cloned,
);
sender_cloned.send(result).expect("Failed to send result");
let mut progress = progress_bar.lock().unwrap();
progress.pbar.update(1).unwrap();
});
}
Err(err) => {
log::error!("Failed to submit thread operation: {}", err);
}
}
}
drop(sender); pool.join();
for result in receiver {
if let Err(err) = result {
log::error!("Error processing files: {:?}", err);
}
}
Ok(())
}
fn extract_files(
backup_path: &Path,
output_path: &Path,
file_id: String,
relative_path: &PathBuf,
organize: parser::Organizer,
) -> std::io::Result<()> {
let src_path = backup_path.join(&file_id[..2]).join(file_id);
if !src_path.exists() {
let msg = format!("Path {} doesn't exist", src_path.display());
log::debug!("{}", msg);
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, msg))
}
let filename = relative_path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let dest_path = match organize {
parser::Organizer::Type => output_path.join(squire::file_type(relative_path, &filename)),
parser::Organizer::Size => output_path.join(squire::file_size(&src_path, &filename)),
parser::Organizer::Root => output_path.join(filename).to_owned(),
parser::Organizer::Auto => output_path.join(relative_path),
};
if let Some(parent) = dest_path.parent() {
match create_dir_all(parent) {
Ok(_) => (),
Err(err) => return Err(err),
}
}
let mut src_file = File::open(&src_path)?;
let mut dest_file = File::create(&dest_path)?;
match std::io::copy(&mut src_file, &mut dest_file) {
Ok(_) => (),
Err(err) => return Err(err),
}
log::debug!(
"Extracted: {} -> {}",
src_path.display(),
dest_path.display()
);
Ok(())
}