Migrate away from structopt

Clap 3 supports derive natively, so we don't need structopt anymore
(since it was based on clap 2).

Besides, sort the arguments to make positional ones first.

Bug: 246385183
Test: atest AuthFsHostTest ComposHostTestCases MicrodroidTestCase
Change-Id: I75e25ffcb63bf717be8deb9720e0fdf78a0c7ccf
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 7788702..523da35 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -16,6 +16,7 @@
         "libauthfs_fsverity_metadata",
         "libbinder_rs",
         "libcfg_if",
+        "libclap",
         "libfsverity_digests_proto_rust",
         "libfuse_rust",
         "liblibc",
@@ -24,7 +25,6 @@
         "libopenssl",
         "libprotobuf",
         "librpcbinder_rs",
-        "libstructopt",
         "libthiserror",
     ],
     prefer_rlib: true,
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index c09ed71..9ff0ae3 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -33,13 +33,13 @@
 //! the state is not persistent, thus only new file/directory are supported.
 
 use anyhow::{anyhow, bail, Result};
+use clap::Parser;
 use log::error;
 use protobuf::Message;
 use std::convert::TryInto;
 use std::fs::File;
 use std::num::NonZeroU8;
 use std::path::{Path, PathBuf};
-use structopt::StructOpt;
 
 mod common;
 mod file;
@@ -53,22 +53,21 @@
 use fsverity_digests_proto::fsverity_digests::FSVerityDigests;
 use fusefs::{AuthFs, AuthFsEntry, LazyVerifiedReadonlyFile};
 
-#[derive(StructOpt)]
+#[derive(Parser)]
 struct Args {
     /// Mount point of AuthFS.
-    #[structopt(parse(from_os_str))]
     mount_point: PathBuf,
 
     /// CID of the VM where the service runs.
-    #[structopt(long)]
+    #[clap(long)]
     cid: u32,
 
     /// Extra options to FUSE
-    #[structopt(short = "o")]
+    #[clap(short = 'o')]
     extra_options: Option<String>,
 
     /// Number of threads to serve FUSE requests.
-    #[structopt(short = "j")]
+    #[clap(short = 'j')]
     thread_number: Option<NonZeroU8>,
 
     /// A read-only remote file with integrity check. Can be multiple.
@@ -76,21 +75,21 @@
     /// For example, `--remote-ro-file 5:sha256-1234abcd` tells the filesystem to associate the
     /// file $MOUNTPOINT/5 with a remote FD 5, and has a fs-verity digest with sha256 of the hex
     /// value 1234abcd.
-    #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
+    #[clap(long, value_parser = parse_remote_ro_file_option)]
     remote_ro_file: Vec<OptionRemoteRoFile>,
 
     /// A read-only remote file without integrity check. Can be multiple.
     ///
     /// For example, `--remote-ro-file-unverified 5` tells the filesystem to associate the file
     /// $MOUNTPOINT/5 with a remote FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_ro_file_unverified: Vec<i32>,
 
     /// A new read-writable remote file with integrity check. Can be multiple.
     ///
     /// For example, `--remote-new-rw-file 5` tells the filesystem to associate the file
     /// $MOUNTPOINT/5 with a remote FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_new_rw_file: Vec<i32>,
 
     /// A read-only directory that represents a remote directory. The directory view is constructed
@@ -107,7 +106,7 @@
     /// include a file like /5/system/framework/framework.jar. "prefix/" tells the filesystem to
     /// strip the path (e.g. "system/") from the mount point to match the expected location of the
     /// remote FD (e.g. a directory FD of "/system" in the remote).
-    #[structopt(long, parse(try_from_str = parse_remote_new_ro_dir_option))]
+    #[clap(long, value_parser = parse_remote_new_ro_dir_option)]
     remote_ro_dir: Vec<OptionRemoteRoDir>,
 
     /// A new directory that is assumed empty in the backing filesystem. New files created in this
@@ -116,14 +115,15 @@
     ///
     /// For example, `--remote-new-rw-dir 5` tells the filesystem to associate $MOUNTPOINT/5
     /// with a remote dir FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_new_rw_dir: Vec<i32>,
 
     /// Enable debugging features.
-    #[structopt(long)]
+    #[clap(long)]
     debug: bool,
 }
 
