Add rust LSP utilities

Added the rust analyzer rules needed to generate a rust-project.json.
Once generated, this will be picked up by the rust analyzer server to
provide better language server features.
To generate it you can `bazel run //tools:gen_rust_project`

Change-Id: I5b05fa6f891ac3710b6f60d98e8be792db740a47
Signed-off-by: Adam Snaider <adsnaider@gmail.com>
diff --git a/.gitignore b/.gitignore
index 8570036..3f27e94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,5 @@
 
 node_modules
 /.aspect
+
+/rust-project.json
diff --git a/README.md b/README.md
index 29b57b2..145e893 100644
--- a/README.md
+++ b/README.md
@@ -320,6 +320,16 @@
 implies that the roboRIO will also have a 169.254.*.* IP addresss, and
 that the only simple way to figure it out is to use mDNS.
 
+### LSP Setup for Rust
+
+You can run `bazel run //tools:gen_rust_project` to generate a rust-project.json
+file which rust-analyzer will pick up. You will need to execute this rule
+periodically as it will be outdated whenever the BUILD files change.
+
+> **Note** that there's currently no way to tell rust-analyzer *how* to compile
+the code, so while it will give you completion support, go to definition, and
+other niceties, it won't show compilation errors or warnings at this point.
+
 
 ### Other resources
   1. Intro to [AOS](./aos/README.md), our robot Operating System
diff --git a/WORKSPACE b/WORKSPACE
index 51f9c8f..f65e4ee 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1089,7 +1089,9 @@
     path = "third_party/rules_rust",
 )
 
-load("@rules_rust//rust:repositories.bzl", "rust_repository_set")
+load("@rules_rust//rust:repositories.bzl", "rust_analyzer_toolchain_repository", "rust_repository_set")
+
+RUST_VERSION = "1.70.0"
 
 rust_repository_set(
     name = "rust",
@@ -1102,8 +1104,8 @@
         "aarch64-unknown-linux-gnu",
     ],
     register_toolchain = False,
-    rustfmt_version = "1.70.0",
-    versions = ["1.70.0"],
+    rustfmt_version = RUST_VERSION,
+    versions = [RUST_VERSION],
 )
 
 load("@rules_rust//crate_universe:repositories.bzl", "crate_universe_dependencies")
@@ -1157,7 +1159,7 @@
     ],
     rust_toolchain_cargo_template = "@rust__{triple}__{channel}_tools//:bin/{tool}",
     rust_toolchain_rustc_template = "@rust__{triple}__{channel}_tools//:bin/{tool}",
-    rust_version = "1.70.0",
+    rust_version = RUST_VERSION,
     supported_platform_triples = [
         "x86_64-unknown-linux-gnu",
         "arm-unknown-linux-gnueabi",
@@ -1170,6 +1172,15 @@
 
 crate_repositories()
 
+load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies")
+
+rust_analyzer_dependencies()
+
+register_toolchains(rust_analyzer_toolchain_repository(
+    name = "rust_analyzer_toolchain",
+    version = RUST_VERSION,
+))
+
 http_archive(
     name = "cxxbridge-cmd",
     build_file = "//third_party/cargo:cxxbridge-cmd/include.BUILD.bazel",
diff --git a/tools/BUILD b/tools/BUILD
index 48e4342..98e1348 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -4,6 +4,15 @@
 
 exports_files(["test_sharding_compliant"])
 
+alias(
+    name = "gen_rust_project",
+    actual = "@rules_rust//tools/rust_analyzer:gen_rust_project",
+    target_compatible_with = select({
+        "//tools/platforms/rust:has_support": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
+)
+
 # Don't use these directly! Use //tools/build_rules/*.bzl instead.
 config_setting(
     name = "compiler_clang",