Bzlmod: Add RepoSpec.java

RepoSpec holds information of a repository rule definition, including where the rule class is defined and the attributes of a repository rule. It's the output of Bzlmod resolution and will be used to create a repository rule instance.

Related: https://github.com/bazelbuild/bazel/issues/13316

RELNOTES: None
PiperOrigin-RevId: 380784644
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
index bd98f49..78f95f9 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
@@ -69,3 +69,12 @@
         "//third_party:jsr305",
     ],
 )
+
+java_library(
+    name = "repo_spec_values",
+    srcs = ["RepoSpec.java"],
+    deps = [
+        "//third_party:auto_value",
+        "//third_party:guava",
+    ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java
new file mode 100644
index 0000000..6bde0a7
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java
@@ -0,0 +1,64 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.bzlmod;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import java.util.Optional;
+
+/**
+ * A class holding information about the attributes of a repository rule and where the rule class is
+ * defined.
+ */
+@AutoValue
+public abstract class RepoSpec {
+
+  /**
+   * The label string for the bzl file this repository rule is defined in, empty for native rule.
+   */
+  public abstract Optional<String> bzlFile();
+
+  public abstract String ruleClassName();
+
+  public abstract ImmutableMap<String, Object> attributes();
+
+  public static Builder builder() {
+    return new AutoValue_RepoSpec.Builder();
+  }
+
+  /** The builder for {@link RepoSpec} */
+  @AutoValue.Builder
+  public abstract static class Builder {
+    public abstract Builder setBzlFile(Optional<String> bzlFile);
+
+    public abstract Builder setRuleClassName(String name);
+
+    public abstract Builder setAttributes(ImmutableMap<String, Object> attributes);
+
+    public abstract RepoSpec build();
+  }
+
+  public boolean isNativeRepoRule() {
+    return !bzlFile().isPresent();
+  }
+
+  /**
+   * Return a string representing the rule class eg. Native repo rule: local_repository, Starlark
+   * repo rule: //:repo.bzl%my_repo
+   */
+  public String getRuleClass() {
+    return bzlFile().map(f -> f + "%").orElse("") + ruleClassName();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
index 161bef0..9aa4b78 100644
--- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
@@ -23,6 +23,7 @@
         "//src/main/java/com/google/devtools/build/lib/analysis:server_directories",
         "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:common",
         "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:registry",
+        "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_spec_values",
         "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution",
         "//src/main/java/com/google/devtools/build/lib/events",
         "//src/main/java/com/google/devtools/build/lib/pkgcache",
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecTest.java
new file mode 100644
index 0000000..99c06d6
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecTest.java
@@ -0,0 +1,57 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package com.google.devtools.build.lib.bazel.bzlmod;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for RepoSpec.java */
+@RunWith(JUnit4.class)
+public class RepoSpecTest {
+
+  @Test
+  public void nativeRepoSpecTest() {
+    RepoSpec repoSpec =
+        RepoSpec.builder()
+            .setRuleClassName("local_repository")
+            .setAttributes(ImmutableMap.of("path", "/foo/bar"))
+            .build();
+    assertThat(repoSpec.isNativeRepoRule()).isTrue();
+    assertThat(repoSpec.ruleClassName()).isEqualTo("local_repository");
+    assertThat(repoSpec.getRuleClass()).isEqualTo("local_repository");
+    assertThat(repoSpec.attributes()).containsExactly("path", "/foo/bar");
+  }
+
+  @Test
+  public void starlarkRepoSpecTest() {
+    RepoSpec repoSpec =
+        RepoSpec.builder()
+            .setBzlFile(Optional.of("//pkg:repo.bzl"))
+            .setRuleClassName("my_repo")
+            .setAttributes(ImmutableMap.of("attr1", "foo", "attr2", "bar"))
+            .build();
+    assertThat(repoSpec.isNativeRepoRule()).isFalse();
+    assertThat(repoSpec.bzlFile()).hasValue("//pkg:repo.bzl");
+    assertThat(repoSpec.ruleClassName()).isEqualTo("my_repo");
+    assertThat(repoSpec.getRuleClass()).isEqualTo("//pkg:repo.bzl%my_repo");
+    assertThat(repoSpec.attributes()).containsExactly("attr1", "foo", "attr2", "bar");
+  }
+}