Add command to update generated crates

This command copies and transforms the generated crates
so that they can be used with git dependencies.

Change-Id: I4f1b8c29c9d937ee40776234f5855211df8d7cfe
diff --git a/Cargo.lock b/Cargo.lock
index f4de997..a914a97 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6,6 +6,7 @@
  "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -147,6 +148,14 @@
 ]
 
 [[package]]
+name = "toml"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "uname"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -203,6 +212,7 @@
 "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
 "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
 "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
+"checksum toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0601da6c97135c8d330c7a13a013ca6cd4143221b01de2f8d4edc50a9e551c7"
 "checksum uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
 "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
 "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
diff --git a/Cargo.toml b/Cargo.toml
index ff6b025..f5679cd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,4 +8,5 @@
 serde = "1.0.9"
 serde_derive = "1.0.9"
 serde_json = "1.0.2"
+toml = "0.4.2"
 uname = "0.1.1"
\ No newline at end of file
diff --git a/fargo-test/Cargo.lock b/fargo-test/Cargo.lock
index 8089307..10b166e 100644
--- a/fargo-test/Cargo.lock
+++ b/fargo-test/Cargo.lock
@@ -2,16 +2,31 @@
 name = "fargo-test"
 version = "0.1.0"
 dependencies = [
+ "apps_mozart_services_buffers 0.1.0 (git+https://fuchsia.googlesource.com/fuchsia-crates)",
  "magenta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mxruntime 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "apps_mozart_services_buffers"
+version = "0.1.0"
+source = "git+https://fuchsia.googlesource.com/fuchsia-crates#108eac120fefdb479fce49ec73b4d9ca1ad714ce"
+dependencies = [
+ "fidl 0.1.0 (git+https://fuchsia.googlesource.com/fuchsia-crates)",
+ "magenta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "bitflags"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "byteorder"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "conv"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -25,6 +40,15 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "fidl"
+version = "0.1.0"
+source = "git+https://fuchsia.googlesource.com/fuchsia-crates#108eac120fefdb479fce49ec73b4d9ca1ad714ce"
+dependencies = [
+ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "magenta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "magenta"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -60,9 +84,12 @@
 ]
 
 [metadata]
+"checksum apps_mozart_services_buffers 0.1.0 (git+https://fuchsia.googlesource.com/fuchsia-crates)" = "<none>"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
+"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
 "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
 "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
+"checksum fidl 0.1.0 (git+https://fuchsia.googlesource.com/fuchsia-crates)" = "<none>"
 "checksum magenta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92159d95603bd96e44850aa5b458656a372b3324940f3523a20d08f0f9cd8760"
 "checksum magenta-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07fd18dd6819f5a24387638769d95fe6e9145eb15c1756d721620604306b7b72"
 "checksum mxruntime 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d267fa561985cefb55172834a020f0e77339dbda7287482fd4b58936e792089a"
diff --git a/fargo-test/Cargo.toml b/fargo-test/Cargo.toml
index 5730de4..e641a4c 100644
--- a/fargo-test/Cargo.toml
+++ b/fargo-test/Cargo.toml
@@ -6,6 +6,7 @@
 [dependencies]
 magenta = "0.1.0"
 mxruntime = "0.1.0"
+apps_mozart_services_buffers = { git = "https://fuchsia.googlesource.com/fuchsia-crates" }
 
 [[test]]
 name = "bork"
diff --git a/src/main.rs b/src/main.rs
index 4e3022f..adb6b57 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,12 +7,14 @@
 #[macro_use]
 extern crate serde_derive;
 extern crate serde_json;
+extern crate toml;
 extern crate uname;
 
 mod cargo_interop;
 mod device;
 mod sdk;
 mod utils;
+mod update_crates;
 
 use cargo_interop::Artifact;
 use clap::{App, Arg, SubCommand};
@@ -21,6 +23,7 @@
 use std::path::PathBuf;
 use std::process::Command;
 use std::str;
+use update_crates::update_crates;
 use utils::strip_binary;
 
 fn build_tests(verbose: bool, release: bool, test_target: &String) -> bool {
@@ -267,6 +270,13 @@
                 .help("Start a simulator with graphics enabled")))
         .subcommand(SubCommand::with_name("ssh")
             .about("Open a shell on Fuchsia device or emulator"))
+        .subcommand(SubCommand::with_name("update-crates")
+            .about("Update the FIDL generated crates")
+            .arg(Arg::with_name("target")
+                .long("target")
+                .value_name("target")
+                .required(true)
+                .help("Target directory for updated crates")))
         .get_matches();
 
     let verbose = matches.is_present("verbose");
@@ -295,5 +305,8 @@
         start_emulator(restart_matches.is_present("graphics"));
     } else if let Some(_) = matches.subcommand_matches("ssh") {
         ssh("");
+    } else if let Some(update_matches) = matches.subcommand_matches("update-crates") {
+        let update_target = update_matches.value_of("target").unwrap().to_string();
+        update_crates(&update_target);
     }
 }
