Allow deps to be renamed (#285)

* Allow deps to be renamed

* Add tests for renamed crates
diff --git a/proto/proto.bzl b/proto/proto.bzl
index 15f1354..bd60a15 100644
--- a/proto/proto.bzl
+++ b/proto/proto.bzl
@@ -167,6 +167,7 @@
             root = lib_rs,
             srcs = srcs,
             deps = compile_deps,
+            aliases = {},
             output = rust_lib,
             edition = proto_toolchain.edition,
         ),
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index ac5b97a..60fb1a8 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -120,6 +120,7 @@
             root = lib_rs,
             srcs = ctx.files.srcs,
             deps = ctx.attr.deps,
+            aliases = ctx.attr.aliases,
             output = rust_lib,
             edition = _get_edition(ctx, toolchain),
         ),
@@ -143,6 +144,7 @@
             root = _crate_root_src(ctx, "main.rs"),
             srcs = ctx.files.srcs,
             deps = ctx.attr.deps,
+            aliases = ctx.attr.aliases,
             output = output,
             edition = _get_edition(ctx, toolchain),
         ),
@@ -168,6 +170,7 @@
             root = crate.root,
             srcs = crate.srcs + ctx.files.srcs,
             deps = crate.deps + ctx.attr.deps,
+            aliases = ctx.attr.aliases,
             output = test_binary,
             edition = crate.edition,
         )
@@ -186,6 +189,7 @@
             root = _crate_root_src(ctx),
             srcs = ctx.files.srcs,
             deps = ctx.attr.deps,
+            aliases = ctx.attr.aliases,
             output = test_binary,
             edition = _get_edition(ctx, toolchain),
         )
@@ -275,6 +279,13 @@
             linking a native library.
         """),
     ),
+    "aliases": attr.label_keyed_string_dict(
+        doc = _tidy("""
+            Remap crates to a new name or moniker for linkage to this target
+
+            These are other `rust_library` targets and will be presented as the new name given.
+        """),
+    ),
     "crate_features": attr.string_list(
         doc = _tidy("""
             List of features to enable for this crate.
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 0839ae4..68fb8e3 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -32,11 +32,19 @@
         "root": "File: The source File entrypoint to this crate, eg. lib.rs",
         "srcs": "List[File]: All source Files that are part of the crate.",
         "deps": "List[Provider]: This crate's (rust or cc) dependencies' providers.",
+        "aliases": "Dict[Label, String]: Renamed and aliased crates",
         "output": "File: The output File that will be produced, depends on crate type.",
         "edition": "str: The edition of this crate.",
     },
 )
 
