Skip to content

Commit 76260b0

Browse files
committed
Refactor option parsing internals to support strict mode
1 parent 40a384a commit 76260b0

File tree

1 file changed

+55
-32
lines changed

1 file changed

+55
-32
lines changed

lib/elixir/lib/option_parser.ex

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ defmodule OptionParser do
8686
8787
"""
8888
def parse(argv, opts \\ []) when is_list(argv) and is_list(opts) do
89-
config = compile_config(opts, all: true)
89+
config =
90+
Keyword.merge(opts, [all: true, strict: false])
91+
|> compile_config()
9092
do_parse(argv, config, [], [], [])
9193
end
9294

@@ -106,7 +108,9 @@ defmodule OptionParser do
106108
107109
"""
108110
def parse_head(argv, opts \\ []) when is_list(argv) and is_list(opts) do
109-
config = compile_config(opts, all: false)
111+
config =
112+
Keyword.merge(opts, [all: false, strict: false])
113+
|> compile_config()
110114
do_parse(argv, config, [], [], [])
111115
end
112116

@@ -115,21 +119,21 @@ defmodule OptionParser do
115119
{Enum.reverse(opts), Enum.reverse(args), Enum.reverse(invalid)}
116120
end
117121

118-
defp do_parse(argv, {aliases, switches, all}=config, opts, args, invalid) do
119-
case next(argv, aliases, switches) do
122+
defp do_parse(argv, {aliases, switches, strict, all}=config, opts, args, invalid) do
123+
case next(argv, aliases, switches, strict) do
120124
{:ok, option, value, rest} ->
121125
# the option exist and it was successfully parsed
122126
kinds = List.wrap Keyword.get(switches, option)
123127
new_opts = do_store_option(opts, option, value, kinds)
124128
do_parse(rest, config, new_opts, args, invalid)
125129

126-
{:error, option, value, rest} ->
130+
{:error, {:value, option, value}, rest} ->
127131
# the option exist but it has wrong value
128132
do_parse(rest, config, opts, args, [{option, value}|invalid])
129133

130-
{:error, option, rest} ->
134+
{:error, {:undefined, option, value}, rest} ->
131135
# the option does not exist (for strict cases)
132-
do_parse(rest, config, opts, args, [{option, nil}|invalid])
136+
do_parse(rest, config, opts, args, [{option, value}|invalid])
133137

134138
{:error, ["--"|rest]} ->
135139
{Enum.reverse(opts), Enum.reverse(args, rest), Enum.reverse(invalid)}
@@ -149,44 +153,47 @@ defmodule OptionParser do
149153
Low-level function that parses one option.
150154
"""
151155
def next(argv, opts \\ []) when is_list(argv) and is_list(opts) do
152-
{aliases, switches, _} = compile_config(opts, false)
153-
next(argv, aliases, switches)
156+
{aliases, switches, strict, _} = compile_config(opts)
157+
next(argv, aliases, switches, strict)
154158
end
155159

156-
defp next([], _aliases, _switches) do
160+
defp next([], _aliases, _switches, _strict) do
157161
{:error, []}
158162
end
159163

160-
defp next(["--"|_]=argv, _aliases, _switches) do
164+
defp next(["--"|_]=argv, _aliases, _switches, _strict) do
161165
{:error, argv}
162166
end
163167

164-
defp next(["-" <> option|rest], aliases, switches) do
168+
defp next(["-" <> option|rest], aliases, switches, strict) do
165169
{option, value} = split_option(option)
166-
{option, kinds, value} = normalize_option(option, value, switches, aliases)
167-
168-
#FIXME: don't modify rest for unknown options in strict mode
169-
{value, kinds, rest} = normalize_value(value, kinds, rest)
170+
opt = tag_option(option, switches, aliases)
170171

171-
case validate_option(option, value, kinds) do
172-
{:ok, new_value} ->
173-
{:ok, option, new_value, rest}
174-
175-
:invalid ->
176-
{:error, option, value, rest}
172+
if strict and not option_defined?(opt, switches) do
173+
{_, opt_name} = opt
174+
{:error, {:undefined, opt_name, value}, rest}
175+
else
176+
{opt_name, kinds, value} = normalize_option(opt, value, switches)
177+
{value, kinds, rest} = normalize_value(value, kinds, rest)
178+
case validate_option(opt_name, value, kinds) do
179+
{:ok, new_value} -> {:ok, opt_name, new_value, rest}
180+
:invalid -> {:error, {:value, opt_name, value}, rest}
181+
end
177182
end
178183
end
179184

180-
defp next(argv, _aliases, _switches) do
185+
defp next(argv, _aliases, _switches, _strict) do
181186
{:error, argv}
182187
end
183188

184189
## Helpers
185190

186-
defp compile_config(opts, all: flag) do
191+
defp compile_config(opts) do
187192
aliases = opts[:aliases] || []
188193
switches = opts[:switches] || []
189-
{aliases, switches, flag}
194+
strict = opts[:strict] || false
195+
all = opts[:all] || false
196+
{aliases, switches, strict, all}
190197
end
191198

192199
defp validate_option(option, value, kinds) do
@@ -229,19 +236,35 @@ defmodule OptionParser do
229236
end
230237
end
231238

232-
defp normalize_option(<<?-, option :: binary>>, value, switches, _aliases) do
233-
normalize_option(get_negated(option, switches), value, switches)
239+
defp tag_option(<<?-, option :: binary>>, switches, _aliases) do
240+
get_negated(option, switches)
234241
end
235242

236-
defp normalize_option(option, value, switches, aliases) do
237-
option = get_option(option)
238-
if alias = aliases[option] do
239-
normalize_option({:default, alias}, value, switches)
243+
defp tag_option(option, _switches, aliases) when is_binary(option) do
244+
opt = get_option(option)
245+
if alias = aliases[opt] do
246+
{:default, alias}
240247
else
241-
{option, [:invalid], value}
248+
{:unknown, opt}
242249
end
243250
end
244251

252+
defp option_defined?({:unknown, _option}, _switches) do
253+
false
254+
end
255+
256+
defp option_defined?({:negated, option}, switches) do
257+
Keyword.has_key?(switches, option)
258+
end
259+
260+
defp option_defined?({:default, option}, switches) do
261+
Keyword.has_key?(switches, option)
262+
end
263+
264+
defp normalize_option({:unknown, option}, value, _switches) do
265+
{option, [:invalid], value}
266+
end
267+
245268
defp normalize_option({:negated, option}, nil, switches) do
246269
kinds = List.wrap(switches[option])
247270

0 commit comments

Comments
 (0)