diff --git a/src/update_crates.rs b/src/update_crates.rs
new file mode 100644
index 0000000..cf1e6cc
--- /dev/null
+++ b/src/update_crates.rs
@@ -0,0 +1,131 @@
+use sdk::fuchsia_root;
+use std::collections::HashMap;
+use std::io;
+use std::io::{Read, Write};
+use std::fs;
+use std::fs::File;
+use std::path::{Path, PathBuf};
+use toml;
+
+static LICENSE_RS_FILE_HEADER: &'static str =
+    r#"// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+"#;
+
+static LICENSE_TOML_FILE_HEADER: &'static str =
+    r#"# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"#;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+struct Dependency {
+    path: Option<String>,
+    git: Option<String>,
+    version: Option<String>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+struct Package {
+    name: String,
+    version: String,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+struct Library {
+    path: String,
+    name: String,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+struct Cargo {
+    package: Package,
+    lib: Option<Library>,
+    dependencies: Option<HashMap<String, Dependency>>,
+}
+
+impl Cargo {
+    fn rewrite(&self) -> Cargo {
+        let new_deps = if self.dependencies.is_some() {
+            let mut new_deps_map: HashMap<String, Dependency> = HashMap::new();
+            let old_deps = self.dependencies.clone().unwrap().clone();
+            for (k, mut v) in old_deps {
+                v.path = None;
+                if k == "magenta" || k == "mxruntime" {
+                    v.version = Some("0.1.0".to_string());
+                } else {
+                    v.git = Some("https://fuchsia.googlesource.com/fuchsia-crates".to_string());
+                }
+                new_deps_map.insert(k, v);
+            }
+            Some(new_deps_map)
+        } else {
+            None
+        };
+        Cargo {
+            package: self.package.clone(),
+            lib: self.lib.clone(),
+            dependencies: new_deps,
+        }
+    }
+}
+
+fn look_for_crates(dir: &Path, root: &Path, target: &Path) -> io::Result<()> {
+    if dir.is_dir() {
+        for entry in fs::read_dir(dir)? {
+            let entry = entry?;
+            let path = entry.path();
+            if path.is_dir() {
+                look_for_crates(&path, root, target)?;
+            } else {
+                if let Some(file_name) = path.file_name() {
+                    let partial_parent = path.parent().unwrap().strip_prefix(root).unwrap();
+                    if file_name.to_str() == Some("Cargo.toml") {
+                        let mut input = String::new();
+                        File::open(&path)
+                            .and_then(|mut f| f.read_to_string(&mut input))
+                            .unwrap();
+                        let decoded: Cargo = toml::from_str(&input).unwrap();
+                        if decoded.lib.is_some() {
+                            let rewritten = decoded.rewrite();
+                            let target_parent = target.join(partial_parent);
+                            fs::create_dir_all(&target_parent).unwrap();
+                            let toml2 = toml::to_string(&rewritten).unwrap();
+                            let target_cargo = target_parent.join("Cargo.toml");
+                            let mut file = File::create(target_cargo)?;
+                            file.write_all(&LICENSE_TOML_FILE_HEADER.as_bytes())?;
+                            file.write_all(toml2.into_bytes().as_slice())?;
+                        }
+                    } else {
+                        if let Some(extension) = path.extension() {
+                            if extension.to_str() == Some("rs") {
+                                let target_parent = target.join(partial_parent);
+                                fs::create_dir_all(&target_parent).unwrap();
+                                let mut input = String::new();
+                                File::open(&path)
+                                    .and_then(|mut f| f.read_to_string(&mut input))
+                                    .unwrap();
+                                let target_rust_file = target_parent.join(file_name);
+                                let mut file = File::create(target_rust_file)?;
+                                file.write_all(&LICENSE_RS_FILE_HEADER.as_bytes())?;
+                                file.write_all(input.into_bytes().as_slice())?;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    Ok(())
+}
+pub fn update_crates(target: &String) {
+    let gen_root = fuchsia_root().join("out/debug-x86-64/gen");
+    let crate_sources = vec!["application", "apps/mozart", "apps/ledger", "apps/modular"];
+    for one_source in crate_sources {
+        let one_source_path = gen_root.join(one_source);
+        look_for_crates(&one_source_path, &gen_root, &PathBuf::from(target)).unwrap();
+    }
+}