Packages

Execution Plane process launch, stdio, PTY, and process-session runtime.

Current section

Files

Jump to
execution_plane_process lib execution_plane process_exit.ex
Raw

lib/execution_plane/process_exit.ex

defmodule ExecutionPlane.ProcessExit do
@moduledoc """
Normalized process exit information shared by the runtime and provider profiles.
"""
defstruct status: :error, code: nil, signal: nil, reason: nil, stderr: nil
@type status :: :success | :exit | :signal | :error
@type t :: %__MODULE__{
status: status(),
code: non_neg_integer() | nil,
signal: atom() | integer() | nil,
reason: term(),
stderr: binary() | nil
}
@doc """
Normalizes raw subprocess exit reasons into a stable struct.
This includes raw integer exit statuses, including the shifted values some
platforms report as `code * 256`.
"""
@spec from_reason(term(), keyword()) :: t()
def from_reason(reason, opts \\ []) do
reason
|> unwrap_shutdown()
|> normalize_exit()
|> put_stderr(Keyword.get(opts, :stderr))
end
@doc """
Returns `true` when the normalized exit represents success.
"""
@spec successful?(t()) :: boolean()
def successful?(%__MODULE__{status: :success}), do: true
def successful?(%__MODULE__{}), do: false
defp unwrap_shutdown({:shutdown, reason}), do: unwrap_shutdown(reason)
defp unwrap_shutdown(reason), do: reason
defp normalize_exit(:normal), do: %__MODULE__{status: :success, code: 0, reason: :normal}
defp normalize_exit(0), do: %__MODULE__{status: :success, code: 0, reason: 0}
defp normalize_exit(code) when is_integer(code) and code > 0,
do: exit_with_code(code, normalize_code(code))
defp normalize_exit({:exit_status, code}) when is_integer(code) and code >= 0 do
exit_with_code({:exit_status, code}, normalize_code(code))
end
defp normalize_exit({:signal, signal}) do
%__MODULE__{status: :signal, signal: signal, reason: {:signal, signal}}
end
defp normalize_exit({:signal, signal, _core}) do
%__MODULE__{status: :signal, signal: signal, reason: {:signal, signal}}
end
defp normalize_exit(:enoent),
do: %__MODULE__{status: :error, reason: {:command_not_found, :enoent}}
defp normalize_exit(:eacces),
do: %__MODULE__{status: :error, reason: {:command_not_found, :eacces}}
defp normalize_exit(reason), do: %__MODULE__{status: :error, reason: reason}
defp normalize_code(code) when code > 255 and rem(code, 256) == 0, do: div(code, 256)
defp normalize_code(code), do: code
defp exit_with_code(reason, 0), do: %__MODULE__{status: :success, code: 0, reason: reason}
defp exit_with_code(reason, code), do: %__MODULE__{status: :exit, code: code, reason: reason}
defp put_stderr(%__MODULE__{} = exit, nil), do: exit
defp put_stderr(%__MODULE__{} = exit, stderr), do: %__MODULE__{exit | stderr: stderr}
end