package com.atlassian.bitbucket.scm;

import com.atlassian.bitbucket.util.PageRequest;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * Describes a handler for processing {@link Command} exit details, whether the command completes or is canceled. The
 * most common use case for implementing this interface is to process exit code, standard error or {@code Throwable}
 * information from the command's execution to determine whether the command "failed" or "succeeded". For "failed"
 * commands, implementations may throw an exception to explain the failure. When throwing an exception, implementors
 * are <i>encouraged</i> to throw exceptions derived from:
 * <ul>
 *     <li>{@link CommandCanceledException CommandCanceledException} if the command
 *     {@link #onCancel(String, int, String, Throwable) was canceled} in an unexpected way; for example, in response
 *     to an error during output processing</li>
 *     <li>{@link CommandFailedException CommandFailedException} if the command
 *     {@link #onExit(String, int, String, Throwable) exited} abnormally; for example, in response to invalid input</li>
 * </ul>
 * <p>
 * Note: Using a {@link CommandErrorHandler} may affect the input received by implementations of this interface. If a
 * custom {@link CommandErrorHandler} is used, no {@code stdErr} will be received by this handler. It is expected that
 * any output written to standard error has already been processed by that handler and does not need to be reprocessed
 * by this handler.
 */
public interface CommandExitHandler {

    /**
     * Invoked after a {@link Command} exits after having been canceled. Commands may be canceled by:
     * <ul>
     *     <li>their {@link CommandErrorHandler}</li>
     *     <li>their {@link CommandInputHandler}</li>
     *     <li>their {@link CommandOutputHandler}</li>
     *     <li>their {@code Future}, for {@link AsyncCommand}</li>
     * </ul>
     * For some commands, being canceled is not an indication of an error condition. For example, an output handler
     * receiving a {@link PageRequest PageRequest} may cancel the command when the requested
     * page has been filled. However, it is worth noting that, to the executing command, being canceled is usually
     * unexpected and will often result in a non-zero exit code (often {@code 13}, for the {@code SIGPIPE} signal the
     * command receives when the JVM closes streams to the process).
     *
     * @param command  the command that was canceled
     * @param exitCode the exit code from the process
     * @param stdErr   any output written to standard error, <i>if no {@link CommandErrorHandler} is in use</i>
     * @param thrown   any exception thrown by the handlers or the command implementation
     */
    void onCancel(@Nonnull String command, int exitCode, @Nullable String stdErr, @Nullable Throwable thrown);

    /**
     * Invoked after a {@link Command} exists after running to completion without being canceled. The command may have
     * completed successfully or failed; determining which is left to the implementation to determine.
     * <p>
     * Some potential candidates for detecting failures:
     * <ul>
     *     <li>non-zero exit codes</li>
     *     <li>output written to standard error</li>
     *     <li>exceptions thrown by a handler</li>
     * </ul>
     * Each of these candidates is situational. For example, some commands that perform long-running or verbose
     * processing and write results to standard output write progress information to standard error. For such commands,
     * the presence of standard error output does not necessarily indicate a failure.
     * <p>
     * Multiple failure cases may be triggered at once. For example, if a command returns with a non-zero exit code,
     * it may have written an explanation to standard error or produced other output that triggered one of the handlers
     * to throw an exception. Prioritising or merging the possible sources of error details is left to the implementor;
     * it is not part of the contract for this interface.
     *
     * @param command  the command that has exited
     * @param exitCode the exit code from the process
     * @param stdErr   any output written to standard error, <i>if no {@link CommandErrorHandler} is in use</i>
     * @param thrown   any exception thrown by the handlers or the command implementation
     */
    void onExit(@Nonnull String command, int exitCode, @Nullable String stdErr, @Nullable Throwable thrown);
}
