11-module (rabbit_cli_main ).
22
3+ -include (" src/sysexits.hrl" ).
4+
35-export ([run /2 ]).
46
57-spec run (string (), iodata ()) -> ok .
68
7- run (_ProgName , RawArgs ) ->
8- % % 1. Argument parsing.
9+ run (ProgName , RawArgs ) ->
10+ ParserOpts = #{ progname => ProgName },
911 CommandMods = discover_commands (),
10- CommandMap = collect_args_spec (CommandMods , #{}),
11- Ret = argparse :parse (RawArgs , CommandMap ),
12-
13- % % 2. Output setup.
14- rabbit_cli_io :setup (Ret ),
15- set_log_level (Ret ),
16- logger :debug (" argparse:parse/2 -> ~p~n " , [Ret ]),
17-
18- % % 3. Command execution.
12+ CommandSpec = collect_args_spec (CommandMods , ParserOpts ),
13+
14+ Ret = try
15+ % % 1. Argument parsing.
16+ ParsedArgs = argparse :parse (RawArgs , CommandSpec , ParserOpts ),
17+
18+ % % 2. Output setup.
19+ rabbit_cli_io :setup (ParsedArgs ),
20+ set_log_level (ParsedArgs ),
21+ logger :debug (" argparse:parse/2 -> ~p~n " , [ParsedArgs ]),
22+
23+ % % 3. Command execution.
24+ Result = case ParsedArgs of
25+ {ArgMap , PathTo } ->
26+ run_handler (
27+ CommandSpec ,
28+ ArgMap ,
29+ PathTo ,
30+ undefined );
31+ ArgMap ->
32+ run_handler (
33+ CommandSpec ,
34+ ArgMap ,
35+ {[], CommandSpec },
36+ {CommandMods , #{}})
37+ end ,
38+
39+ % % 4. Close output and return exit status.
40+ rabbit_cli_io :close (),
41+ Result
42+ catch
43+ error :{argparse , Reason } ->
44+ Prefixes = maps :get (prefixes , ParserOpts , " -" ),
45+ case help_requested (Reason , Prefixes ) of
46+ false ->
47+ Error = argparse :format_error (
48+ Reason ,
49+ CommandSpec ,
50+ ParserOpts ),
51+ io :format (standard_error , " error: ~s " , [Error ]);
52+ CommandPath ->
53+ Help = argparse :help (
54+ CommandSpec ,
55+ ParserOpts #{command => tl (CommandPath )}),
56+ io :format (standard_error , " ~s " , [Help ])
57+ end ,
58+ {exit , ? EX_USAGE }
59+ end ,
1960 case Ret of
20- {ArgMap , PathTo } ->
21- run_handler (
22- CommandMap , ArgMap , PathTo , undefined );
23- ArgMap ->
24- % { maps:find(default, Options), Modules, Options}
25- run_handler (
26- CommandMap , ArgMap , {[], CommandMap }, {CommandMods , #{}})
27- end ,
28-
29-
30- % % 4. Close output and return exit status.
31- rabbit_cli_io :close (),
32- ok .
61+ ok -> ok ;
62+ {exit , ExitCode } -> halt (ExitCode )
63+ end .
3364
3465discover_commands () ->
3566 [% TODO: Generate that list.
@@ -154,37 +185,37 @@ create_handlers(Mod, _CmdName, Cmd0, DefaultTerm) ->
154185 Cmd #{commands => NewCmds }
155186 end .
156187
157- run_handler (CmdMap , ArgMap , {Path , #{handler := {Mod , ModFun , Default }}}, _MO ) ->
158- ArgList = arg_map_to_arg_list (CmdMap , Path , ArgMap , Default ),
188+ run_handler (CmdSpec , ArgMap , {Path , #{handler := {Mod , ModFun , Default }}}, _MO ) ->
189+ ArgList = arg_map_to_arg_list (CmdSpec , Path , ArgMap , Default ),
159190 % % if argument count may not match, better error can be produced
160191 erlang :apply (Mod , ModFun , ArgList );
161- run_handler (_CmdMap , ArgMap , {_Path , #{handler := {Mod , ModFun }}}, _MO ) when is_atom (Mod ), is_atom (ModFun ) ->
192+ run_handler (_CmdSpec , ArgMap , {_Path , #{handler := {Mod , ModFun }}}, _MO ) when is_atom (Mod ), is_atom (ModFun ) ->
162193 Mod :ModFun (ArgMap );
163- run_handler (CmdMap , ArgMap , {Path , #{handler := {Fun , Default }}}, _MO ) when is_function (Fun ) ->
164- ArgList = arg_map_to_arg_list (CmdMap , Path , ArgMap , Default ),
194+ run_handler (CmdSpec , ArgMap , {Path , #{handler := {Fun , Default }}}, _MO ) when is_function (Fun ) ->
195+ ArgList = arg_map_to_arg_list (CmdSpec , Path , ArgMap , Default ),
165196 % % if argument count may not match, better error can be produced
166197 erlang :apply (Fun , ArgList );
167- run_handler (_CmdMap , ArgMap , {_Path , #{handler := Handler }}, _MO ) when is_function (Handler , 1 ) ->
198+ run_handler (_CmdSpec , ArgMap , {_Path , #{handler := Handler }}, _MO ) when is_function (Handler , 1 ) ->
168199 Handler (ArgMap );
169200% % below is compatibility mode: cli/1 behaviour has been removed in 1.1.0, but
170201% % is still honoured for existing users
171- run_handler (CmdMap , ArgMap , {[], _ }, {Modules , Options }) when is_map_key (default , Options ) ->
172- ArgList = arg_map_to_arg_list (CmdMap , [], ArgMap , maps :get (default , Options )),
173- exec_cli (Modules , CmdMap , ArgList , Options );
174- run_handler (CmdMap , ArgMap , {[], _ }, {Modules , Options }) ->
202+ run_handler (CmdSpec , ArgMap , {[], _ }, {Modules , Options }) when is_map_key (default , Options ) ->
203+ ArgList = arg_map_to_arg_list (CmdSpec , [], ArgMap , maps :get (default , Options )),
204+ exec_cli (Modules , CmdSpec , ArgList , Options );
205+ run_handler (CmdSpec , ArgMap , {[], _ }, {Modules , Options }) ->
175206 % {undefined, {ok, Default}, Modules, Options}
176- exec_cli (Modules , CmdMap , [ArgMap ], Options ).
207+ exec_cli (Modules , CmdSpec , [ArgMap ], Options ).
177208
178209% % finds first module that exports ctl/1 and execute it
179- exec_cli ([], CmdMap , _ArgMap , ArgOpts ) ->
210+ exec_cli ([], CmdSpec , _ArgMap , ArgOpts ) ->
180211 % % command not found, let's print usage
181- io :format (argparse :help (CmdMap , ArgOpts ));
182- exec_cli ([Mod |Tail ], CmdMap , Args , ArgOpts ) ->
212+ io :format (argparse :help (CmdSpec , ArgOpts ));
213+ exec_cli ([Mod |Tail ], CmdSpec , Args , ArgOpts ) ->
183214 case erlang :function_exported (Mod , cli , length (Args )) of
184215 true ->
185216 erlang :apply (Mod , cli , Args );
186217 false ->
187- exec_cli (Tail , CmdMap , Args , ArgOpts )
218+ exec_cli (Tail , CmdSpec , Args , ArgOpts )
188219 end .
189220
190221% % Given command map, path to reach a specific command, and a parsed argument
@@ -201,3 +232,20 @@ collect_arguments(Command, [H|Tail], Acc) ->
201232 Args = maps :get (arguments , Command , []),
202233 Next = maps :get (H , maps :get (commands , Command , H )),
203234 collect_arguments (Next , Tail , Acc ++ Args ).
235+
236+ % % Finds out whether it was --help/-h requested, and exception was thrown due to that
237+ help_requested ({unknown_argument , CmdPath , [Prefix , $h ]}, Prefixes ) ->
238+ is_prefix (Prefix , Prefixes , CmdPath );
239+ help_requested ({unknown_argument , CmdPath , [Prefix , Prefix , $h , $e , $l , $p ]}, Prefixes ) ->
240+ is_prefix (Prefix , Prefixes , CmdPath );
241+ help_requested (_ , _ ) ->
242+ false .
243+
244+ % % returns CmdPath when Prefix is one of supplied Prefixes
245+ is_prefix (Prefix , Prefixes , CmdPath ) ->
246+ case lists :member (Prefix , Prefixes ) of
247+ true ->
248+ CmdPath ;
249+ false ->
250+ false
251+ end .
0 commit comments