Extra validation of cmdline arguments.

PiperOrigin-RevId: 431975072
diff --git a/rs_bindings_from_cc/cmdline.cc b/rs_bindings_from_cc/cmdline.cc
index 2a6fead..eaf9357 100644
--- a/rs_bindings_from_cc/cmdline.cc
+++ b/rs_bindings_from_cc/cmdline.cc
@@ -80,20 +80,56 @@
     return absl::InvalidArgumentError("please specify --targets_and_headers");
   }
   nlohmann::json targets_and_headers =
-      nlohmann::json::parse(std::move(targets_and_headers_str));
+      nlohmann::json::parse(std::move(targets_and_headers_str),
+                            /* cb= */ nullptr,
+                            /* allow_exceptions= */ false);
   if (!targets_and_headers.is_array()) {
     return absl::InvalidArgumentError(
         "Expected `--targets_and_headers` to be a JSON array of objects");
   }
   for (const auto& target_and_headers : targets_and_headers) {
+    if (!target_and_headers.contains("t")) {
+      return absl::InvalidArgumentError(
+          "Missing `t` field in an `--targets_and_headers` object");
+    }
+    if (!target_and_headers["t"].is_string()) {
+      return absl::InvalidArgumentError(
+          "Expected `t` fields of `--targets_and_headers` to be a string");
+    }
+    if (!target_and_headers.contains("h")) {
+      return absl::InvalidArgumentError(
+          "Missing `h` field in an `--targets_and_headers` object");
+    }
     if (!target_and_headers["h"].is_array()) {
       return absl::InvalidArgumentError(
           "Expected `h` fields of `--targets_and_headers` to be an array");
     }
-    BlazeLabel target = BlazeLabel{target_and_headers["t"]};
+    BlazeLabel target{std::string(target_and_headers["t"])};
+    if (target.value().empty()) {
+      return absl::InvalidArgumentError(
+          "Expected `t` fields of `--targets_and_headers` to be a non-empty "
+          "string");
+    }
     for (const auto& header : target_and_headers["h"]) {
-      cmdline.headers_to_targets_.insert(
-          std::make_pair(HeaderName(std::string(header)), target));
+      if (!header.is_string()) {
+        return absl::InvalidArgumentError(
+            "Expected `h` fields of `--targets_and_headers` to be an array of "
+            "strings");
+      }
+      std::string header_str(header);
+      if (header_str.empty()) {
+        return absl::InvalidArgumentError(
+            "Expected `h` fields of `--targets_and_headers` to be an array of "
+            "non-empty strings");
+      }
+      const auto [it, inserted] = cmdline.headers_to_targets_.insert(
+          std::make_pair(HeaderName(header_str), target));
+      if (!inserted) {
+        return absl::InvalidArgumentError(absl::Substitute(
+            "The `--targets_and_headers` cmdline argument assigns "
+            "`$0` header to two conflicting targets: `$1` vs `$2`",
+            header_str, target.value(), it->second.value()));
+      }
     }
   }
 
diff --git a/rs_bindings_from_cc/cmdline_test.cc b/rs_bindings_from_cc/cmdline_test.cc
index e8aa0e4..146d816 100644
--- a/rs_bindings_from_cc/cmdline_test.cc
+++ b/rs_bindings_from_cc/cmdline_test.cc
@@ -56,6 +56,13 @@
                        HasSubstr("please specify --targets_and_headers")));
 }
 
+TEST(CmdlineTest, TargetsAndHeadersInvalidJson) {
+  ASSERT_THAT(
+      TestCmdline({"h1"}, "#!$%"),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               AllOf(HasSubstr("--targets_and_headers"), HasSubstr("array"))));
+}
+
 TEST(CmdlineTest, TargetsAndHeadersIntInsteadOfTopLevelArray) {
   ASSERT_THAT(
       TestCmdline({"h1"}, "123"),
@@ -63,6 +70,12 @@
                AllOf(HasSubstr("--targets_and_headers"), HasSubstr("array"))));
 }
 
+TEST(CmdlineTest, TargetsAndHeadersIntInTopLevelArray) {
+  ASSERT_THAT(TestCmdline({"h1"}, "[123, 456]"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"))));
+}
+
 TEST(CmdlineTest, TargetsAndHeadersIntInsteadOfHeadersArray) {
   ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": "t1", "h": 123}])"),
               StatusIs(absl::StatusCode::kInvalidArgument,
@@ -70,6 +83,59 @@
                              HasSubstr("`h`"), HasSubstr("array"))));
 }
 
+TEST(CmdlineTest, TargetsAndHeadersMissingTarget) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"h": ["h1", "h2"]}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`t`"), HasSubstr("Missing"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersMissingHeader) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": "t1"}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`h`"), HasSubstr("Missing"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersEmptyHeader) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": "t1", "h": ["", "h2"]}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`h`"), HasSubstr("empty string"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersEmptyTarget) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": "", "h": ["h1", "h2"]}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`t`"), HasSubstr("empty string"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersIntInsteadOfTarget) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": 123, "h": ["h1", "h2"]}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`t`"), HasSubstr("string"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersIntInsteadOfHeader) {
+  ASSERT_THAT(TestCmdline({"h1"}, R"([{"t": "t1", "h": [123, "h2"]}])"),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       AllOf(HasSubstr("--targets_and_headers"),
+                             HasSubstr("`h`"), HasSubstr("string"))));
+}
+
+TEST(CmdlineTest, TargetsAndHeadersDuplicateHeader) {
+  constexpr char kTestInput[] = R"([
+      {"t": "t1", "h": ["h1"]},
+      {"t": "t2", "h": ["h1"]} ])";
+  ASSERT_THAT(
+      TestCmdline({"h1"}, kTestInput),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               AllOf(HasSubstr("--targets_and_headers"), HasSubstr("conflict"),
+                     HasSubstr("h1"), HasSubstr("t1"), HasSubstr("t2"))));
+}
+
 TEST(CmdlineTest, PublicHeadersEmpty) {
   constexpr char kTargetsAndHeaders[] = R"([
     {"t": "target1", "h": ["a.h", "b.h"]}