Expand multi-letter aliases in OptionParser (#5025)
Before this commit, -abc was processed by OptionParser as an alias. With this commit, we deprecate this behaviour: if -abc still matches an alias, we warn, otherwise, we split it into -a -b -c.
This commit is contained in:
@@ -327,23 +327,34 @@ defmodule OptionParser do
|
||||
{:error, argv}
|
||||
end
|
||||
|
||||
defp next(["-" <> option | rest] = argv, aliases, switches, strict) do
|
||||
# Handles --foo or --foo=bar
|
||||
defp next(["--" <> option | rest], _aliases, switches, strict) do
|
||||
{option, value} = split_option(option)
|
||||
original = "-" <> option
|
||||
tagged = tag_option(option, switches, aliases)
|
||||
tagged = tag_option(option, switches)
|
||||
do_next(tagged, value, "--" <> option, rest, switches, strict)
|
||||
end
|
||||
|
||||
# Handles -a, -abc, -abc=something
|
||||
defp next(["-" <> option = original | rest] = argv, aliases, switches, strict) do
|
||||
{option, value} = split_option(option)
|
||||
original_option = "-" <> option
|
||||
|
||||
cond do
|
||||
negative_number?(original) ->
|
||||
{:error, argv}
|
||||
strict and not option_defined?(tagged, switches) ->
|
||||
{:undefined, original, value, rest}
|
||||
true ->
|
||||
{option, kinds, value} = normalize_option(tagged, value, switches)
|
||||
{value, kinds, rest} = normalize_value(value, kinds, rest, strict)
|
||||
case validate_option(value, kinds) do
|
||||
{:ok, new_value} -> {:ok, option, new_value, rest}
|
||||
:invalid -> {:invalid, original, value, rest}
|
||||
String.contains?(option, ["-", "_"]) ->
|
||||
{:undefined, original_option, value, rest}
|
||||
String.length(option) > 1 ->
|
||||
if (opt = get_option(option)) && (alias = aliases[opt]) do
|
||||
IO.warn "multi-letter aliases are deprecated, got: #{inspect(opt)}"
|
||||
do_next({:default, alias}, value, original_option, rest, switches, strict)
|
||||
else
|
||||
next(expand_multiletter_alias(option, value) ++ rest, aliases, switches, strict)
|
||||
end
|
||||
true ->
|
||||
# We have a regular one-letter alias here
|
||||
tagged = tag_single_letter_alias(option, aliases)
|
||||
do_next(tagged, value, original_option, rest, switches, strict)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -351,6 +362,19 @@ defmodule OptionParser do
|
||||
{:error, argv}
|
||||
end
|
||||
|
||||
defp do_next(tagged, value, original, rest, switches, strict) do
|
||||
if strict and not option_defined?(tagged, switches) do
|
||||
{:undefined, original, value, rest}
|
||||
else
|
||||
{option, kinds, value} = normalize_option(tagged, value, switches)
|
||||
{value, kinds, rest} = normalize_value(value, kinds, rest, strict)
|
||||
case validate_option(value, kinds) do
|
||||
{:ok, new_value} -> {:ok, option, new_value, rest}
|
||||
:invalid -> {:invalid, original, value, rest}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Receives a key-value enumerable and converts it to argv.
|
||||
|
||||
@@ -506,18 +530,18 @@ defmodule OptionParser do
|
||||
end
|
||||
end
|
||||
|
||||
defp tag_option("-no-" <> option, switches, _aliases) do
|
||||
defp tag_option("no-" <> option = original, switches) do
|
||||
cond do
|
||||
(negated = get_option(option)) && :boolean in List.wrap(switches[negated]) ->
|
||||
{:negated, negated}
|
||||
option = get_option("no-" <> option) ->
|
||||
option = get_option(original) ->
|
||||
{:default, option}
|
||||
true ->
|
||||
:unknown
|
||||
end
|
||||
end
|
||||
|
||||
defp tag_option("-" <> option, _switches, _aliases) do
|
||||
defp tag_option(option, _switches) do
|
||||
if option = get_option(option) do
|
||||
{:default, option}
|
||||
else
|
||||
@@ -525,15 +549,20 @@ defmodule OptionParser do
|
||||
end
|
||||
end
|
||||
|
||||
defp tag_option(option, _switches, aliases) when is_binary(option) do
|
||||
opt = get_option(option)
|
||||
if alias = aliases[opt] do
|
||||
defp tag_single_letter_alias(alias, aliases) when is_binary(alias) do
|
||||
if alias = aliases[String.to_atom(alias)] do
|
||||
{:default, alias}
|
||||
else
|
||||
:unknown
|
||||
end
|
||||
end
|
||||
|
||||
defp expand_multiletter_alias(letters, value) when is_binary(letters) do
|
||||
expanded = letters |> String.codepoints() |> Enum.map(&("-" <> &1))
|
||||
last = List.last(expanded) <> if(value, do: "=" <> value, else: "")
|
||||
Enum.drop(expanded, -1) ++ [last]
|
||||
end
|
||||
|
||||
defp option_defined?(:unknown, _switches) do
|
||||
false
|
||||
end
|
||||
|
||||
@@ -58,9 +58,17 @@ defmodule OptionParserTest do
|
||||
== {[s: "from_docs/"], [], []}
|
||||
end
|
||||
|
||||
test "does not parse - as a switch" do
|
||||
assert OptionParser.parse(["-source=from_docs/"], aliases: [s: :source])
|
||||
== {[], [], [{"-source", "from_docs/"}]}
|
||||
test "parses -ab as -a -b" do
|
||||
aliases = [a: :first, b: :second]
|
||||
|
||||
assert OptionParser.parse(["-ab"], aliases: aliases)
|
||||
== {[first: true, second: true], [], []}
|
||||
|
||||
assert OptionParser.parse(["-ab=1"], aliases: aliases, switches: [second: :integer])
|
||||
== {[first: true, second: 1], [], []}
|
||||
|
||||
assert OptionParser.parse(["-ab", "1"], aliases: aliases, switches: [second: :integer])
|
||||
== {[first: true, second: 1], [], []}
|
||||
end
|
||||
|
||||
test "parses configured booleans" do
|
||||
|
||||
Reference in New Issue
Block a user