Implement Rust backend for AIDL compiler

New backend for Rust compatible with libbinder_rs, based on the
existing C++/Java/NDK backends. Includes support for aidl_interface.

Bug: 161559780
Test: atest aidl_unittests
Change-Id: I54353421320f1228f16d22c2d2b60cd8add8b6f9
diff --git a/options.cpp b/options.cpp
index 0bcc4f2..5fb44a4 100644
--- a/options.cpp
+++ b/options.cpp
@@ -45,8 +45,8 @@
   std::ostringstream sstr;
   sstr << "AIDL Compiler: built for platform SDK version " << PLATFORM_SDK_VERSION << endl;
   sstr << "usage:" << endl
-       << myname_ << " --lang={java|cpp|ndk} [OPTION]... INPUT..." << endl
-       << "   Generate Java or C++ files for AIDL file(s)." << endl
+       << myname_ << " --lang={java|cpp|ndk|rust} [OPTION]... INPUT..." << endl
+       << "   Generate Java, C++ or Rust files for AIDL file(s)." << endl
        << endl
        << myname_ << " --preprocess OUTPUT INPUT..." << endl
        << "   Create an AIDL file having declarations of AIDL file(s)." << endl
@@ -70,6 +70,10 @@
     sstr << myname_ << " [OPTION]... INPUT HEADER_DIR OUTPUT" << endl
          << "   Generate C++ headers and source for an AIDL file." << endl
          << endl;
+  } else if (language_ == Options::Language::RUST) {
+    sstr << myname_ << " [OPTION]... INPUT [OUTPUT]" << endl
+         << "   Generate Rust file for an AIDL file." << endl
+         << endl;
   }
 
   sstr << "OPTION:" << endl
@@ -149,6 +153,8 @@
       return "java";
     case Options::Language::NDK:
       return "ndk";
+    case Options::Language::RUST:
+      return "rust";
     case Options::Language::UNSPECIFIED:
       return "unspecified";
     default:
@@ -241,6 +247,9 @@
           } else if (lang == "ndk") {
             language_ = Options::Language::NDK;
             task_ = Options::Task::COMPILE;
+          } else if (lang == "rust") {
+            language_ = Options::Language::RUST;
+            task_ = Options::Task::COMPILE;
           } else {
             error_message_ << "Unsupported language: '" << lang << "'" << endl;
             return;
@@ -360,7 +369,7 @@
       error_message_ << "No input file" << endl;
       return;
     }
-    if (language_ == Options::Language::JAVA) {
+    if (language_ == Options::Language::JAVA || language_ == Options::Language::RUST) {
       input_files_.emplace_back(argv[optind++]);
       if (argc - optind >= 1) {
         output_file_ = argv[optind++];
@@ -374,7 +383,7 @@
         if (android::base::EndsWith(output_file_, ".aidl")) {
           output_file_ = output_file_.substr(0, output_file_.length() - strlen(".aidl"));
         }
-        output_file_ += ".java";
+        output_file_ += (language_ == Options::Language::JAVA) ? ".java" : ".rs";
       }
     } else if (IsCppOutput()) {
       input_files_.emplace_back(argv[optind++]);
@@ -441,6 +450,17 @@
         return;
       }
     }
+    if (language_ == Options::Language::RUST && task_ == Options::Task::COMPILE) {
+      if (output_dir_.empty()) {
+        error_message_ << "Output directory is not set. Set with --out." << endl;
+        return;
+      }
+      if (!output_header_dir_.empty()) {
+        error_message_ << "Header output directory is set, which does not make "
+                       << "sense for Rust." << endl;
+        return;
+      }
+    }
   }
   if (task_ == Options::Task::COMPILE) {
     for (const string& input : input_files_) {