Packages

Arguments parses command line arguments to a map with a declarative, simple setup

Current section

Files

Jump to
arguments lib parser.ex
Raw

lib/parser.ex

defmodule Arguments.Parser do
@moduledoc """
Parses arguments into a map for easy pattern matching
"""
@doc """
Parse will take a list of incoming arguments and a list of built arguments to generate a map
"""
@spec parse(String.t, [map]) :: map
def parse(incoming, arguments) do
incoming
|> OptionParser.parse(
strict: get_switches(arguments),
aliases: get_aliases(arguments))
|> flags_to_map()
|> apply_commands(arguments)
|> just_flags()
|> apply_defaults(arguments)
end
@spec get_switches([map]) :: map
defp get_switches(arguments) do
arguments
|> Enum.filter(&Map.get(&1, :type))
|> Enum.map(&({&1.name, &1.type}))
end
@spec get_aliases([map]) :: map
defp get_aliases(arguments) do
arguments
|> Enum.filter(&Map.get(&1, :alias))
|> Enum.map(&({&1.alias, &1.name}))
end
@spec get_aliases({term, any}) :: term
defp just_flags({flags, _}), do: flags
@spec flags_to_map({[{any, any}], [String.t], any}) :: {map, [String.t]}
defp flags_to_map({kw, incoming_args, _bad_args}) do
{Map.new(kw), incoming_args}
end
@spec apply_commands({map, []}, any) :: {map, []}
defp apply_commands({flags, []}, _), do: {flags, []}
@spec apply_commands({map, [String.t]}, [map]) :: {map, [String.t]}
defp apply_commands({flags, incoming_args}, arguments) do
arguments
|> Enum.reduce({flags, incoming_args}, &apply_command/2)
end
@spec apply_command(map, {map, []}) :: {map, list}
defp apply_command(
%{argument_type: :command, string_name: str_name, name: cmd_name,
arguments: args},
{flags, [str_name | incoming_args]}) do
arguments = Enum.zip(args, incoming_args)
used = Enum.count(arguments) + 1
flags = arguments
|> Enum.reduce(flags, &add_new_flags/2)
|> Map.put(cmd_name, true) # Tag command
{flags, Enum.drop(incoming_args, used)}
end
@spec apply_command(any, term) :: term
defp apply_command(_, acc), do: acc
@spec apply_defaults(map, [map]) :: map
defp apply_defaults(flags, arguments) do
arguments
|> Enum.reduce(flags, &apply_default/2)
end
# Only apply the default if it exists in the list of flags
@spec apply_defaults(map, map) :: map
defp apply_default(%{name: name, defaults: defaults}, flags) when is_list(defaults) do
case Map.get(flags, name) do
nil -> flags
_ -> Enum.reduce(defaults, flags, &add_new_flags/2)
end
end
@spec apply_defaults(map, map) :: map
defp apply_default(%{name: name, defaults: default_fn}, flags) do
case Map.get(flags, name) do
nil -> flags
value -> Enum.reduce(apply_fn(default_fn, value), flags, &add_new_flags/2)
end
end
@spec apply_defaults(any, term) :: term
defp apply_default(_, flags), do: flags
@spec apply_defaults(any, any) :: Enum.t
defp apply_fn(f, value) do
{func, _} = Code.eval_quoted(f, [])
func.(value)
end
# Apply flag IF not already flagged
@spec add_new_flags({atom, any}, map) :: map
defp add_new_flags({k, v}, flags) do
Map.put_new(flags, k, v)
end
end