Encode PrintActionCommand failures with FailureDetails

RELNOTES: None.
PiperOrigin-RevId: 314408840
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
index 4f9e458..d1c0e84 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
@@ -50,8 +50,10 @@
 import com.google.devtools.build.lib.runtime.Command;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
 import com.google.devtools.build.lib.runtime.KeepGoingOption;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.PrintActionCommand.Code;
 import com.google.devtools.build.lib.util.DetailedExitCode;
-import com.google.devtools.build.lib.util.ExitCode;
 import com.google.devtools.build.lib.util.io.OutErr;
 import com.google.devtools.common.options.Option;
 import com.google.devtools.common.options.OptionDocumentationCategory;
@@ -142,25 +144,34 @@
     }
 
     private DetailedExitCode printActionsForTargets(CommandEnvironment env) {
-      BuildResult result = gatherActionsForTargets(env, requestedTargets);
-      if (result == null) {
-        return DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE);
+      BuildResult result;
+      try {
+        result = gatherActionsForTargets(env, requestedTargets);
+      } catch (PrintActionException e) {
+        return DetailedExitCode.of(e.createFailureDetail());
       }
       if (hasFatalBuildFailure(result)) {
         env.getReporter().handle(Event.error("Build failed when printing actions"));
         return result.getDetailedExitCode();
       }
-      String action = TextFormat.printToString(summaryBuilder);
+      String action = TextFormat.printer().printToString(summaryBuilder);
       if (!action.isEmpty()) {
         outErr.printOut(action);
         return result.getDetailedExitCode();
       } else {
-        env.getReporter().handle(Event.error("no actions to print were found"));
-        return DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE);
+        String message = "no actions to print were found";
+        env.getReporter().handle(Event.error(message));
+        return DetailedExitCode.of(
+            FailureDetail.newBuilder()
+                .setMessage(message)
+                .setPrintActionCommand(
+                    FailureDetails.PrintActionCommand.newBuilder().setCode(Code.ACTIONS_NOT_FOUND))
+                .build());
       }
     }
 
-    private BuildResult gatherActionsForTargets(CommandEnvironment env, List<String> targets) {
+    private BuildResult gatherActionsForTargets(CommandEnvironment env, List<String> targets)
+        throws PrintActionException {
       BlazeRuntime runtime = env.getRuntime();
       String commandName = PrintActionCommand.this.getClass().getAnnotation(Command.class).name();
       BuildRequest request = BuildRequest.create(commandName, options,
@@ -190,14 +201,15 @@
                   env.getSkyframeExecutor().getActionKeyContext(),
                   targets);
             } else {
-              Target target = null;
+              Target target;
               try {
                 target =
                     env.getPackageManager()
                         .getTarget(env.getReporter(), configuredTarget.getLabel());
               } catch (NoSuchTargetException | NoSuchPackageException | InterruptedException e) {
-                env.getReporter().handle(Event.error("Failed to find target to gather actions."));
-                return null;
+                String message = "Failed to find target to gather actions";
+                env.getReporter().handle(Event.error(message));
+                throw new PrintActionException(message, Code.TARGET_NOT_FOUND);
               }
               gatherActionsForTarget(
                   configuredTarget,
@@ -206,19 +218,20 @@
                   env.getSkyframeExecutor().getActionKeyContext());
             }
           } catch (CommandLineExpansionException e) {
-            env.getReporter().handle(Event.error(null, "Error expanding command line: " + e));
-            return null;
+            String message = "Error expanding command line: " + e;
+            env.getReporter().handle(Event.error(null, message));
+            throw new PrintActionException(message, Code.COMMAND_LINE_EXPANSION_FAILURE);
           }
         } else {
-          env.getReporter().handle(Event.error(
-              null, configuredTarget + " is not a supported target kind"));
-          return null;
+          String message = configuredTarget + " is not a supported target kind";
+          env.getReporter().handle(Event.error(null, message));
+          throw new PrintActionException(message, Code.TARGET_KIND_UNSUPPORTED);
         }
       }
       return result;
     }
 
-    private BuildResult gatherActionsForFiles(
+    private void gatherActionsForFiles(
         ConfiguredTarget configuredTarget,
         CommandEnvironment env,
         ActionGraph actionGraph,
@@ -227,9 +240,7 @@
         throws CommandLineExpansionException {
       Set<String> filesDesired = new LinkedHashSet<>(files);
       ActionFilter filter = new DefaultActionFilter(filesDesired, actionMnemonicMatcher);
-
       gatherActionsForFile(configuredTarget, filter, env, actionGraph, actionKeyContext);
-      return null;
     }
 
     private void gatherActionsForTarget(
@@ -394,6 +405,7 @@
       if (!rule.isAttrDefined("hdrs", BuildType.LABEL_LIST)) {
         return false;
       }
+
       List<Label> hdrs =
           ConfiguredAttributeMapper.of(rule, ruleConfiguredTarget.getConfigConditions())
               .get("hdrs", BuildType.LABEL_LIST);
@@ -407,4 +419,21 @@
       return false; // no match
     }
   }
+
+  private static class PrintActionException extends Exception {
+    private final FailureDetails.PrintActionCommand.Code detailedCode;
+
+    private PrintActionException(String message, Code detailedCode) {
+      super(message);
+      this.detailedCode = detailedCode;
+    }
+
+    private FailureDetail createFailureDetail() {
+      return FailureDetail.newBuilder()
+          .setMessage(getMessage())
+          .setPrintActionCommand(
+              FailureDetails.PrintActionCommand.newBuilder().setCode(detailedCode))
+          .build();
+    }
+  }
 }
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index 1641448..8fa25ea 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -127,6 +127,7 @@
     ProfileCommand profile_command = 151;
     RunCommand run_command = 152;
     VersionCommand version_command = 153;
+    PrintActionCommand print_action_command = 154;
   }
 
   reserved 102; // For internal use
@@ -750,3 +751,15 @@
 
   Code code = 1;
 }
+
+message PrintActionCommand {
+  enum Code {
+    PRINT_ACTION_COMMAND_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+    TARGET_NOT_FOUND = 1 [(metadata) = { exit_code: 1 }];
+    COMMAND_LINE_EXPANSION_FAILURE = 2 [(metadata) = { exit_code: 1 }];
+    TARGET_KIND_UNSUPPORTED = 3 [(metadata) = { exit_code: 1 }];
+    ACTIONS_NOT_FOUND = 4 [(metadata) = { exit_code: 1 }];
+  }
+
+  Code code = 1;
+}
\ No newline at end of file