Use structopt

Change-Id: I43c5b47d03f9b33d7dbd04ea8a84c46a8c3ff0fe
diff --git a/Cargo.lock b/Cargo.lock
index f902d83..fc8cc27 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -107,6 +107,7 @@
  "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -176,6 +177,14 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "inotify"
 version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -310,6 +319,16 @@
 ]
 
 [[package]]
+name = "proc-macro-error"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "proc-macro2"
 version = "0.4.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -318,6 +337,14 @@
 ]
 
 [[package]]
+name = "proc-macro2"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "quote"
 version = "0.6.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -326,6 +353,14 @@
 ]
 
 [[package]]
+name = "quote"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "redox_syscall"
 version = "0.1.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -384,6 +419,27 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "structopt"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "syn"
 version = "0.15.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -394,6 +450,16 @@
 ]
 
 [[package]]
+name = "syn"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "synstructure"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -429,6 +495,11 @@
 ]
 
 [[package]]
+name = "unicode-segmentation"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "unicode-width"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -439,6 +510,11 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "vec_map"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -519,6 +595,7 @@
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
 "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
 "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
 "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
@@ -533,8 +610,11 @@
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
 "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
 "checksum notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3572d71f13ea8ed41867accd971fd564aa75934cf7a1fae03ddb8c74a8a49943"
+"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097"
 "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
+"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
 "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
+"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
 "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
 "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
 "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
@@ -544,13 +624,18 @@
 "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
 "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+"checksum structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ac9d6e93dd792b217bf89cda5c14566e3043960c6f9da890c2ba5d09d07804c"
+"checksum structopt-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ae9e5165d463a0dea76967d021f8d0f9316057bf5163aa2a4843790e842ff37"
 "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e"
+"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
 "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
 "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
 "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
 "checksum uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
+"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
 "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
 "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
 "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
diff --git a/Cargo.toml b/Cargo.toml
index 6c5f426..d07347e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@
 serde = "1.0"
 serde_derive = "1.0"
 serde_json = "1.0"
+structopt = "0.3"
 toml = "0.4"
 
 [[bin]]
diff --git a/src/bin/fargo.rs b/src/bin/fargo.rs
index 895fa0d..5ef2e18 100644
--- a/src/bin/fargo.rs
+++ b/src/bin/fargo.rs
@@ -1,10 +1,10 @@
 #![recursion_limit = "1024"]
 
