@@ -3,6 +3,11 @@ defmodule OptionParser do
3
3
This module contains functions to parse command line arguments.
4
4
"""
5
5
6
+ @ type argv :: [ String . t ]
7
+ @ type parsed :: Keyword . t
8
+ @ type errors :: Keyword . t
9
+ @ type options :: [ switches: Keyword . t , strict: Keyword . t , aliases: Keyword . t ]
10
+
6
11
@ doc """
7
12
Parses `argv` into a keywords list.
8
13
@@ -32,7 +37,18 @@ defmodule OptionParser do
32
37
## Switches
33
38
34
39
Many times though, it is better to explicitly list the available
35
- switches and their formats. The following types are supported:
40
+ switches and their formats. The switches can be specified via two
41
+ different options:
42
+
43
+ * `:strict` - the switches are strict. Any switch that does not
44
+ exist in the switch list is treated as an error;
45
+
46
+ * `:switches` - configure some switches. Switches that does not
47
+ exist in the switch list are still attempted to be parsed;
48
+
49
+ Note only `:strict` or `:switches` may be given at once.
50
+
51
+ For each switch, the following types are supported:
36
52
37
53
* `:boolean` - Marks the given switch as a boolean. Boolean switches
38
54
never consume the following value unless it is
@@ -41,27 +57,35 @@ defmodule OptionParser do
41
57
* `:float` - Parses the switch as a float;
42
58
* `:string` - Returns the switch as a string;
43
59
44
- If a switch can't be parsed, the option is returned in the invalid
45
- options list (third element of the returned tuple).
60
+ If a switch can't be parsed or is not specfied in the strict case,
61
+ the option is returned in the invalid options list (third element
62
+ of the returned tuple).
46
63
47
64
The following extra "types" are supported:
48
65
49
66
* `:keep` - Keeps duplicated items in the list instead of overriding;
50
67
51
68
Examples:
52
69
53
- iex> OptionParser.parse(["--unlock", "path/to/file"], switches : [unlock: :boolean])
70
+ iex> OptionParser.parse(["--unlock", "path/to/file"], strict : [unlock: :boolean])
54
71
{[unlock: true], ["path/to/file"], []}
55
72
56
73
iex> OptionParser.parse(["--unlock", "--limit", "0", "path/to/file"],
57
- ...> switches : [unlock: :boolean, limit: :integer])
74
+ ...> strict : [unlock: :boolean, limit: :integer])
58
75
{[unlock: true, limit: 0], ["path/to/file"], []}
59
76
60
- iex> OptionParser.parse(["--limit", "3"], switches : [limit: :integer])
77
+ iex> OptionParser.parse(["--limit", "3"], strict : [limit: :integer])
61
78
{[limit: 3], [], []}
62
79
63
- iex> OptionParser.parse(["--limit", "yyz"], switches: [limit: :integer])
64
- {[], [], [limit: "yyz"]}
80
+ iex> OptionParser.parse(["--limit", "xyz"], strict: [limit: :integer])
81
+ {[], [], [limit: "xyz"]}
82
+
83
+ iex> OptionParser.parse(["--unknown", "xyz"], strict: [])
84
+ {[], ["xyz"], [unknown: nil]}
85
+
86
+ iex> OptionParser.parse(["--limit", "3", "--unknown", "xyz"],
87
+ ...> switches: [limit: :integer])
88
+ {[limit: 3, unknown: "xyz"], [], []}
65
89
66
90
## Negation switches
67
91
@@ -85,10 +109,9 @@ defmodule OptionParser do
85
109
{[debug: true], [], []}
86
110
87
111
"""
112
+ @ spec parse ( argv , options ) :: { parsed , argv , errors }
88
113
def parse ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
89
- config =
90
- Keyword . merge ( opts , [ all: true , strict: false ] )
91
- |> compile_config ( )
114
+ config = compile_config ( opts , true )
92
115
do_parse ( argv , config , [ ] , [ ] , [ ] )
93
116
end
94
117
@@ -107,14 +130,12 @@ defmodule OptionParser do
107
130
{[verbose: true, source: "lib"], ["test/enum_test.exs", "--unlock"], []}
108
131
109
132
"""
133
+ @ spec parse_head ( argv , options ) :: { parsed , argv , errors }
110
134
def parse_head ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
111
- config =
112
- Keyword . merge ( opts , [ all: false , strict: false ] )
113
- |> compile_config ( )
135
+ config = compile_config ( opts , false )
114
136
do_parse ( argv , config , [ ] , [ ] , [ ] )
115
137
end
116
138
117
-
118
139
defp do_parse ( [ ] , _config , opts , args , invalid ) do
119
140
{ Enum . reverse ( opts ) , Enum . reverse ( args ) , Enum . reverse ( invalid ) }
120
141
end
@@ -127,11 +148,11 @@ defmodule OptionParser do
127
148
new_opts = do_store_option ( opts , option , value , kinds )
128
149
do_parse ( rest , config , new_opts , args , invalid )
129
150
130
- { :error , { :value , option , value } , rest } ->
151
+ { :invalid , option , value , rest } ->
131
152
# the option exist but it has wrong value
132
153
do_parse ( rest , config , opts , args , [ { option , value } | invalid ] )
133
154
134
- { :error , { : undefined, option , value } , rest } ->
155
+ { :undefined , option , value , rest } ->
135
156
# the option does not exist (for strict cases)
136
157
do_parse ( rest , config , opts , args , [ { option , value } | invalid ] )
137
158
@@ -148,12 +169,32 @@ defmodule OptionParser do
148
169
end
149
170
end
150
171
151
-
152
172
@ doc """
153
173
Low-level function that parses one option.
174
+
175
+ It accepts the same options as `parse/2` and `parse_head/2`
176
+ as both functions are built on top of next. This function
177
+ may return:
178
+
179
+ * `{:ok, key, value, rest}` - the option `key` with `value` was successfully parsed
180
+
181
+ * `{:invalid, key, value, rest}` - the option `key` is invalid with `value`
182
+ (returned when the switch type does not match the one given via the command line)
183
+
184
+ * `{:undefined, key, value, rest}` - the option `key` is undefined
185
+ (returned on strict cases and the switch is unknown)
186
+
187
+ * `{:error, rest}` - there are no switches at the top of the given argv
154
188
"""
189
+
190
+ @ spec next ( argv , options ) ::
191
+ { :ok , key :: atom , value :: term , argv } |
192
+ { :invalid , key :: atom , value :: term , argv } |
193
+ { :undefined , key :: atom , value :: term , argv } |
194
+ { :error , argv }
195
+
155
196
def next ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
156
- { aliases , switches , strict , _ } = compile_config ( opts )
197
+ { aliases , switches , strict , _ } = compile_config ( opts , true )
157
198
next ( argv , aliases , switches , strict )
158
199
end
159
200
@@ -171,13 +212,13 @@ defmodule OptionParser do
171
212
172
213
if strict and not option_defined? ( opt , switches ) do
173
214
{ _ , opt_name } = opt
174
- { :error , { : undefined, opt_name , value } , rest }
215
+ { :undefined , opt_name , value , rest }
175
216
else
176
217
{ opt_name , kinds , value } = normalize_option ( opt , value , switches )
177
218
{ value , kinds , rest } = normalize_value ( value , kinds , rest , strict )
178
219
case validate_option ( opt_name , value , kinds ) do
179
220
{ :ok , new_value } -> { :ok , opt_name , new_value , rest }
180
- :invalid -> { :error , { :value , opt_name , value } , rest }
221
+ :invalid -> { :invalid , opt_name , value , rest }
181
222
end
182
223
end
183
224
end
@@ -188,11 +229,18 @@ defmodule OptionParser do
188
229
189
230
## Helpers
190
231
191
- defp compile_config ( opts ) do
192
- aliases = opts [ :aliases ] || [ ]
193
- switches = opts [ :switches ] || [ ]
194
- strict = opts [ :strict ] || false
195
- all = opts [ :all ] || false
232
+ defp compile_config ( opts , all ) do
233
+ aliases = opts [ :aliases ] || [ ]
234
+
235
+ { switches , strict } = cond do
236
+ s = opts [ :switches ] ->
237
+ { s , false }
238
+ s = opts [ :strict ] ->
239
+ { s , true }
240
+ true ->
241
+ { [ ] , false }
242
+ end
243
+
196
244
{ aliases , switches , strict , all }
197
245
end
198
246
0 commit comments