1 use clap::{Args, Parser, Subcommand}; 2 3 #[derive(Parser)] 4 #[command( 5 about = "Tool to push your rebuilt modules to your device.\nSet ANDROID_SERIAL to choose your device if there is more than one." 6 )] 7 #[command(version = "0.4")] 8 pub struct Cli { 9 #[command(subcommand)] 10 pub command: Commands, 11 #[clap(flatten)] 12 pub global_options: GlobalOptions, 13 } 14 15 #[derive(Subcommand)] 16 pub enum Commands { 17 /// Shows the file differences between build tree and host. 18 /// Show the actions that would be run. 19 Status, 20 /// Updates the device (via adb push) with files from $ANDROID_PRODUCT_OUT. 21 /// Only pushes files listed on --partitions. 22 /// This does not work well when $ANDROID_PRODUCT_OUT and the device image 23 /// are vastly different. You should reimage the device in that case. 24 Update, 25 /// Adds module name to the list of tracked modules. 26 /// If an installed file under $ANDROID_PRODUCT_OUT is not 27 /// part of a tracked module or the base image, then it will 28 /// not be pushed to the device. 29 Track(ModuleNames), 30 /// Change the base module we are tracking from `droid` to something else. 31 TrackBase(BaseModule), 32 /// Removes module name from list of tracked modules. 33 /// See `track` for more details. 34 Untrack(ModuleNames), 35 /// Removes untracked files from the device. 36 Clean { 37 #[clap(long, short)] 38 force: bool, 39 }, 40 } 41 42 #[derive(Debug, Args)] 43 pub struct ModuleNames { 44 /// List one or modules, space separated. 45 /// Use the module name in Android.bp 46 pub modules: Vec<String>, 47 } 48 49 #[derive(Debug, Args)] 50 pub struct BaseModule { 51 /// The module name the system image is built from like 'droid' or 'sync'. 52 /// It can also be an unbundled mainline module name. 53 pub base: String, 54 } 55 56 #[derive(Args, Debug)] 57 pub struct GlobalOptions { 58 // TODO(rbraunstein): Revisit all the command name descriptions. 59 // TODO(rbraunstein): Add system_other to the default list, but deal gracefully 60 // with it not being on the device. 61 /// Partitions in the product tree to sync. Repeat arg or comma-separate. 62 /// 63 /// By default this includes: "system", "system_ext", "odm", "product" 64 /// 65 /// If a partition is explicitly passed in but that does not exist in the 66 /// tracked files then adevice will error. 67 #[clap(long, short, global = true, value_delimiter = ',')] 68 pub partitions: Option<Vec<String>>, 69 // TODO(rbraunstein): Validate relative, not absolute paths. 70 /// If unset defaults to ANDROID_PRODUCT_OUT env variable. 71 #[clap(long = "product_out", global = true)] 72 pub product_out: Option<String>, 73 /// Do not make any modification if more than this many are needed 74 #[clap(long, short, default_value_t = 400, global = true)] 75 pub max_allowed_changes: usize, 76 /// If passed, use the device, otherwise use the only connected device or ANDROID_SERIAL env value. 77 #[clap(long, short, global = true)] 78 pub serial: Option<String>, 79 /// Override the type of restart that happens after an update. 80 #[clap(long = "restart", short, global = true, value_enum, default_value_t=RestartChoice::Auto)] 81 pub restart_choice: RestartChoice, 82 /// Path to config file. Uses $HOME/.config/asuite/adevice-tracking.json if unset. 83 #[clap(long = "config", global = true)] 84 pub config_path: Option<String>, 85 #[clap(long = "force", global = true, alias = "force", alias = "force")] 86 // Force device update even if unbuilt modules are detected. 87 pub force: bool, 88 // Don't wait for device to become available after restarting it. 89 #[clap(long = "nowait", global = true, alias = "no_wait", alias = "no-wait")] 90 pub nowait: bool, 91 } 92 93 #[derive(clap::ValueEnum, Clone, Debug)] 94 pub enum Verbosity { 95 /// Only show minimal information. 96 None, 97 /// Show all adb operations. 98 Details, 99 /// For debugging internals of tool and timings. 100 Debug, 101 } 102 103 /// Allows you to choose how to reboot or to not reboot. 104 #[derive(clap::ValueEnum, Clone, Debug)] 105 pub enum RestartChoice { 106 /// Let the system choose the restart based on the files changed. 107 Auto, 108 /// Don't restart. 109 None, 110 /// Always do a full system reboot after updates. 111 Reboot, 112 /// Always do a framework restart restart after updates. 113 Restart, 114 } 115 116 #[derive(Clone, Debug, PartialEq)] 117 pub enum Wait { 118 Yes, 119 No, 120 } 121 122 impl From<Wait> for bool { from(w: Wait) -> bool123 fn from(w: Wait) -> bool { 124 match w { 125 Wait::Yes => true, 126 Wait::No => false, 127 } 128 } 129 } 130 131 impl Cli { 132 /// Decide if the options indicate that we should wait for the device. 133 /// Exists in case the cli options get more complicated like --wait=false should_wait(&self) -> Wait134 pub fn should_wait(&self) -> Wait { 135 match self.global_options.nowait { 136 true => Wait::No, 137 false => Wait::Yes, 138 } 139 } 140 } 141 142 #[cfg(test)] 143 mod tests { 144 use crate::cli::Wait; 145 146 use super::Cli; 147 use clap::Parser; 148 149 #[test] force_default_false()150 fn force_default_false() { 151 let cli = Cli::parse_from(["fake_prog", "update"]); 152 assert!(!cli.global_options.force); 153 } 154 155 #[test] force_works()156 fn force_works() { 157 let cli = Cli::parse_from(["fake_prog", "update", "--force"]); 158 assert!(cli.global_options.force); 159 } 160 161 #[test] nowait_works()162 fn nowait_works() { 163 let cli = Cli::parse_from(["fake_prog", "update", "--nowait"]); 164 assert!(cli.global_options.nowait); 165 } 166 167 #[test] no_wait_alias_works()168 fn no_wait_alias_works() { 169 let cli = Cli::parse_from(["fake_prog", "update", "--no_wait"]); 170 assert!(cli.global_options.nowait); 171 } 172 173 #[test] unset_nowait_is_none()174 fn unset_nowait_is_none() { 175 let cli = Cli::parse_from(["fake_prog", "update"]); 176 assert!(!cli.global_options.nowait); 177 } 178 179 #[test] it_should_wait()180 fn it_should_wait() { 181 let cli = Cli::parse_from(["fake_prog", "update"]); 182 assert_eq!(Wait::Yes, cli.should_wait()); 183 let should_wait: bool = cli.should_wait().into(); 184 assert!(should_wait); 185 } 186 187 #[test] it_should_not_wait()188 fn it_should_not_wait() { 189 let cli = Cli::parse_from(["fake_prog", "update", "--nowait"]); 190 assert_eq!(Wait::No, cli.should_wait()); 191 let should_wait: bool = cli.should_wait().into(); 192 assert!(!should_wait); 193 } 194 } 195