Refactor AttributeInfo into a proto

This is a no-op in functionality, but begins to pave the way for a proto output format for Stardoc.

RELNOTES: None.
PiperOrigin-RevId: 244020123
diff --git a/src/BUILD b/src/BUILD
index 5026e34..2844953 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -497,6 +497,7 @@
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:dist_jars",
         "//src/main/java/com/google/devtools/build/lib/bazel/debug:dist_jars",
         "//src/main/java/com/google/devtools/build/lib/skylarkdebug/proto:dist_jars",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:dist_jars",
         "@googleapis//:dist_jars",
         "@remoteapis//:dist_jars",
     ],
diff --git a/src/main/java/com/google/devtools/build/skydoc/BUILD b/src/main/java/com/google/devtools/build/skydoc/BUILD
index 8f4b587..f34fda2 100644
--- a/src/main/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/BUILD
@@ -27,6 +27,7 @@
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository:srcs",
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/test:srcs",
         "//src/main/java/com/google/devtools/build/skydoc/rendering:srcs",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:srcs",
     ],
 )
 
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/BUILD b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/BUILD
index 766f404..d2f0f40 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/BUILD
@@ -19,6 +19,7 @@
         "//src/main/java/com/google/devtools/build/lib/cmdline",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi",
         "//src/main/java/com/google/devtools/build/skydoc/rendering",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
         "//third_party:guava",
         "//third_party:jsr305",
     ],
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeDescriptor.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeDescriptor.java
index a480f85..1793dad 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeDescriptor.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeDescriptor.java
@@ -16,23 +16,24 @@
 
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAttrApi.Descriptor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo.Type;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 
 /**
  * Fake implementation of {@link Descriptor}.
  */
 public class FakeDescriptor implements Descriptor {
-  private final Type type;
+  private final AttributeType type;
   private final String docString;
   private final boolean mandatory;
 
-  public FakeDescriptor(Type type, String docString, boolean mandatory) {
+  public FakeDescriptor(AttributeType type, String docString, boolean mandatory) {
     this.type = type;
     this.docString = docString;
     this.mandatory = mandatory;
   }
 
-  public Type getType() {
+  public AttributeType getType() {
     return type;
   }
 
@@ -46,4 +47,13 @@
 
   @Override
   public void repr(SkylarkPrinter printer) {}
+
+  public AttributeInfo asAttributeInfo(String attributeName) {
+    return AttributeInfo.newBuilder()
+        .setName(attributeName)
+        .setDocString(getDocString())
+        .setType(getType())
+        .setMandatory(isMandatory())
+        .build();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkAttrApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkAttrApi.java
index 54280b8..a67fff7 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkAttrApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkAttrApi.java
@@ -22,7 +22,7 @@
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo.Type;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 
 /**
  * Fake implementation of {@link SkylarkAttrApi}.
@@ -39,7 +39,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.INT, doc, mandatory);
+    return new FakeDescriptor(AttributeType.INT, doc, mandatory);
   }
 
   @Override
@@ -52,7 +52,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.STRING, doc, mandatory);
+    return new FakeDescriptor(AttributeType.STRING, doc, mandatory);
   }
 
   @Override
@@ -72,7 +72,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.LABEL, doc, mandatory);
+    return new FakeDescriptor(AttributeType.LABEL, doc, mandatory);
   }
 
   @Override
@@ -86,7 +86,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.STRING_LIST, doc, mandatory);
+    return new FakeDescriptor(AttributeType.STRING_LIST, doc, mandatory);
   }
 
   @Override
@@ -100,7 +100,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.INT_LIST, doc, mandatory);
+    return new FakeDescriptor(AttributeType.INT_LIST, doc, mandatory);
   }
 
   @Override
@@ -120,7 +120,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.LABEL_LIST, doc, mandatory);
+    return new FakeDescriptor(AttributeType.LABEL_LIST, doc, mandatory);
   }
 
   @Override
@@ -140,7 +140,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.LABEL_STRING_DICT, doc, mandatory);
+    return new FakeDescriptor(AttributeType.LABEL_STRING_DICT, doc, mandatory);
   }
 
   @Override
@@ -152,7 +152,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.BOOLEAN, doc, mandatory);
+    return new FakeDescriptor(AttributeType.BOOLEAN, doc, mandatory);
   }
 
   @Override
@@ -164,7 +164,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.OUTPUT, doc, mandatory);
+    return new FakeDescriptor(AttributeType.OUTPUT, doc, mandatory);
   }
 
   @Override
@@ -178,7 +178,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.OUTPUT_LIST, doc, mandatory);
+    return new FakeDescriptor(AttributeType.OUTPUT_LIST, doc, mandatory);
   }
 
   @Override
@@ -192,7 +192,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.STRING_DICT, doc, mandatory);
+    return new FakeDescriptor(AttributeType.STRING_DICT, doc, mandatory);
   }
 
   @Override
@@ -206,7 +206,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.STRING_LIST_DICT, doc, mandatory);
+    return new FakeDescriptor(AttributeType.STRING_LIST_DICT, doc, mandatory);
   }
 
   @Override
@@ -218,7 +218,7 @@
       Environment env,
       StarlarkContext context)
       throws EvalException {
-    return new FakeDescriptor(Type.LICENSE, doc, mandatory);
+    return new FakeDescriptor(AttributeType.STRING_LIST, doc, mandatory);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index 875b3ca..9201d4d 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -33,11 +33,11 @@
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkType;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo.Type;
 import com.google.devtools.build.skydoc.rendering.ProviderFieldInfo;
 import com.google.devtools.build.skydoc.rendering.ProviderInfo;
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
@@ -53,7 +53,7 @@
 public class FakeSkylarkRuleFunctionsApi implements SkylarkRuleFunctionsApi<FileApi> {
 
   private static final FakeDescriptor IMPLICIT_NAME_ATTRIBUTE_DESCRIPTOR =
-      new FakeDescriptor(Type.NAME, "A unique name for this target.", true);
+      new FakeDescriptor(AttributeType.NAME, "A unique name for this target.", true);
   private final List<RuleInfo> ruleInfoList;
   private final List<ProviderInfo> providerInfoList;
 
@@ -132,14 +132,11 @@
     }
 
     attrsMapBuilder.put("name", IMPLICIT_NAME_ATTRIBUTE_DESCRIPTOR);
-    attrInfos = attrsMapBuilder.build().entrySet().stream()
-        .filter(entry -> !entry.getKey().startsWith("_"))
-        .map(entry -> new AttributeInfo(
-            entry.getKey(),
-            entry.getValue().getDocString(),
-            entry.getValue().getType(),
-            entry.getValue().isMandatory()))
-        .collect(Collectors.toList());
+    attrInfos =
+        attrsMapBuilder.build().entrySet().stream()
+            .filter(entry -> !entry.getKey().startsWith("_"))
+            .map(entry -> entry.getValue().asAttributeInfo(entry.getKey()))
+            .collect(Collectors.toList());
     attrInfos.sort(new AttributeNameComparator());
 
     RuleDefinitionIdentifier functionIdentifier = new RuleDefinitionIdentifier();
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
index 9c349e9..4d5a99e 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
@@ -18,6 +18,7 @@
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/repository",
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi",
         "//src/main/java/com/google/devtools/build/skydoc/rendering",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
         "//third_party:guava",
         "//third_party:jsr305",
     ],
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
index 82ec124..d09d4db 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
@@ -25,9 +25,9 @@
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.skydoc.fakebuildapi.FakeDescriptor;
 import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkRuleFunctionsApi.AttributeNameComparator;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo.Type;
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import java.util.List;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
@@ -37,7 +37,7 @@
  */
 public class FakeRepositoryModule implements RepositoryModuleApi {
   private static final FakeDescriptor IMPLICIT_NAME_ATTRIBUTE_DESCRIPTOR =
-      new FakeDescriptor(Type.NAME, "A unique name for this repository.", true);
+      new FakeDescriptor(AttributeType.NAME, "A unique name for this repository.", true);
 
   private final List<RuleInfo> ruleInfoList;
 
@@ -66,13 +66,7 @@
     attrInfos =
         attrsMapBuilder.build().entrySet().stream()
             .filter(entry -> !entry.getKey().startsWith("_"))
-            .map(
-                entry ->
-                    new AttributeInfo(
-                        entry.getKey(),
-                        entry.getValue().getDocString(),
-                        entry.getValue().getType(),
-                        entry.getValue().isMandatory()))
+            .map(entry -> entry.getValue().asAttributeInfo(entry.getKey()))
             .collect(Collectors.toList());
     attrInfos.sort(new AttributeNameComparator());
 
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/AttributeInfo.java b/src/main/java/com/google/devtools/build/skydoc/rendering/AttributeInfo.java
deleted file mode 100644
index 0361932..0000000
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/AttributeInfo.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2018 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.skydoc.rendering;
-
-/**
- * Stores information about a skylark attribute definition.
- */
-public class AttributeInfo {
-
-  private final String name;
-  private final String docString;
-  private final Type type;
-  private final boolean mandatory;
-
-  public AttributeInfo(String name, String docString, Type type, boolean mandatory) {
-    this.name = name;
-    this.docString = docString.trim();
-    this.type = type;
-    this.mandatory = mandatory;
-  }
-
-  @SuppressWarnings("unused") // Used by markdown template.
-  public String getName() {
-    return name;
-  }
-
-  @SuppressWarnings("unused") // Used by markdown template.
-  public String getDocString() {
-    return docString;
-  }
-
-  @SuppressWarnings("unused") // Used by markdown template.
-  public Type getType() {
-    return type;
-  }
-
-  /**
-   * Returns a string representing whether this attribute is required or optional.
-   */
-  @SuppressWarnings("unused") // Used by markdown template.
-  public String getMandatoryString() {
-    return mandatory ? "required" : "optional";
-  }
-
-  /**
-   * Attribute type. For example, an attribute described by attr.label() will be of type LABEL.
-   */
-  public enum Type {
-    NAME("Name"),
-    INT("Integer"),
-    LABEL("Label"),
-    STRING("String"),
-    STRING_LIST("List of strings"),
-    INT_LIST("List of integers"),
-    LABEL_LIST("List of labels"),
-    BOOLEAN("Boolean"),
-    LICENSE("List of strings"),
-    LABEL_STRING_DICT("Dictionary: Label -> String"),
-    STRING_DICT("Dictionary: String -> String"),
-    STRING_LIST_DICT("Dictionary: String -> List of strings"),
-    OUTPUT("Label"),
-    OUTPUT_LIST("List of labels");
-
-    private final String description;
-
-    Type(String description) {
-      this.description = description;
-    }
-
-    /**
-     * Returns a human-readable string representing this attribute type.
-     */
-    public String getDescription() {
-      return description;
-    }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD b/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
index d3603e2..1cf5d90 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
@@ -17,6 +17,7 @@
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:skylarkinterface",
         "//src/main/java/com/google/devtools/build/lib:syntax",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
         "//src/tools/skylark/java/com/google/devtools/skylark/common",
         "//third_party:apache_velocity",
         "//third_party:guava",
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
index 335bda8..ff13751 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
@@ -15,6 +15,8 @@
 package com.google.devtools.build.skydoc.rendering;
 
 import com.google.common.base.Joiner;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -99,9 +101,49 @@
         break;
     }
     if (typeLink == null) {
-      return attrInfo.getType().getDescription();
+      return attributeTypeDescription(attrInfo.getType());
     } else {
-      return String.format("<a href=\"%s\">%s</a>", typeLink, attrInfo.getType().getDescription());
+      return String.format(
+          "<a href=\"%s\">%s</a>", typeLink, attributeTypeDescription(attrInfo.getType()));
     }
   }
+
+  public String mandatoryString(AttributeInfo attrInfo) {
+    return attrInfo.getMandatory() ? "required" : "optional";
+  }
+
+  private String attributeTypeDescription(AttributeType attributeType) {
+    switch (attributeType) {
+      case NAME:
+        return "Name";
+      case INT:
+        return "Integer";
+      case LABEL:
+        return "Label";
+      case STRING:
+        return "String";
+      case STRING_LIST:
+        return "List of strings";
+      case INT_LIST:
+        return "List of integers";
+      case LABEL_LIST:
+        return "List of labels";
+      case BOOLEAN:
+        return "Boolean";
+      case LABEL_STRING_DICT:
+        return "Dictionary: Label -> String";
+      case STRING_DICT:
+        return "Dictionary: String -> String";
+      case STRING_LIST_DICT:
+        return "Dictionary: String -> List of strings";
+      case OUTPUT:
+        return "Label";
+      case OUTPUT_LIST:
+        return "List of labels";
+      case UNKNOWN:
+      case UNRECOGNIZED:
+        throw new IllegalArgumentException("Unhandled type " + attributeType);
+    }
+    throw new IllegalArgumentException("Unhandled type " + attributeType);
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/RuleInfo.java b/src/main/java/com/google/devtools/build/skydoc/rendering/RuleInfo.java
index 6f231cf..b1c795a 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/RuleInfo.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/RuleInfo.java
@@ -16,6 +16,7 @@
 
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.syntax.BaseFunction;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
 import java.util.Collection;
 
 /**
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/proto/BUILD b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/BUILD
new file mode 100644
index 0000000..510022e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/BUILD
@@ -0,0 +1,31 @@
+package(
+    default_visibility = [
+        "//src/main/java/com/google/devtools/build/skydoc:__subpackages__",
+        "//src/test/java/com/google/devtools/build/skydoc:__subpackages__",
+    ],
+)
+
+load("//tools/build_rules:utilities.bzl", "java_library_srcs")
+
+licenses(["notice"])  # Apache 2.0
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+)
+
+proto_library(
+    name = "stardoc_output_proto",
+    srcs = ["stardoc_output.proto"],
+)
+
+java_proto_library(
+    name = "stardoc_output_java_proto",
+    deps = [":stardoc_output_proto"],
+)
+
+java_library_srcs(
+    name = "dist_jars",
+    visibility = ["//src:__pkg__"],
+    deps = [":stardoc_output_java_proto"],
+)
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto
new file mode 100644
index 0000000..004d3b9
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto
@@ -0,0 +1,66 @@
+// Copyright 2019 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.
+//
+// Protos for Stardoc data.
+//
+// Stardoc collects information about Starlark functions, providers, and rules.
+syntax = "proto3";
+
+package stardoc_output;
+
+// option java_api_version = 2;
+option java_package = "com.google.devtools.build.skydoc.rendering.proto";
+option java_outer_classname = "StardocOutputProtos";
+
+// Representation of a Starlark rule attribute type. These generally
+// have a one-to-one correspondence with functions defined at
+// https://docs.bazel.build/versions/master/skylark/lib/attr.html.
+enum AttributeType {
+  UNKNOWN = 0;
+  // A special case of STRING; all rules have exactly one implicit
+  // attribute "name" of type NAME.
+  NAME = 1;
+  INT = 2;
+  LABEL = 3;
+  STRING = 4;
+  STRING_LIST = 5;
+  INT_LIST = 6;
+  LABEL_LIST = 7;
+  BOOLEAN = 8;
+  LABEL_STRING_DICT = 9;
+  STRING_DICT = 10;
+  STRING_LIST_DICT = 11;
+  OUTPUT = 12;
+  OUTPUT_LIST = 13;
+}
+
+// Representation of a Starlark rule attribute definition, comprised of an
+// attribute name, and a schema defined by a call to one of the 'attr' module
+// methods enumerated at
+// https://docs.bazel.build/versions/master/skylark/lib/attr.html
+message AttributeInfo {
+  // The name of the attribute.
+  string name = 1;
+
+  // The documentation string of the attribute, supplied via the 'doc'
+  // parameter to the schema-creation call.
+  string doc_string = 2;
+
+  // The type of the attribute, defined generally by which function is invoked
+  // in the attr module.
+  AttributeType type = 3;
+
+  // If true, all targets of the rule must specify a value for this attribute.
+  bool mandatory = 4;
+}
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/templates/rule.vm b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/rule.vm
index 209c324..d9ea3bf 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/templates/rule.vm
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/rule.vm
@@ -21,10 +21,10 @@
     <tr id="${ruleName}-${attribute.name}">
       <td><code>${attribute.name}</code></td>
       <td>
-        ${util.attributeTypeString($attribute)}; ${attribute.mandatoryString}
+        ${util.attributeTypeString($attribute)}; ${util.mandatoryString($attribute)}
 #if (!$attribute.docString.isEmpty())
         <p>
-          ${attribute.docString}
+          ${attribute.docString.trim()}
         </p>
 #end
       </td>
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD
index bcfc620..cff1161 100644
--- a/src/test/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -28,6 +28,7 @@
         "//src/main/java/com/google/devtools/build/skydoc:skydoc_lib",
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi",
         "//src/main/java/com/google/devtools/build/skydoc/rendering",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
         "//src/test/java/com/google/devtools/build/lib:testutil",
         "//src/test/java/com/google/devtools/build/lib/skylark:testutil",
         "//third_party:guava",
diff --git a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
index 4b5c3b0..0601cf6 100644
--- a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
@@ -28,10 +28,10 @@
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.skydoc.SkydocMain.StarlarkEvaluationException;
-import com.google.devtools.build.skydoc.rendering.AttributeInfo;
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
 import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo;
 import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo.DocstringParseException;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -130,12 +130,14 @@
     assertThat(ruleInfo.getValue().getDocString()).isEqualTo("This is my rule. It does stuff.");
     assertThat(getAttrNames(ruleInfo.getValue())).containsExactly(
         "name", "a", "b", "c", "d").inOrder();
-    assertThat(getAttrTypes(ruleInfo.getValue())).containsExactly(
-        AttributeInfo.Type.NAME,
-        AttributeInfo.Type.LABEL,
-        AttributeInfo.Type.STRING_DICT,
-        AttributeInfo.Type.OUTPUT,
-        AttributeInfo.Type.BOOLEAN).inOrder();
+    assertThat(getAttrTypes(ruleInfo.getValue()))
+        .containsExactly(
+            AttributeType.NAME,
+            AttributeType.LABEL,
+            AttributeType.STRING_DICT,
+            AttributeType.OUTPUT,
+            AttributeType.BOOLEAN)
+        .inOrder();
   }
 
   private static Iterable<String> getAttrNames(RuleInfo ruleInfo) {
@@ -143,7 +145,7 @@
         .collect(Collectors.toList());
   }
 
-  private static Iterable<AttributeInfo.Type> getAttrTypes(RuleInfo ruleInfo) {
+  private static Iterable<AttributeType> getAttrTypes(RuleInfo ruleInfo) {
     return ruleInfo.getAttributes().stream().map(attr -> attr.getType())
         .collect(Collectors.toList());
   }