+#[derive(Clone)]
 struct OptionRemoteRoFile {
     /// ID to refer to the remote file.
     remote_fd: i32,
@@ -132,6 +132,7 @@
     digest: String,
 }
 
+#[derive(Clone)]
 struct OptionRemoteRoDir {
     /// ID to refer to the remote dir.
     remote_dir_fd: i32,
@@ -305,7 +306,7 @@
 }
 
 fn try_main() -> Result<()> {
-    let args = Args::from_args_safe()?;
+    let args = Args::parse();
 
     let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
     android_logger::init_once(
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index a9d0da3..d8e7069 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -7,7 +7,7 @@
     srcs: ["src/main.rs"],
     rustlibs: [
         "libanyhow",
-        "libstructopt",
+        "libclap",
     ],
     prefer_rlib: true,
 }
diff --git a/microdroid/initrd/src/main.rs b/microdroid/initrd/src/main.rs
index 1023a40..69c6ae4 100644
--- a/microdroid/initrd/src/main.rs
+++ b/microdroid/initrd/src/main.rs
@@ -14,26 +14,23 @@
 
 //! Append bootconfig to initrd image
 use anyhow::Result;
-
+use clap::Parser;
 use std::fs::File;
 use std::io::{Read, Write};
 use std::path::PathBuf;
-use structopt::StructOpt;
 
 const FOOTER_ALIGNMENT: usize = 4;
 const ZEROS: [u8; 4] = [0u8; 4_usize];
 
-#[derive(StructOpt, Debug)]
+#[derive(Parser, Debug)]
 struct Args {
-    /// Output
-    #[structopt(parse(from_os_str), long = "output")]
-    output: PathBuf,
     /// Initrd (without bootconfig)
-    #[structopt(parse(from_os_str))]
     initrd: PathBuf,
     /// Bootconfig
-    #[structopt(parse(from_os_str))]
     bootconfigs: Vec<PathBuf>,
+    /// Output
+    #[clap(long = "output")]
+    output: PathBuf,
 }
 
 fn get_checksum(file_path: &PathBuf) -> Result<u32> {
@@ -67,7 +64,7 @@
 }
 
 fn try_main() -> Result<()> {
-    let args = Args::from_args_safe()?;
+    let args = Args::parse();
     attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
     Ok(())
 }
diff --git a/vm/Android.bp b/vm/Android.bp
index eac640e..7b016d4 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -12,6 +12,7 @@
         "android.system.virtualizationservice-rust",
         "libanyhow",
         "libbinder_rs",
+        "libclap",
         "libenv_logger",
         "liblibc",
         "liblog_rust",
@@ -19,7 +20,6 @@
         "librustutils",
         "libserde_json",
         "libserde",
-        "libstructopt",
         "libvmconfig",
         "libvmclient",
         "libzip",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index b35bd4b..0845897 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -24,110 +24,104 @@
 };
 use anyhow::{Context, Error};
 use binder::ProcessState;
+use clap::Parser;
 use create_idsig::command_create_idsig;
 use create_partition::command_create_partition;
 use run::{command_run, command_run_app};
 use rustutils::system_properties;
 use std::path::{Path, PathBuf};
-use structopt::clap::AppSettings;
-use structopt::StructOpt;
 
 #[derive(Debug)]
 struct Idsigs(Vec<PathBuf>);
 
