Packages

WebSocket-first Elixir framework with auto-generated HTTP MCP APIs

Current section

Files

Jump to
dialup lib command.ex
Raw

lib/command.ex

defmodule Dialup.Command do
@moduledoc false
@doc """
Builds a Commanded command struct from context, command name, bind map, and request params.
"""
def build(context_module, command_name, bind, arguments)
when is_map(bind) and is_map(arguments) do
module = command_module(context_module, command_name)
with {:ok, fields} <- merge_fields(bind, arguments) do
{:ok, struct(module, fields)}
end
rescue
e in [ArgumentError, UndefinedFunctionError] ->
{:error, {:invalid_command, Exception.message(e)}}
end
@doc """
Resolves the command struct module for a context and command atom.
"""
def command_module(context_module, command_name) do
Module.concat([context_module, Commands, Macro.camelize(to_string(command_name))])
end
@doc """
Maps a dispatch error to a human-readable message using static action error metadata.
"""
def map_error(action, reason) do
errors = Map.get(action, :errors, %{})
key =
case reason do
atom when is_atom(atom) -> atom
{atom, _} when is_atom(atom) -> atom
_ -> nil
end
if key && Map.has_key?(errors, key) do
Map.fetch!(errors, key)
else
"操作を完了できませんでした"
end
end
defp merge_fields(bind, arguments) do
with {:ok, normalized_bind} <- normalize_key_map(bind),
{:ok, normalized_args} <-
normalize_key_map(Map.drop(arguments, ["_version", :_version, "_dialup_actor"])) do
{:ok, Map.merge(normalized_bind, normalized_args)}
end
end
defp normalize_key_map(map) do
Enum.reduce_while(map, {:ok, %{}}, fn {k, v}, {:ok, acc} ->
case key_to_atom(k) do
{:ok, key} -> {:cont, {:ok, Map.put(acc, key, v)}}
{:error, _} = error -> {:halt, error}
end
end)
end
defp key_to_atom(key) when is_atom(key), do: {:ok, key}
defp key_to_atom(key) when is_binary(key) do
{:ok, String.to_existing_atom(key)}
rescue
ArgumentError -> {:error, {:invalid_command, "unknown parameter key: #{key}"}}
end
end