Current section
Files
Jump to
Current section
Files
lib/nexus/cli/dispatcher.ex
defmodule Nexus.CLI.Dispatcher do
@moduledoc """
Dispatches parsed CLI commands to the appropriate handler functions.
"""
alias Nexus.CLI
alias Nexus.CLI.Help
alias Nexus.CLI.Input
alias Nexus.Parser
@doc """
Dispatches the parsed command to the corresponding handler.
- `module` is the module where the handler functions are defined.
- `parsed` is the result from `Nexus.Parser.parse_ast/2`.
"""
@spec dispatch(CLI.t(), Parser.result()) :: :ok | {:error, CLI.error()}
def dispatch(%CLI{} = cli, %{flags: %{help: true}} = result) do
Help.display(cli, result.command)
:ok
end
def dispatch(%CLI{} = cli, %{command: []} = result) do
case find_active_root_flag(cli.root_flags, result.flags) do
nil -> dispatch(cli, put_in(result, [:flags, :help], true))
flag_name -> dispatch_root_flag(cli, flag_name, result)
end
end
def dispatch(%CLI{} = cli, %{args: args, flags: flags, command: command}) when map_size(args) == 1 do
single =
case Map.values(args) do
[value] -> value
[] -> nil
[first | _rest] -> first
end
input = %Input{flags: flags, value: single}
try do
case command do
[root] -> cli.handler.handle_input(root, input)
path -> cli.handler.handle_input(path, input)
end
rescue
e in [UndefinedFunctionError] ->
log_handler_error(e, cli, command, "Handler function not defined", __STACKTRACE__)
{:error, {1, "Command '#{format_command(command)}' is not implemented"}}
e in [FunctionClauseError] ->
log_handler_error(e, cli, command, "Invalid arguments for handler", __STACKTRACE__)
{:error, {1, "Invalid arguments for command '#{format_command(command)}'"}}
e in [ArgumentError] ->
log_handler_error(e, cli, command, "Invalid argument", __STACKTRACE__)
{:error, {1, "Invalid argument: #{Exception.message(e)}"}}
exception ->
log_handler_error(exception, cli, command, "Unexpected error in handler", __STACKTRACE__)
{:error, {1, "An error occurred while executing '#{format_command(command)}'"}}
end
end
def dispatch(%CLI{} = cli, %{args: args, flags: flags, command: command}) do
input = %Input{args: args, flags: flags}
try do
case command do
[root] -> cli.handler.handle_input(root, input)
path -> cli.handler.handle_input(path, input)
end
rescue
e in [UndefinedFunctionError] ->
log_handler_error(e, cli, command, "Handler function not defined", __STACKTRACE__)
{:error, {1, "Command '#{format_command(command)}' is not implemented"}}
e in [FunctionClauseError] ->
log_handler_error(e, cli, command, "Invalid arguments for handler", __STACKTRACE__)
{:error, {1, "Invalid arguments for command '#{format_command(command)}'"}}
e in [ArgumentError] ->
log_handler_error(e, cli, command, "Invalid argument", __STACKTRACE__)
{:error, {1, "Invalid argument: #{Exception.message(e)}"}}
exception ->
log_handler_error(exception, cli, command, "Unexpected error in handler", __STACKTRACE__)
{:error, {1, "An error occurred while executing '#{format_command(command)}'"}}
end
end
defp log_handler_error(exception, cli, command, context, stack) do
require Logger
Logger.error([
"CLI Handler Error in #{cli.handler} for command '#{format_command(command)}': ",
context,
"\nException: #{Exception.format(:error, exception, stack)}"
])
end
defp format_command([single]), do: to_string(single)
defp format_command(path) when is_list(path), do: Enum.join(path, " ")
defp find_active_root_flag(root_flags, flags) do
Enum.find_value(root_flags, fn flag ->
if Map.get(flags, flag.name, false) == true, do: flag.name
end)
end
defp dispatch_root_flag(cli, flag_name, result) do
input = %Input{flags: result.flags, args: result.args}
try do
cli.handler.handle_input(flag_name, input)
rescue
e in [UndefinedFunctionError] ->
log_handler_error(e, cli, [flag_name], "Handler function not defined", __STACKTRACE__)
{:error, {1, "Root flag '#{flag_name}' is not implemented"}}
e in [FunctionClauseError] ->
log_handler_error(e, cli, [flag_name], "Invalid arguments for handler", __STACKTRACE__)
{:error, {1, "Invalid arguments for root flag '#{flag_name}'"}}
e in [ArgumentError] ->
log_handler_error(e, cli, [flag_name], "Invalid argument", __STACKTRACE__)
{:error, {1, "Invalid argument: #{Exception.message(e)}"}}
exception ->
log_handler_error(exception, cli, [flag_name], "Unexpected error in handler", __STACKTRACE__)
{:error, {1, "An error occurred while executing root flag '#{flag_name}'"}}
end
end
end