+AliasableDep = provider(
+    fields = {
+        "name": "str",
+        "dep": "CrateInfo",
+    }
+)
+
 DepInfo = provider(
     fields = {
         "direct_crates": "depset[CrateInfo]",
@@ -84,7 +92,7 @@
     else:
         return libname
 
-def collect_deps(deps, toolchain):
+def collect_deps(deps, aliases, toolchain):
     """
     Walks through dependencies and collects the transitive dependencies.
 
@@ -101,10 +109,17 @@
     transitive_crates = depset()
     transitive_dylibs = depset(order = "topological")  # dylib link flag ordering matters.
     transitive_staticlibs = depset()
+
+    aliases = {k.label: v for k,v in aliases.items()}
     for dep in deps:
         if CrateInfo in dep:
             # This dependency is a rust_library
-            direct_crates += [dep[CrateInfo]]
+            direct_dep = dep[CrateInfo]
+            aliasable_dep = AliasableDep(
+                name = aliases.get(dep.label, direct_dep.name),
+                dep = direct_dep,
+            )
+            direct_crates += [aliasable_dep]
             transitive_crates = depset([dep[CrateInfo]], transitive = [transitive_crates])
             transitive_crates = depset(transitive = [transitive_crates, dep[DepInfo].transitive_crates])
             transitive_dylibs = depset(transitive = [transitive_dylibs, dep[DepInfo].transitive_dylibs])
@@ -197,6 +212,7 @@
 
     dep_info = collect_deps(
         crate_info.deps,
+        crate_info.aliases,
         toolchain,
     )
 
@@ -389,7 +405,7 @@
     )
 
 def _crate_to_link_flag(crate_info):
-    return ["--extern", "{}={}".format(crate_info.name, crate_info.output.path)]
+    return ["--extern", "{}={}".format(crate_info.name, crate_info.dep.output.path)]
 
 def _get_crate_dirname(crate):
     return crate.output.dirname
diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl
index db69b67..1e90e28 100644
--- a/rust/private/rustdoc_test.bzl
+++ b/rust/private/rustdoc_test.bzl
@@ -67,7 +67,7 @@
     link_search_flags = []
 
     link_flags += ["--extern=" + crate.name + "=" + crate.output.short_path]
-    link_flags += ["--extern=" + c.name + "=" + c.output.short_path for c in d.direct_crates.to_list()]
+    link_flags += ["--extern=" + c.name + "=" + c.dep.output.short_path for c in d.direct_crates.to_list()]
     link_search_flags += ["-Ldependency={}".format(dirname(c.output.short_path)) for c in d.transitive_crates.to_list()]
 
     link_flags += ["-ldylib=" + get_lib_name(lib) for lib in d.transitive_dylibs.to_list()]
diff --git a/test/renamed_deps/BUILD b/test/renamed_deps/BUILD
new file mode 100644
index 0000000..04408be
--- /dev/null
+++ b/test/renamed_deps/BUILD
@@ -0,0 +1,48 @@
+load(
+    "//rust:rust.bzl",
+    "rust_library",
+    "rust_test",
+)
+
+rust_library(
+    name = "mod1",
+    srcs = ["mod1.rs"],
+)
+
+rust_library(
+    name = "mod2",
+    srcs = ["mod2.rs"],
+    deps = [":mod1"],
+)
+
+rust_library(
+    name = "mod3",
+    srcs = ["mod3.rs"],
+    aliases = {
+        ":mod1": "alias_a",
+        ":mod2": "alias_b",
+    },
+    deps = [
+        ":mod1",
+        ":mod2",
+    ],
+)
+
+rust_test(
+    name = "mod1_test",
+    crate = ":mod1",
+)
+
+rust_test(
+    name = "mod2_test",
+    crate = ":mod2",
+)
+
+rust_test(
+    name = "mod3_test",
+    crate = ":mod3",
+    aliases = {
+        ":mod1": "alias_a",
+        ":mod2": "alias_b",
+    },
+)
diff --git a/test/renamed_deps/mod1.rs b/test/renamed_deps/mod1.rs
new file mode 100644
index 0000000..a66f490
--- /dev/null
+++ b/test/renamed_deps/mod1.rs
@@ -0,0 +1,11 @@
+pub fn world() -> String {
+    "world".to_owned()
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn test_world() {
+        assert_eq!(super::world(), "world");
+    }
+}
diff --git a/test/renamed_deps/mod2.rs b/test/renamed_deps/mod2.rs
new file mode 100644
index 0000000..cff993b
--- /dev/null
+++ b/test/renamed_deps/mod2.rs
@@ -0,0 +1,22 @@
+extern crate mod1;
+
+pub fn greeter(name: &str) -> String {
+    format!("Hello, {}!", name)
+}
+
+pub fn default_greeter() -> String {
+    greeter(&mod1::world())
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn test_greeter() {
+        assert_eq!(super::greeter("Bob"), "Hello, Bob!");
+    }
+
+    #[test]
+    fn test_default_greeter() {
+        assert_eq!(super::default_greeter(), "Hello, world!");
+    }
+}
diff --git a/test/renamed_deps/mod3.rs b/test/renamed_deps/mod3.rs
new file mode 100644
index 0000000..4942bf2
--- /dev/null
+++ b/test/renamed_deps/mod3.rs
@@ -0,0 +1,38 @@
+// This crate depends on 2 crates with one of them depending on the other one.
+// If the crates are not set correctly in the dependency chain, this crate won't
+// compile. The order of the `extern crate` is important to trigger that bug.
+extern crate alias_a;
+extern crate alias_b;
+
+pub fn greet(name: &str) {
+    println!("{}", alias_b::greeter(name))
+}
+
+pub fn greet_default() {
+    println!("{}", alias_b::default_greeter())
+}
+
+/// This is a documentation.
+///
+/// # Examples
+///
+/// ```rust
+/// assert!(
+///   mod3::am_i_the_world("world") == true
+/// );
+/// assert!(
+///   mod3::am_i_the_world("myself") == false
+/// );
+/// ```
+pub fn am_i_the_world(me: &str) -> bool {
+    return me == alias_a::world();
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn test_am_i_the_world() {
+        assert!(super::am_i_the_world("world"));
+        assert!(!super::am_i_the_world("bob"));
+    }
+}