From e095d48e718e6f914fd99754f832c6b23dbfccfa Mon Sep 17 00:00:00 2001 From: w1nst0n Date: Thu, 16 Sep 2021 14:43:15 +0200 Subject: [PATCH] Improvement/Add logging (closes #2, #3, #25) --- CHANGELOG.md | 10 ++++++ Cargo.lock | 68 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ src/core/sync.rs | 62 +++++++++++++++++++++++------------ src/gui/mod.rs | 4 ++- src/gui/views/list.rs | 23 +++++++------ src/gui/views/mod.rs | 1 - src/gui/views/settings.rs | 1 + src/main.rs | 59 +++++++++++++++++++++++++++++++++ 9 files changed, 200 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cae6586..39377ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,13 @@ and `Removed`. ## [Unreleased] +### Added +- UAD now comes with a logger. Debug information will be written to a `uad.log` file (Warning level log in *stdout*) (#2) + +### Changed +- UAD will no longer crash at start if it doesn't find ADB but display a useful message (#25) +- Better handling of ADB errors (#3) + +### Fixed +- Spelling mistake +- diff --git a/Cargo.lock b/Cargo.lock index 1c9691a..e7ae085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,17 @@ dependencies = [ "libloading 0.7.0", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -196,6 +207,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clipboard-win" version = "4.2.1" @@ -287,6 +311,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + [[package]] name = "copyless" version = "0.1.5" @@ -654,6 +689,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fern" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" +dependencies = [ + "colored", + "log", +] + [[package]] name = "fixedbitset" version = "0.4.0" @@ -1526,6 +1571,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -2301,6 +2356,16 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "tiny-skia" version = "0.2.0" @@ -2371,7 +2436,10 @@ dependencies = [ name = "uad_gui" version = "0.1.0" dependencies = [ + "chrono", + "fern", "iced", + "log", "serde", "serde_json", "static_init", diff --git a/Cargo.toml b/Cargo.toml index 3fba37d..bd97cfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ iced = { git = "https://github.com/hecrj/iced.git", features = ["svg"] } serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" static_init = "^1.0" +fern = { version = "^0.6", features = ["colored"] } +log = "^0.4" +chrono = "^0.4" [profile.release] opt-level = "s" diff --git a/src/core/sync.rs b/src/core/sync.rs index 6515ca3..50d27d1 100644 --- a/src/core/sync.rs +++ b/src/core/sync.rs @@ -1,5 +1,6 @@ use std::process::Command; use std::collections::HashSet; +use crate::core::uad_lists::Removal; #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; @@ -11,26 +12,34 @@ pub fn adb_shell_command(args: &str) -> Result { let output = Command::new("adb") .args(&["shell", args]) .creation_flags(0x08000000) // do not open a cmd window - .output() - .expect("adb command failed to start. Do you have ADB installed?"); + .output(); #[cfg(target_os = "macos")] let output = Command::new("adb") .args(&["shell", args]) - .output() - .expect("adb command failed to start. Do you have ADB installed?"); + .output(); #[cfg(not(any(target_os = "macos", target_os = "windows")))] let output = Command::new("adb") .args(&["shell", args]) - .output() - .expect("adb command failed to start. Do you have ADB installed?"); + .output(); - if !output.status.success() { - Err(String::from_utf8(output.stderr).unwrap()) - } else { - Ok(String::from_utf8(output.stdout).unwrap()) - } + match output { + Err(e) => { + error!("ADB: {}", e); + Err("ADB was not found".to_string()) + }, + Ok(o) => { + if !o.status.success() { + let stderr = String::from_utf8(o.stderr).unwrap().trim_end().to_string(); + error!("ADB: {}", stderr); + Err(stderr) + } else { + Ok(String::from_utf8(o.stdout).unwrap().trim_end().to_string()) + } + } + } + } @@ -42,33 +51,46 @@ pub fn list_all_system_packages() -> String { } pub fn hashset_installed_system_packages() -> HashSet { - let hashet: HashSet = adb_shell_command("pm list packages -s") + adb_shell_command("pm list packages -s") .unwrap_or("".to_string()) .replace("package:", "") .lines() .map(String::from) - .collect(); - - hashet + .collect() } -pub fn uninstall_package(package: String) -> String { +pub fn uninstall_package(package: String, removal: Removal) { let arg = "pm uninstall --user 0 ".to_string() + &package; + let output = adb_shell_command(&arg).unwrap(); + if output.contains("Success") { + info!("REMOVE [{}]: {}", removal, package); + + } else { + error!("REMOVE [{}]: {}", removal, output); + } - adb_shell_command(&arg).unwrap() } -pub fn restore_package(package: String) -> String { +pub fn restore_package(package: String, removal: Removal) { let arg = "cmd package install-existing --user 0 ".to_string() + &package; + let output = adb_shell_command(&arg).unwrap(); + + if output.contains("installed for user") { + info!("RESTORE [{}]: {}", removal, package); + } else { + error!("RESTORE: {} -- ", output); + } - adb_shell_command(&arg).unwrap() } pub fn get_phone_model() -> String { match adb_shell_command("getprop ro.product.model") { - Ok(model) => model, + Ok(model) => { + model + }, + Err(err) => { if err.contains("adb: no devices/emulators found") { "adb: no devices/emulators found".to_string() diff --git a/src/gui/mod.rs b/src/gui/mod.rs index e98891d..f7a0b11 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -107,6 +107,8 @@ impl Application for UadGui { UadGui::Loaded(state) => match message { Message::CatalogRefreshPress => { state.list_view = ListView::default(); + state.device_name = get_phone_brand(); + info!("PHONE_MODEL: {}", state.device_name); state.list_view.update(ListMessage::LoadSettings(state.settings_view.clone())); state.view = View::List; Command::perform(Self::load_phone_packages(), Message::CatalogAction) @@ -121,7 +123,6 @@ impl Application for UadGui { Command::none() } Message::CatalogAction(msg) => { - state.device_name = get_phone_brand(); state.list_view.update(msg).map(Message::CatalogAction) } Message::SettingsAction(msg) => { @@ -238,6 +239,7 @@ impl UadGui { State::default() } pub async fn load_phone_packages() -> ListMessage { + info!("PHONE_MODEL: {}", get_phone_brand()); ListMessage::LoadPackages(&UAD_LISTS) } } diff --git a/src/gui/views/list.rs b/src/gui/views/list.rs index be7bd98..92355b1 100644 --- a/src/gui/views/list.rs +++ b/src/gui/views/list.rs @@ -19,6 +19,7 @@ use crate::core::sync::{ pub struct SelectionPackage { pub name: String, pub state: PackageState, + pub removal: Removal, } #[derive(Default, Debug, Clone)] @@ -138,7 +139,8 @@ impl List { self.selected_packages.push( SelectionPackage { name: self.filtered_packages[i].name.clone(), - state: self.filtered_packages[i].state + state: self.filtered_packages[i].state, + removal: self.filtered_packages[i].removal, } ); } else { @@ -170,17 +172,20 @@ impl List { Message::ApplyActionOnSelection => { for p in &self.selected_packages { match p.state { - PackageState::Installed => uninstall_package(p.name.clone()), - PackageState::Uninstalled => restore_package(p.name.clone()), - PackageState::All => "[DEBUG] ApplySelectionAction: Unknown package state".to_string(), // This can't happen + PackageState::Installed => uninstall_package(p.name.clone(), p.removal), + PackageState::Uninstalled => restore_package(p.name.clone(), p.removal), + PackageState::All => error!("ApplySelectionAction: Unknown package state"), // This can't happen }; for phone_p in &mut self.phone_packages { if p.name == phone_p.name { phone_p.state = match phone_p.state { PackageState::Installed => PackageState::Uninstalled, - PackageState::Uninstalled => PackageState::Installed, - PackageState::All => PackageState::All, // This can't happen (Like... never) + PackageState::Uninstalled => PackageState::Installed, + PackageState::All => { + error!("ApplyActionOnSelection: Unknown package state"); + PackageState::All // This can't happen (like... never) + } }; break } @@ -193,7 +198,7 @@ impl List { let mut package; for p in &mut self.filtered_packages { p.selected = true; - package = SelectionPackage { name: p.name.clone(), state: p.state }; + package = SelectionPackage { name: p.name.clone(), state: p.state, removal: p.removal }; if !self.selected_packages.contains(&package) { self.selected_packages.push(package); } @@ -408,13 +413,13 @@ impl PackageRow { pub fn update(&mut self, message: RowMessage) -> Command { match message { RowMessage::RemovePressed(package) => { - uninstall_package(package.name); + uninstall_package(package.name, self.removal); self.state = PackageState::Uninstalled; self.selected = false; Command::none() } RowMessage::RestorePressed(package) => { - restore_package(package.name); + restore_package(package.name, self.removal); self.state = PackageState::Installed; self.selected = false; Command::none() diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index ca11771..d1a434d 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -1,4 +1,3 @@ pub mod list; -pub mod package; pub mod settings; pub mod about; \ No newline at end of file diff --git a/src/gui/views/settings.rs b/src/gui/views/settings.rs index ffbec68..19b425c 100644 --- a/src/gui/views/settings.rs +++ b/src/gui/views/settings.rs @@ -24,6 +24,7 @@ impl Settings { pub fn update(&mut self, msg: Message) { match msg { Message::ExpertModeToogle(toggled) => { + info!("Expert mode {}", if toggled {"enabled"} else {"disabled"}); self.expert_mode = toggled; } } diff --git a/src/main.rs b/src/main.rs index 84f1141..72ccaa4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,68 @@ #![windows_subsystem = "windows"] #![feature(drain_filter)] +#[macro_use] extern crate log; + +use chrono; +use std::{fs::OpenOptions, fmt::Arguments}; +use fern::{FormatCallback, colors::{Color, ColoredLevelConfig}}; +use log::Record; mod core; mod gui; fn main() { + setup_logger().expect("setup logging"); gui::UadGui::start(); +} + +pub fn setup_logger() -> Result<(), fern::InitError> { + + let colors = ColoredLevelConfig::new().info(Color::Green); + + let make_formatter = |use_colors: bool| { + move |out: FormatCallback, message: &Arguments, record: &Record| { + out.finish(format_args!( + "{} {} [{}:{}] {}", + chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + if use_colors { + format!("{:5}", colors.color(record.level())) + } else { + format!("{:5}", record.level().to_string()) + }, + record.file().unwrap_or("?".into()), + record + .line() + .map(|l| l.to_string()) + .unwrap_or(String::new()), + message + )) + } + }; + + let default_log_level = log::LevelFilter::Warn; + let log_file = OpenOptions::new() + .write(true) + .create(true) + .append(false) + .truncate(true) + .open("uad.log")?; + + let file_dispatcher = fern::Dispatch::new() + .format(make_formatter(false)) + .level(default_log_level) + .level_for("uad_gui", log::LevelFilter::Debug) + .chain(log_file); + + let stdout_dispatcher = fern::Dispatch::new() + .format(make_formatter(true)) + .level(default_log_level) + .level_for("uad_gui", log::LevelFilter::Warn) + .chain(std::io::stdout()); + + fern::Dispatch::new() + .chain(stdout_dispatcher) + .chain(file_dispatcher) + .apply()?; + + Ok(()) } \ No newline at end of file