-use fargo::run;
+use fargo::run2::run2;
 use itertools::Itertools;
 
 fn main() {
-    if let Err(ref e) = run() {
+    if let Err(ref e) = run2() {
         println!("error: {}", e.iter_chain().join(", "));
         ::std::process::exit(1);
     }
diff --git a/src/lib.rs b/src/lib.rs
index b9b74a9..9e2dd29 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,7 @@
 mod cross;
 mod device;
 mod package;
+pub mod run2;
 mod sdk;
 mod utils;
 
diff --git a/src/run2.rs b/src/run2.rs
new file mode 100644
index 0000000..132f7e5
--- /dev/null
+++ b/src/run2.rs
@@ -0,0 +1,244 @@
+use crate::{
+    random_story_name, run_binary, run_program_on_target, run_switches_to_mode, FuchsiaConfig,
+    RunCargoOptions, TargetOptions, DEFAULT_MOD_NAME,
+};
+use failure::Error;
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+enum FargoCommand {
+    /// Auto build and test in Fuchsia device or emulator
+    Autotest,
+    /// Build binary targeting Fuchsia device or emulator
+    Build,
+    /// Build rustc targeting Fuchsia
+    BuildRustc,
+    /// Run a cargo command for Fuchsia. Use -- to indicate that all following arguments should be
+    /// passed to cargo.
+    Cargo,
+    /// Check binary targeting Fuchsia device or emulator
+    Check,
+    /// Run a configure script for the cross compilation environment
+    Configure,
+    /// Build a package's documentation
+    Doc,
+    /// Enable networking for a running emulator
+    EnableNetworking,
+    /// Run cargo fmt using the Fuchsia toolchain
+    Fmt,
+    /// List visible Fuchsia devices
+    ListDevices,
+    /// Make a Fuchsia package from an unstripped binary
+    MakePackage,
+    /// Run pkg-config for the cross compilation environment
+    PkgConfig,
+    /// Stop all Fuchsia emulators and start a new one
+    Restart,
+    /// Act as as custom runner for Cargo targeting a Fuchsia device
+    RunOnTarget(RunOnTarget),
+    /// Run binary on Fuchsia device or emulator
+    Run(Run),
+    /// Open a shell on Fuchsia device or emulator
+    Ssh,
+    /// Start a Fuchsia emulator
+    Start,
+    /// Stop all Fuchsia emulators
+    Stop,
+    /// Run unit tests on Fuchsia device or emulator
+    Test,
+    /// Write a .cargo/config file to allow cargo to operate correctly for Fuchsia
+    WriteConfig,
+}
+
+#[derive(Debug, StructOpt)]
+struct Run {
+    /// Build artifacts in release mode, with optimizations.
+    #[structopt(long)]
+    release: bool,
+
+    /// Use run to run the binary.
+    #[structopt(long)]
+    run_with_run: bool,
+
+    /// Use tiles_ctl add to run the binary.
+    #[structopt(long)]
+    run_with_tiles: bool,
+
+    /// Use Use sessionctl to run the binary.
+    #[structopt(long)]
+    run_with_sessionctl: bool,
+
+    /// Name of app as it appears in the manifest file.
+    #[structopt(long, default_value = "app")]
+    app_name: String,
+
+    /// Name of story to pass to sessionctl.
+    #[structopt(short = "n", long)]
+    story_name: Option<String>,
+
+    /// Name of mod to pass to sessionctl.
+    #[structopt(short, long)]
+    mod_name: Option<String>,
+
+    /// Path to sandbox file to use when running.
+    #[structopt(long)]
+    cmx_path: Option<PathBuf>,
+
+    /// Package to build.
+    #[structopt(short, long)]
+    package: Option<String>,
+
+    /// Name of the bin target to run.
+    #[structopt(long)]
+    bin: Option<String>,
+
+    /// Run a specific example from the examples/ dir.
+    #[structopt(long)]
+    example: Option<String>,
+}
+
+#[derive(Debug, StructOpt)]
+struct RunOnTarget {
+    /// Path to the binary to run.
+    binary_to_run: String,
+
+    /// Use run to run the binary.
+    #[structopt(long)]
+    run_with_run: bool,
+
+    /// Use tiles_ctl add to run the binary.
+    #[structopt(long)]
+    run_with_tiles: bool,
+
+    /// Use Use sessionctl to run the binary.
+    #[structopt(long)]
+    run_with_sessionctl: bool,
+
+    /// Name of app as it appears in the manifest file.
+    #[structopt(long, default_value = "app")]
+    app_name: String,
+
+    /// Name of story to pass to sessionctl.
+    #[structopt(short = "n", long)]
+    story_name: Option<String>,
+
+    /// Name of mod to pass to sessionctl.
+    #[structopt(short, long)]
+    mod_name: Option<String>,
+
+    /// Path to sandbox file to use when running.
+    #[structopt(long)]
+    cmx_path: Option<PathBuf>,
+
+    /// Additional arguments for the binary.
+    runner_args: Vec<String>,
+
+    #[structopt(long)]
+    no_capture: bool,
+}
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "fargo", about = "Fargo is a prototype Fuchsia-specific wrapper around Cargo.")]
+struct FargoOption {
+    /// Print verbose output while performing commands
+    #[structopt(short, long)]
+    verbose: bool,
+
+    /// Disable the setting of CC, AR and such environmental variables
+    #[structopt(long)]
+    disable_cross_env: bool,
+
+    /// Name of device to target, needed if there are multiple devices visible on the network
+    #[structopt(short = "N", long)]
+    device_name: Option<String>,
+
+    /// Path to Cargo.toml
+    #[structopt(long, global = true)]
+    manifest_path: Option<PathBuf>,
+
+    #[structopt(subcommand)]
+    command: FargoCommand,
+}
+
+fn to_opt_str(value: &Option<String>) -> Option<&str> {
+    value.as_ref().map(String::as_str)
+}
+
+#[doc(hidden)]
+pub fn run2() -> Result<(), Error> {
+    let opt = FargoOption::from_args();
+    println!("{:?}", opt);
+
+    let verbose = opt.verbose;
+    let fuchsia_config = FuchsiaConfig::new_from_fx_exec()?;
+    if verbose {
+        println!("fuchsia_config = {:#?}", fuchsia_config);
+    }
+
+    let target_options =
+        TargetOptions::new(&fuchsia_config, opt.device_name.as_ref().map(String::as_str));
+    let run_cargo_options = RunCargoOptions { verbose, ..RunCargoOptions::default() };
+
+    match opt.command {
+        FargoCommand::Run(run) => {
+            println!("Want to run {:#?}", run);
+            let mut params = vec![];
+
+            if let Some(package) = run.package.as_ref() {
+                params.push("--package");
+                params.push(package);
+            }
+            if let Some(bin) = run.bin.as_ref() {
+                params.push("--bin");
+                params.push(bin);
+            }
+
+            if let Some(example) = run.example.as_ref() {
+                params.push("--example");
+                params.push(example);
+            }
+
+            let run_mode =
+                run_switches_to_mode(run.run_with_tiles, run.run_with_run, run.run_with_sessionctl);
+            return run_binary(
+                &run_cargo_options
+                    .release(run.release)
+                    .run_mode(run_mode)
+                    .story_name(&to_opt_str(&run.story_name))
+                    .mod_name(&to_opt_str(&run.mod_name))
+                    .app_name(&Some(&run.app_name))
+                    .manifest_path(opt.manifest_path)
+                    .cmx_path(run.cmx_path),
+                &target_options,
+                &params,
+            );
+        }
+        FargoCommand::RunOnTarget(run_on_target) => {
+            let run_cargo_options = run_cargo_options.cmx_path(run_on_target.cmx_path);
+            let run_params = run_on_target.runner_args;
+            let run_mode = run_switches_to_mode(
+                run_on_target.run_with_tiles,
+                run_on_target.run_with_run,
+                run_on_target.run_with_sessionctl,
+            );
+
+            return run_program_on_target(
+                &run_on_target.binary_to_run,
+                verbose,
+                run_on_target.no_capture,
+                &fuchsia_config,
+                &target_options,
+                run_mode,
+                &run_cargo_options.cmx_path,
+                &run_on_target.story_name.unwrap_or(random_story_name()),
+                &run_on_target.mod_name.unwrap_or(DEFAULT_MOD_NAME.to_string()),
+                &run_on_target.app_name,
+                &[],
+                None,
+            );
+        }
+        _ => println!("command {:#?} not yet implemented", opt.command),
+    }
+    Ok(())
+}