Fold FailureDetailUtil into DetailedExitCode
This reduces the number of notable programmatic entities associated
with FailureDetail messages by one.
Removing the "Util" class helps prevent it from being a dumping ground of
FailureDetail factory methods for categories that ought to be coupled
with their subsystems' code instead.
RELNOTES: None.
PiperOrigin-RevId: 303775924
diff --git a/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java b/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
index de37710..75d8643 100644
--- a/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
+++ b/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
@@ -14,9 +14,15 @@
package com.google.devtools.build.lib.util;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.devtools.build.lib.server.FailureDetails;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.MessageOrBuilder;
+import java.util.Map;
import javax.annotation.Nullable;
/** An {@link ExitCode} and an optional {@link FailureDetail}. */
@@ -33,6 +39,15 @@
return exitCode;
}
+ /** Returns the registered {@link ExitCode} associated with a {@link FailureDetail} message. */
+ private static ExitCode getExitCode(FailureDetail failureDetail) {
+ // TODO(mschaller): Consider specializing for unregistered exit codes here, if absolutely
+ // necessary.
+ int numericExitCode = getNumericExitCode(failureDetail);
+ return checkNotNull(
+ ExitCode.forCode(numericExitCode), "No ExitCode for numericExitCode %s", numericExitCode);
+ }
+
@Nullable
public FailureDetail getFailureDetail() {
return failureDetail;
@@ -81,7 +96,7 @@
* FailureDetail}'s metadata.
*/
public static DetailedExitCode of(FailureDetail failureDetail) {
- return new DetailedExitCode(FailureDetailUtil.getExitCode(failureDetail), failureDetail);
+ return new DetailedExitCode(getExitCode(failureDetail), failureDetail);
}
@Override
@@ -89,4 +104,68 @@
return String.format(
"DetailedExitCode{exitCode=%s, failureDetail=%s}", exitCode, failureDetail);
}
+
+ /** Returns the numeric exit code associated with a {@link FailureDetail} message. */
+ private static int getNumericExitCode(FailureDetail failureDetail) {
+ MessageOrBuilder categoryMsg = getCategorySubmessage(failureDetail);
+ EnumValueDescriptor subcategoryDescriptor =
+ getSubcategoryDescriptor(failureDetail, categoryMsg);
+ return getNumericExitCode(subcategoryDescriptor);
+ }
+
+ /**
+ * Returns the numeric exit code associated with a {@link FailureDetail} submessage's subcategory
+ * enum value.
+ */
+ private static int getNumericExitCode(EnumValueDescriptor subcategoryDescriptor) {
+ checkArgument(
+ subcategoryDescriptor.getOptions().hasExtension(FailureDetails.metadata),
+ "Enum value %s has no FailureDetails.metadata",
+ subcategoryDescriptor);
+ return subcategoryDescriptor.getOptions().getExtension(FailureDetails.metadata).getExitCode();
+ }
+
+ /**
+ * Returns the category submessage, i.e. the message in {@link FailureDetail}'s oneof. Throws if
+ * none of those fields are set.
+ */
+ private static MessageOrBuilder getCategorySubmessage(FailureDetail failureDetail) {
+ MessageOrBuilder categoryMsg = null;
+ for (Map.Entry<FieldDescriptor, Object> entry : failureDetail.getAllFields().entrySet()) {
+ FieldDescriptor fieldDescriptor = entry.getKey();
+ if (isCategoryField(fieldDescriptor)) {
+ categoryMsg = (MessageOrBuilder) entry.getValue();
+ break;
+ }
+ }
+ return checkNotNull(
+ categoryMsg, "FailureDetail missing category submessage: %s", failureDetail);
+ }
+
+ /**
+ * Returns whether the {@link FieldDescriptor} describes a field in {@link FailureDetail}'s oneof.
+ *
+ * <p>Uses the field number criteria described in failure_details.proto.
+ */
+ private static boolean isCategoryField(FieldDescriptor fieldDescriptor) {
+ int fieldNum = fieldDescriptor.getNumber();
+ return 100 < fieldNum && fieldNum <= 10_000;
+ }
+
+ /**
+ * Returns the enum value descriptor for the enum field with field number 1 in the {@link
+ * FailureDetail}'s category submessage.
+ */
+ private static EnumValueDescriptor getSubcategoryDescriptor(
+ FailureDetail failureDetail, MessageOrBuilder categoryMsg) {
+ FieldDescriptor fieldNumberOne = categoryMsg.getDescriptorForType().findFieldByNumber(1);
+ checkNotNull(
+ fieldNumberOne, "FailureDetail category submessage has no field #1: %s", failureDetail);
+ Object fieldNumberOneVal = categoryMsg.getField(fieldNumberOne);
+ checkArgument(
+ fieldNumberOneVal instanceof EnumValueDescriptor,
+ "FailureDetail category submessage has non-enum field #1: %s",
+ failureDetail);
+ return (EnumValueDescriptor) fieldNumberOneVal;
+ }
}