@@ -86,7 +86,10 @@ defmodule OptionParser do
86
86
87
87
"""
88
88
def parse ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
89
- parse ( argv , opts , true )
89
+ config =
90
+ Keyword . merge ( opts , [ all: true , strict: false ] )
91
+ |> compile_config ( )
92
+ do_parse ( argv , config , [ ] , [ ] , [ ] )
90
93
end
91
94
92
95
@ doc """
@@ -105,74 +108,122 @@ defmodule OptionParser do
105
108
106
109
"""
107
110
def parse_head ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
108
- parse ( argv , opts , false )
111
+ config =
112
+ Keyword . merge ( opts , [ all: false , strict: false ] )
113
+ |> compile_config ( )
114
+ do_parse ( argv , config , [ ] , [ ] , [ ] )
109
115
end
110
116
111
- ## Helpers
112
117
113
- defp parse ( argv , opts , bool ) do
114
- aliases = opts [ :aliases ] || [ ]
115
- switches = opts [ :switches ] || [ ]
116
- parse ( argv , aliases , switches , bool )
118
+ defp do_parse ( [ ] , _config , opts , args , invalid ) do
119
+ { Enum . reverse ( opts ) , Enum . reverse ( args ) , Enum . reverse ( invalid ) }
117
120
end
118
121
119
- defp parse ( argv , aliases , switches , all ) do
120
- parse ( argv , aliases , switches , [ ] , [ ] , [ ] , all )
122
+ defp do_parse ( argv , { aliases , switches , strict , all } = config , opts , args , invalid ) do
123
+ case next ( argv , aliases , switches , strict ) do
124
+ { :ok , option , value , rest } ->
125
+ # the option exist and it was successfully parsed
126
+ kinds = List . wrap Keyword . get ( switches , option )
127
+ new_opts = do_store_option ( opts , option , value , kinds )
128
+ do_parse ( rest , config , new_opts , args , invalid )
129
+
130
+ { :error , { :value , option , value } , rest } ->
131
+ # the option exist but it has wrong value
132
+ do_parse ( rest , config , opts , args , [ { option , value } | invalid ] )
133
+
134
+ { :error , { :undefined , option , value } , rest } ->
135
+ # the option does not exist (for strict cases)
136
+ do_parse ( rest , config , opts , args , [ { option , value } | invalid ] )
137
+
138
+ { :error , [ "--" | rest ] } ->
139
+ { Enum . reverse ( opts ) , Enum . reverse ( args , rest ) , Enum . reverse ( invalid ) }
140
+
141
+ { :error , [ arg | rest ] = remaining_args } ->
142
+ # there is no option
143
+ if all do
144
+ do_parse ( rest , config , opts , [ arg | args ] , invalid )
145
+ else
146
+ { Enum . reverse ( opts ) , Enum . reverse ( args , remaining_args ) , Enum . reverse ( invalid ) }
147
+ end
148
+ end
121
149
end
122
150
123
- defp parse ( [ "--" | t ] , _aliases , _switches , dict , args , invalid , _all ) do
124
- { Enum . reverse ( dict ) , Enum . reverse ( args , t ) , Enum . reverse ( invalid ) }
151
+
152
+ @ doc """
153
+ Low-level function that parses one option.
154
+ """
155
+ def next ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
156
+ { aliases , switches , strict , _ } = compile_config ( opts )
157
+ next ( argv , aliases , switches , strict )
125
158
end
126
159
127
- defp parse ( [ "-" <> option | t ] , aliases , switches , dict , args , invalid , all ) do
128
- { option , value } = split_option ( option )
129
- { option , kinds , value } = normalize_option ( option , value , switches , aliases )
130
- { value , kinds , t } = normalize_value ( value , kinds , t )
131
- { dict , invalid } = store_option ( dict , invalid , option , value , kinds )
132
- parse ( t , aliases , switches , dict , args , invalid , all )
160
+ defp next ( [ ] , _aliases , _switches , _strict ) do
161
+ { :error , [ ] }
133
162
end
134
163
135
- defp parse ( [ h | t ] , aliases , switches , dict , args , invalid , true ) do
136
- parse ( t , aliases , switches , dict , [ h | args ] , invalid , true )
164
+ defp next ( [ "--" | _ ] = argv , _aliases , _switches , _strict ) do
165
+ { :error , argv }
137
166
end
138
167
139
- defp parse ( [ ] , _ , _switches , dict , args , invalid , true ) do
140
- { Enum . reverse ( dict ) , Enum . reverse ( args ) , Enum . reverse ( invalid ) }
168
+ defp next ( [ "-" <> option | rest ] , aliases , switches , strict ) do
169
+ { option , value } = split_option ( option )
170
+ opt = tag_option ( option , value , switches , aliases )
171
+
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 , strict )
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
182
+ end
141
183
end
142
184
143
- defp parse ( value , _ , _switches , dict , _args , invalid , false ) do
144
- { Enum . reverse ( dict ) , value , Enum . reverse ( invalid ) }
185
+ defp next ( argv , _aliases , _switches , _strict ) do
186
+ { :error , argv }
145
187
end
146
188
147
- defp store_option ( dict , invalid , option , value , kinds ) do
148
- { invalid_option , value } =
149
- cond do
150
- :invalid in kinds ->
151
- { option , value }
152
- :boolean in kinds ->
153
- case value do
154
- t when t in [ true , "true" ] -> { nil , true }
155
- f when f in [ false , "false" ] -> { nil , false }
156
- _ -> { option , value }
157
- end
158
- :integer in kinds ->
159
- case Integer . parse ( value ) do
160
- { value , "" } -> { nil , value }
161
- _ -> { option , value }
162
- end
163
- :float in kinds ->
164
- case Float . parse ( value ) do
165
- { value , "" } -> { nil , value }
166
- _ -> { option , value }
167
- end
168
- true ->
169
- { nil , value }
170
- end
189
+ ## Helpers
171
190
172
- if invalid_option do
173
- { dict , [ { option , value } | invalid ] }
191
+ defp compile_config ( opts ) do
192
+ aliases = opts [ :aliases ] || [ ]
193
+ switches = opts [ :switches ] || [ ]
194
+ strict = opts [ :strict ] || false
195
+ all = opts [ :all ] || false
196
+ { aliases , switches , strict , all }
197
+ end
198
+
199
+ defp validate_option ( option , value , kinds ) do
200
+ { invalid_opt , value } = cond do
201
+ :invalid in kinds ->
202
+ { option , value }
203
+ :boolean in kinds ->
204
+ case value do
205
+ t when t in [ true , "true" ] -> { nil , true }
206
+ f when f in [ false , "false" ] -> { nil , false }
207
+ _ -> { option , value }
208
+ end
209
+ :integer in kinds ->
210
+ case Integer . parse ( value ) do
211
+ { value , "" } -> { nil , value }
212
+ _ -> { option , value }
213
+ end
214
+ :float in kinds ->
215
+ case Float . parse ( value ) do
216
+ { value , "" } -> { nil , value }
217
+ _ -> { option , value }
218
+ end
219
+ true ->
220
+ { nil , value }
221
+ end
222
+
223
+ if invalid_opt do
224
+ :invalid
174
225
else
175
- { do_store_option ( dict , option , value , kinds ) , invalid }
226
+ { :ok , value }
176
227
end
177
228
end
178
229
@@ -185,19 +236,35 @@ defmodule OptionParser do
185
236
end
186
237
end
187
238
188
- defp normalize_option ( << ?- , option :: binary >> , value , switches , _aliases ) do
189
- normalize_option ( get_negated ( option , switches ) , value , switches )
239
+ defp tag_option ( << ?- , option :: binary >> , value , switches , _aliases ) do
240
+ get_negated ( option , value , switches )
190
241
end
191
242
192
- defp normalize_option ( option , value , switches , aliases ) do
193
- option = get_option ( option )
194
- if alias = aliases [ option ] do
195
- normalize_option ( { :default , alias } , value , switches )
243
+ defp tag_option ( option , _value , _switches , aliases ) when is_binary ( option ) do
244
+ opt = get_option ( option )
245
+ if alias = aliases [ opt ] do
246
+ { :default , alias }
196
247
else
197
- { option , [ :invalid ] , value }
248
+ { :unknown , opt }
198
249
end
199
250
end
200
251
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
+
201
268
defp normalize_option ( { :negated , option } , nil , switches ) do
202
269
kinds = List . wrap ( switches [ option ] )
203
270
@@ -219,21 +286,22 @@ defmodule OptionParser do
219
286
{ option , List . wrap ( switches [ option ] ) , value }
220
287
end
221
288
222
- defp normalize_value ( nil , kinds , t ) do
289
+ defp normalize_value ( nil , kinds , t , strict ) do
290
+ null = if strict , do: nil , else: true
223
291
cond do
224
292
:boolean in kinds ->
225
293
{ true , kinds , t }
226
294
value_in_tail? ( t ) ->
227
295
[ h | t ] = t
228
296
{ h , kinds , t }
229
297
kinds == [ ] ->
230
- { true , kinds , t }
298
+ { null , kinds , t }
231
299
true ->
232
- { true , [ :invalid ] , t }
300
+ { null , [ :invalid ] , t }
233
301
end
234
302
end
235
303
236
- defp normalize_value ( value , kinds , t ) do
304
+ defp normalize_value ( value , kinds , t , _ ) do
237
305
{ value , kinds , t }
238
306
end
239
307
@@ -256,13 +324,17 @@ defmodule OptionParser do
256
324
option |> to_underscore |> String . to_atom
257
325
end
258
326
259
- defp get_negated ( "no-" <> rest = option , switches ) do
327
+ defp get_negated ( "no-" <> rest = option , value , switches ) do
260
328
negated = get_option ( rest )
261
- option = if Keyword . has_key? ( switches , negated ) , do: negated , else: get_option ( option )
329
+ option = if Keyword . has_key? ( switches , negated ) and value == nil do
330
+ negated
331
+ else
332
+ get_option ( option )
333
+ end
262
334
{ :negated , option }
263
335
end
264
336
265
- defp get_negated ( rest , _switches ) do
337
+ defp get_negated ( rest , _value , _switches ) do
266
338
{ :default , get_option ( rest ) }
267
339
end
268
340
end
0 commit comments