-#[derive(StructOpt)]
-#[structopt(no_version, global_settings = &[AppSettings::DisableVersion])]
+#[derive(Parser)]
 enum Opt {
     /// Run a virtual machine with a config in APK
     RunApp {
-        /// Name of VM
-        #[structopt(long)]
-        name: Option<String>,
-
         /// Path to VM Payload APK
-        #[structopt(parse(from_os_str))]
         apk: PathBuf,
 
         /// Path to idsig of the APK
-        #[structopt(parse(from_os_str))]
         idsig: PathBuf,
 
         /// Path to the instance image. Created if not exists.
-        #[structopt(parse(from_os_str))]
         instance: PathBuf,
 
         /// Path to VM config JSON within APK (e.g. assets/vm_config.json)
         config_path: String,
 
+        /// Name of VM
+        #[clap(long)]
+        name: Option<String>,
+
         /// Detach VM from the terminal and run in the background
-        #[structopt(short, long)]
+        #[clap(short, long)]
         daemonize: bool,
 
         /// Path to file for VM console output.
-        #[structopt(long)]
+        #[clap(long)]
         console: Option<PathBuf>,
 
         /// Path to file for VM log output.
-        #[structopt(long)]
+        #[clap(long)]
         log: Option<PathBuf>,
 
         /// Path to file where ramdump is recorded on kernel panic
-        #[structopt(long)]
+        #[clap(long)]
         ramdump: Option<PathBuf>,
 
         /// Debug level of the VM. Supported values: "none" (default), "app_only", and "full".
-        #[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
+        #[clap(long, default_value = "none", value_parser = parse_debug_level)]
         debug: DebugLevel,
 
         /// Run VM in protected mode.
-        #[structopt(short, long)]
+        #[clap(short, long)]
         protected: bool,
 
         /// Memory size (in MiB) of the VM. If unspecified, defaults to the value of `memory_mib`
         /// in the VM config file.
-        #[structopt(short, long)]
+        #[clap(short, long)]
         mem: Option<u32>,
 
         /// Number of vCPUs in the VM. If unspecified, defaults to 1.
-        #[structopt(long)]
+        #[clap(long)]
         cpus: Option<u32>,
 
         /// Comma separated list of task profile names to apply to the VM
-        #[structopt(long)]
+        #[clap(long)]
         task_profiles: Vec<String>,
 
         /// Paths to extra idsig files.
-        #[structopt(long = "extra-idsig")]
+        #[clap(long = "extra-idsig")]
         extra_idsigs: Vec<PathBuf>,
     },
     /// Run a virtual machine
     Run {
-        /// Name of VM
-        #[structopt(long)]
-        name: Option<String>,
-
         /// Path to VM config JSON
-        #[structopt(parse(from_os_str))]
         config: PathBuf,
 
+        /// Name of VM
+        #[clap(long)]
+        name: Option<String>,
+
         /// Detach VM from the terminal and run in the background
-        #[structopt(short, long)]
+        #[clap(short, long)]
         daemonize: bool,
 
         /// Number of vCPUs in the VM. If unspecified, defaults to 1.
-        #[structopt(long)]
+        #[clap(long)]
         cpus: Option<u32>,
 
         /// Comma separated list of task profile names to apply to the VM
-        #[structopt(long)]
+        #[clap(long)]
         task_profiles: Vec<String>,
 
         /// Path to file for VM console output.
-        #[structopt(long)]
+        #[clap(long)]
         console: Option<PathBuf>,
 
         /// Path to file for VM log output.
-        #[structopt(long)]
+        #[clap(long)]
         log: Option<PathBuf>,
     },
     /// Stop a virtual machine running in the background
@@ -142,23 +136,22 @@
     /// Create a new empty partition to be used as a writable partition for a VM
     CreatePartition {
         /// Path at which to create the image file
-        #[structopt(parse(from_os_str))]
         path: PathBuf,
 
         /// The desired size of the partition, in bytes.
         size: u64,
 
         /// Type of the partition
-        #[structopt(short="t", long="type", default_value="raw", parse(try_from_str=parse_partition_type))]
+        #[clap(short = 't', long = "type", default_value = "raw",
+               value_parser = parse_partition_type)]
         partition_type: PartitionType,
     },
     /// Creates or update the idsig file by digesting the input APK file.
     CreateIdsig {
         /// Path to VM Payload APK
-        #[structopt(parse(from_os_str))]
         apk: PathBuf,
+
         /// Path to idsig of the APK
-        #[structopt(parse(from_os_str))]
         path: PathBuf,
     },
 }
@@ -182,7 +175,7 @@
 
 fn main() -> Result<(), Error> {
     env_logger::init();
-    let opt = Opt::from_args();
+    let opt = Opt::parse();
 
     // We need to start the thread pool for Binder to work properly, especially link_to_death.
     ProcessState::start_thread_pool();