1212-include (" cli_console_command.hrl" ).
1313-include_lib (" stdlib/include/ms_transform.hrl" ).
1414
15- -export ([start_link /0 , register /4 , run /2 , get_help / 1 , handle_info / 2 ]).
16- -export ([init /1 , handle_call /3 , handle_cast /2 ]).
15+ -export ([start_link /0 , register /4 , register /2 , run / 2 , get_help / 1 ]).
16+ -export ([init /1 , handle_call /3 , handle_cast /2 , handle_info / 2 ]).
1717
1818-define (SERVER , ? MODULE ).
1919-define (ETS_TABLE_NAME , cli_commands ).
20+ -define (PERSISTENT_TERM_KEY , {? MODULE , register_command_callbacks }).
2021
2122-record (state , {
2223 running_commands = [] :: [{CommandPid :: pid (), From :: {pid (), term ()}}]
2829% %% API
2930% %%===================================================================
3031
31- -spec register ([command ()], [command_argument ()], command_fun (), string ()) -> ok .
32+ -spec register ([command ()], [command_argument ()], command_fun (), string ()) ->
33+ ok | {error , {multiple_argument_definitions , Name :: string ()}}.
3234register (Command , Arguments , Fun , Description ) ->
3335 gen_server :call (? SERVER , {register , Command , Arguments , Fun , Description }).
3436
37+ -spec register (module (), atom ()) ->
38+ ok | {error , {multiple_argument_definitions , Name :: string ()}}.
39+ register (Module , Function ) ->
40+ gen_server :call (? SERVER , {register , Module , Function }).
41+
3542-spec run ([string ()], proplists :proplist ()) ->
3643 {ok , term ()} |
3744 {error , command_not_found } |
@@ -58,7 +65,8 @@ start_link() ->
5865-spec init (term ()) -> {ok , state ()}.
5966init (_ ) ->
6067 process_flag (trap_exit , true ),
61- ets :new (cli_commands , [set , protected , named_table ]),
68+ ets :new (? ETS_TABLE_NAME , [set , protected , named_table ]),
69+ init_commands_from_registered_callback (),
6270 {ok , # state {}}.
6371
6472-spec handle_call (term (), {pid (), Tag :: term ()}, state ()) ->
@@ -81,7 +89,15 @@ handle_call({get_help, Command}, _From, State = #state{}) ->
8189 {reply , {ok , [cli_console_formatter :title (" Help~n " ) | Output ]}, State };
8290handle_call ({register , Command , ArgsDef , Fun , Description }, _From ,
8391 State = # state {}) ->
84- {reply , register_command (Command , ArgsDef , Fun , Description ), State }.
92+ {reply , register_command (Command , ArgsDef , Fun , Description ), State };
93+ handle_call ({register , Module , Function }, _From , State = # state {}) ->
94+ case catch register_commands_via_callback ([{Module , Function }]) of
95+ ok ->
96+ store_command_register_callback (Module , Function ),
97+ {reply , ok , State };
98+ Else ->
99+ {reply , {error , Else }, State }
100+ end .
85101
86102-spec handle_cast (term (), state ()) -> {noreply , state ()}.
87103handle_cast (_Request , State = # state {}) ->
@@ -123,9 +139,7 @@ run_command(Command, Args, ArgsDef, Fun) ->
123139 true ->
124140 get_help (Command );
125141 _ ->
126- MissingArguments = get_missing_arguments (ArgsDef , NewArgs ),
127- evaluate_argument_check (MissingArguments ,
128- fun () -> execute_fun (Fun , NewArgs ) end )
142+ evaluate_argument_check (ArgsDef , NewArgs , Fun )
129143 end ;
130144 Else ->
131145 Else
@@ -141,6 +155,10 @@ execute_fun(Fun, NewArgs) ->
141155 ]}
142156 end .
143157
158+ evaluate_argument_check (ArgsDef , NewArgs , Fun ) ->
159+ MissingArguments = get_missing_arguments (ArgsDef , NewArgs ),
160+ evaluate_argument_check (MissingArguments , fun () -> execute_fun (Fun , NewArgs ) end ).
161+
144162evaluate_argument_check ([], Fun ) ->
145163 Fun ();
146164evaluate_argument_check (MissingArguments , _Fun ) ->
@@ -281,7 +299,7 @@ format_command({Commands, Desc}) ->
281299 cli_console_formatter :text (" ~ts ~ts " , [CommandStr , Desc ]).
282300
283301format_command_string (# argument {name = Name }) ->
284- " <" ++ Name ++ " >" ;
302+ " <" ++ Name ++ " >" ;
285303format_command_string (Command ) ->
286304 Command .
287305
@@ -321,8 +339,10 @@ register_command(Command, ArgsDef, Fun, Description) ->
321339 CommandArguments = get_command_arguments (Command ),
322340 case check_multiple_argdefs (ArgsDef ++ CommandArguments ) of
323341 ok ->
324- ets :match_delete (? ETS_TABLE_NAME , {generate_match_head (Command ),
325- generate_filter (Command ), []}),
342+ % need to clean up previous commands to make does not register multiple
343+ % wildcard argument command
344+ [ets :delete (? ETS_TABLE_NAME , PrevCommand ) ||
345+ {PrevCommand , _ , _ , _ } <- get_wildcard_command (Command )],
326346 true = ets :insert (? ETS_TABLE_NAME , {Command , ArgsDef , Fun , Description }),
327347 ok ;
328348 Else ->
@@ -388,17 +408,53 @@ generate_filter([], _ItemCount, Where) ->
388408 lists :reverse (Where );
389409generate_filter ([Command | Rest ], ItemCount , Where ) ->
390410 Item = item_num (ItemCount ),
391- Filter = {'orelse' ,
392- {'==' , Command , Item },
393- {'==' , argument , {element , 1 , Item }}
394- },
411+ Filter = generate_filter (Command , Item ),
395412 generate_filter (Rest , ItemCount + 1 , [Filter | Where ]).
396413
414+ generate_filter (# argument {}, Item ) ->
415+ {'==' , argument , {element , 1 , Item }};
416+ generate_filter (Command , Item ) ->
417+ {'orelse' ,
418+ {'==' , Command , Item },
419+ {'==' , argument , {element , 1 , Item }}
420+ }.
421+
397422item_num (ItemNum ) ->
398423 list_to_atom (" $" ++ integer_to_list (ItemNum )).
399424
400425extract_command_args (CommandSpec , Command , Args ) ->
401426 CommandArgWithIndex = get_command_arguments_with_index (CommandSpec ),
402427 lists :foldl (fun ({Index , # argument {name = Name }}, Acc ) ->
403428 [{Name , lists :nth (Index , Command )} | Acc ]
404- end , Args , CommandArgWithIndex ).
429+ end , Args , CommandArgWithIndex ).
430+
431+ store_command_register_callback (Module , Function ) ->
432+ Callbacks = get_command_register_callbacks (),
433+ Data = {Module , Function },
434+ NewCallbacks =
435+ case lists :member (Data , Callbacks ) of
436+ true ->
437+ Callbacks ;
438+ false ->
439+ [Data | Callbacks ]
440+ end ,
441+ persistent_term :put (? PERSISTENT_TERM_KEY , NewCallbacks ).
442+
443+ get_command_register_callbacks () ->
444+ persistent_term :get (? PERSISTENT_TERM_KEY , []).
445+
446+ init_commands_from_registered_callback () ->
447+ register_commands_via_callback (get_command_register_callbacks ()).
448+
449+ register_commands_via_callback ([]) ->
450+ ok ;
451+ register_commands_via_callback ([{Module , Function } | Rest ]) ->
452+ Result = apply (Module , Function , []),
453+ register_command_callback_result (Result ),
454+ register_commands_via_callback (Rest ).
455+
456+ register_command_callback_result ([]) ->
457+ ok ;
458+ register_command_callback_result ([{Command , ArgDef , Fun , Description } | Rest ]) ->
459+ ok = register_command (Command , ArgDef , Fun , Description ),
460+ register_command_callback_result (Rest ).
0 commit comments