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 #[clap(long, short, global = true, value_delimiter = ',')] 63 pub partitions: Option<Vec<String>>, 64 // TODO(rbraunstein): Validate relative, not absolute paths. 65 /// If unset defaults to ANDROID_PRODUCT_OUT env variable. 66 #[clap(long = "product_out", global = true)] 67 pub product_out: Option<String>, 68 /// Do not make any modification if more than this many are needed 69 #[clap(long, short, default_value_t = 400, global = true)] 70 pub max_allowed_changes: usize, 71 /// If passed, use the device, otherwise use the only connected device or ANDROID_SERIAL env value. 72 #[clap(long, short, global = true)] 73 pub serial: Option<String>, 74 /// Override the type of restart that happens after an update. 75 #[clap(long = "restart", short, global = true, value_enum, default_value_t=RestartChoice::Auto)] 76 pub restart_choice: RestartChoice, 77 /// Path to config file. Uses $HOME/.config/asuite/adevice-tracking.json if unset. 78 #[clap(long = "config", global = true)] 79 pub config_path: Option<String>, 80 // Don't wait for device to become available after restarting it. 81 #[clap(long = "nowait", global = true, alias = "no_wait", alias = "no-wait")] 82 pub nowait: bool, 83 } 84 85 #[derive(clap::ValueEnum, Clone, Debug)] 86 pub enum Verbosity { 87 /// Only show minimal information. 88 None, 89 /// Show all adb operations. 90 Details, 91 /// For debugging internals of tool and timings. 92 Debug, 93 } 94 95 /// Allows you to choose how to reboot or to not reboot. 96 #[derive(clap::ValueEnum, Clone, Debug)] 97 pub enum RestartChoice { 98 /// Let the system choose the restart based on the files changed. 99 Auto, 100 /// Don't restart. 101 None, 102 /// Always do a full system reboot after updates. 103 Reboot, 104 /// Always do a framework restart restart after updates. 105 Restart, 106 } 107 108 #[derive(Clone, Debug, PartialEq)] 109 pub enum Wait { 110 Yes, 111 No, 112 } 113 114 impl From<Wait> for bool { from(w: Wait) -> bool115 fn from(w: Wait) -> bool { 116 match w { 117 Wait::Yes => true, 118 Wait::No => false, 119 } 120 } 121 } 122 123 impl Cli { 124 /// Decide if the options indicate that we should wait for the device. 125 /// Exists in case the cli options get more complicated like --wait=false should_wait(&self) -> Wait126 pub fn should_wait(&self) -> Wait { 127 match self.global_options.nowait { 128 true => Wait::No, 129 false => Wait::Yes, 130 } 131 } 132 } 133 134 #[cfg(test)] 135 mod tests { 136 use crate::cli::Wait; 137 138 use super::Cli; 139 use clap::Parser; 140 141 #[test] nowait_works()142 fn nowait_works() { 143 let cli = Cli::parse_from(["fake_prog", "update", "--nowait"]); 144 assert!(cli.global_options.nowait); 145 } 146 147 #[test] no_wait_alias_works()148 fn no_wait_alias_works() { 149 let cli = Cli::parse_from(["fake_prog", "update", "--no_wait"]); 150 assert!(cli.global_options.nowait); 151 } 152 153 #[test] unset_nowait_is_none()154 fn unset_nowait_is_none() { 155 let cli = Cli::parse_from(["fake_prog", "update"]); 156 assert!(!cli.global_options.nowait); 157 } 158 159 #[test] it_should_wait()160 fn it_should_wait() { 161 let cli = Cli::parse_from(["fake_prog", "update"]); 162 assert_eq!(Wait::Yes, cli.should_wait()); 163 let should_wait: bool = cli.should_wait().into(); 164 assert!(should_wait); 165 } 166 167 #[test] it_should_not_wait()168 fn it_should_not_wait() { 169 let cli = Cli::parse_from(["fake_prog", "update", "--nowait"]); 170 assert_eq!(Wait::No, cli.should_wait()); 171 let should_wait: bool = cli.should_wait().into(); 172 assert!(!should_wait); 173 } 174 } 175