use crate::{constant, squire};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy)]
pub enum Organizer {
Type,
Size,
Root,
Auto,
}
pub struct ArgConfig {
pub list: bool,
pub all: bool,
pub debug: bool,
pub serial_numbers: Vec<String>,
pub backup_dir: PathBuf,
pub output_dir: PathBuf,
pub workers: usize,
pub organize: Organizer,
}
fn missing_value(key: &str) {
println!("ERROR: '{}' flag requires a value.", key);
std::process::exit(1)
}
fn default_ios_backup_directory() -> PathBuf {
let home = dirs::home_dir().expect("Could not determine home directory");
if cfg!(target_os = "windows") {
home.join("AppData/Roaming/Apple Computer/MobileSync/Backup")
} else {
home.join("Library/Application Support/MobileSync/Backup")
}
}
fn helper() -> String {
"ios crate takes the following arguments\n\n\
\t--version: Print project version.\n\n\
\t--list: List the available backups.\n\
\t--debug: Enable debug level logging.\n\
\t--all: Extract all available backups.\n\
\t--serial: Initiate backup extraction for given serial number(s).\n\
\t--organize: Organize the extracted files by type, size, root, and auto.\n\
\t--workers | --threads: Numbers of workers (threads) to spin up for extraction.\n\
\t--backup-dir | --source: Custom path for the backup. Defaults to OS specific path.\n\
\t--output-dir | --destination: Destination directory. Defaults to 'extracted' in current path.\n"
.to_string()
}
pub fn arguments(metadata: &constant::MetaData) -> ArgConfig {
let args: Vec<String> = std::env::args().collect();
let mut version = false;
let mut list = false;
let mut all = false;
let mut debug = false;
let mut serial = String::new();
let mut workers = String::new();
let mut backup_dir = String::new();
let mut output_dir = String::new();
let mut organize = Organizer::Auto;
let mut i = 1; while i < args.len() {
match args[i].as_str() {
"-h" | "--help" | "-H" => {
println!("Usage: {} [OPTIONS]\n\n{}", args[0], helper());
std::process::exit(0)
}
"-V" | "-v" | "--version" => {
version = true;
}
"--list" => {
list = true;
}
"--all" => {
all = true;
}
"--debug" => {
debug = true;
}
"--serial" => {
i += 1; if i < args.len() {
serial = args[i].clone();
} else {
missing_value(&args[i - 1]);
}
}
"--workers" | "--threads" => {
i += 1; if i < args.len() {
workers = args[i].clone();
} else {
missing_value(&args[i - 1]);
}
}
"--organize" => {
i += 1; if i < args.len() {
match args[i].as_str() {
"type" => organize = Organizer::Type,
"size" => organize = Organizer::Size,
"root" => organize = Organizer::Root,
"auto" => organize = Organizer::Auto,
_ => {
println!("ERROR: '--organize' can only be 'type', 'size', 'root' or 'auto' (default)");
std::process::exit(1)
}
}
} else {
missing_value(&args[i - 1]);
}
}
"--backup-dir" | "--backup_dir" | "--source" | "--src" => {
i += 1; if i < args.len() {
backup_dir = args[i].clone();
} else {
missing_value(&args[i - 1]);
}
}
"--output-dir" | "--output_dir" | "--destination" | "--dst" => {
i += 1; if i < args.len() {
output_dir = args[i].clone();
} else {
missing_value(&args[i - 1]);
}
}
_ => {
println!("\nERROR: Unknown argument: {}\n\n{}", args[i], helper());
std::process::exit(1)
}
}
i += 1;
}
if version {
println!("{} {}", &metadata.pkg_name, &metadata.pkg_version);
std::process::exit(0)
}
let backup_dir_final = if backup_dir.is_empty() {
default_ios_backup_directory()
} else {
let tmp = PathBuf::from(backup_dir);
if tmp.exists() {
tmp
} else {
println!(
"ERROR: Backup directory '{}' does not exist!",
tmp.display()
);
std::process::exit(1)
}
};
let output_dir_final = if output_dir.is_empty() {
PathBuf::from("extracted")
} else {
PathBuf::from(output_dir)
};
let workers_final = if workers.is_empty() {
squire::default_workers()
} else {
workers.parse::<usize>().unwrap()
};
let serial_numbers: Vec<String> = serial
.split(",")
.filter(|s| !s.is_empty())
.map(String::from)
.collect();
ArgConfig {
list,
all,
debug,
serial_numbers,
backup_dir: backup_dir_final,
output_dir: output_dir_final,
workers: workers_final,
organize,
}
}