diff --git a/Cargo.lock b/Cargo.lock index 3fbd340..c8a9299 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -861,6 +861,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctor" version = "0.2.7" @@ -3600,6 +3621,7 @@ name = "uad-ng" version = "1.0.3" dependencies = [ "chrono", + "csv", "dark-light", "dirs 5.0.1", "fern", diff --git a/Cargo.toml b/Cargo.toml index c8cdcf3..0bb53c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ ureq = { version = "*", features = ["json"] } retry = "^2.0.0" iced = { version = "^0.12.0", features = ["advanced", "image"] } rfd = "^0.14" +csv="^1.3" [target.'cfg(not(target_os = "windows"))'.dependencies] flate2 = { version = "^1", optional = true } diff --git a/src/core/save.rs b/src/core/save.rs index 65dd79b..b92b032 100644 --- a/src/core/save.rs +++ b/src/core/save.rs @@ -28,7 +28,7 @@ pub async fn backup_phone( users: Vec, device_id: String, phone_packages: Vec>, -) -> Result<(), String> { +) -> Result { let mut backup = PhoneBackup { device_id: device_id.clone(), ..PhoneBackup::default() @@ -63,7 +63,7 @@ pub async fn backup_phone( format!("{}.json", chrono::Local::now().format("%Y-%m-%d_%H-%M-%S")); match fs::write(backup_path.join(backup_filename), json) { - Ok(_) => Ok(()), + Ok(_) => Ok(true), Err(err) => Err(err.to_string()), } } diff --git a/src/core/theme.rs b/src/core/theme.rs index 1e0e126..6d30d84 100644 --- a/src/core/theme.rs +++ b/src/core/theme.rs @@ -17,6 +17,7 @@ pub struct BaseColors { #[derive(Debug, Clone, Copy)] pub struct NormalColors { pub primary: Color, + #[allow(dead_code)] pub secondary: Color, pub surface: Color, pub error: Color, diff --git a/src/core/utils.rs b/src/core/utils.rs index 886915d..2f59e2d 100644 --- a/src/core/utils.rs +++ b/src/core/utils.rs @@ -3,7 +3,8 @@ use crate::core::theme::Theme; use crate::core::uad_lists::{PackageHashMap, PackageState, Removal, UadList}; use crate::gui::widgets::package_row::PackageRow; use chrono::offset::Utc; -use chrono::DateTime; +use chrono::{DateTime, Local}; +use csv::Writer; use std::path::PathBuf; use std::process::Command; use std::{fmt, fs}; @@ -167,41 +168,35 @@ pub async fn open_folder() -> Result { Ok(picked_folder.path().to_owned()) } -/// Export uninstalled packages in a file. +/// Export uninstalled packages in a csv file. /// Exported information will contain package name and description. pub async fn export_packages( user: Option, - device_id: String, phone_packages: Vec>, ) -> Result { - let uninstalled_packages: Vec = phone_packages[user.unwrap().index] + let uninstalled_packages: Vec<&PackageRow> = phone_packages[user.unwrap().index] .iter() .filter(|p| p.state.to_string() == "Uninstalled") - .map(|p| { - format!( - "{}Name: {}\nDescription: {}", - "-------------------------------------------------------------------\n", - p.name, - p.description.replace('\n', " ") - ) - }) .collect(); - let backup_content = format!( - "Device ID: {}\nUser ID: {}\n-------------------------------------------------------------------\n{}", - device_id, - user.unwrap().id, - uninstalled_packages.join("\n") - ); - let backup_file = format!( - "{}_{}.txt", + "{}_{}.csv", UNINSTALLED_PACKAGES_FILE_NAME, - chrono::Local::now().format("%Y%m%d") + Local::now().format("%Y%m%d") ); - match fs::write(backup_file, backup_content) { - Ok(_) => Ok(true), - Err(err) => Err(err.to_string()), + let file = fs::File::create(backup_file).map_err(|err| err.to_string())?; + let mut wtr = Writer::from_writer(file); + + wtr.write_record(["Package Name", "Description"]) + .map_err(|err| err.to_string())?; + + for package in uninstalled_packages { + wtr.write_record([&package.name, &package.description.replace('\n', " ")]) + .map_err(|err| err.to_string())?; } + + wtr.flush().map_err(|err| err.to_string())?; + + Ok(true) } diff --git a/src/gui/views/settings.rs b/src/gui/views/settings.rs index b1c772a..4cb2106 100644 --- a/src/gui/views/settings.rs +++ b/src/gui/views/settings.rs @@ -61,7 +61,7 @@ pub enum Message { BackupDevice, RestoreDevice, RestoringDevice(Result), - DeviceBackedUp(Result<(), String>), + DeviceBackedUp(Result), ChooseBackUpFolder, FolderChosen(Result), ExportPackages, @@ -156,11 +156,19 @@ impl Settings { ), Message::DeviceBackedUp, ), - Message::DeviceBackedUp(_) => { - info!("[BACKUP] Backup successfully created"); - self.device.backup.backups = - list_available_backups(&self.general.backup_folder.join(phone.adb_id.clone())); - self.device.backup.selected = self.device.backup.backups.first().cloned(); + Message::DeviceBackedUp(is_backed_up) => { + match is_backed_up { + Ok(_) => { + info!("[BACKUP] Backup successfully created"); + self.device.backup.backups = list_available_backups( + &self.general.backup_folder.join(phone.adb_id.clone()), + ); + self.device.backup.selected = self.device.backup.backups.first().cloned(); + } + Err(err) => { + error!("[BACKUP FAILED] Backup creation failed: {:?}", err); + } + } Command::none() } Message::RestoreDevice => match restore_backup(phone, packages, &self.device) { @@ -234,11 +242,7 @@ impl Settings { } } Message::ExportPackages => Command::perform( - export_packages( - *selected_user, - self.device.device_id.clone(), - packages.to_vec(), - ), + export_packages(*selected_user, packages.to_vec()), Message::PackagesExported, ), Message::PackagesExported(exported) => {