Skip to content

Commit 55bd9f8

Browse files
author
Sean Cribbs
committed
Introduce spec record for args and flags, datatypes, and validators.
This uses cuttlefish's datatype conversion routines to automatically parse kvargs and flags. This relieves the burden of conversion from the user and resolves the issue of needing to customize error messages emitted from typecast functions, because cuttlefish already provides them. See #44. In order to maintain the same functionality provided by typecast (for example, clique_typecast:to_node/1), an additional 'validator' option is now recognized. The validator will be called after type conversion. While adding these features, the proplist options seemed to be complicating too much of the code, so the internal representation of an arg or flag spec was converted to a record with all the necessary fields. Users of the code will not need to change passed arguments because the record is only used internally for efficiency and clarity. Additionally, the record enables us to avoid calling list_to_atom/1 on user input in the parser, and instead use simple matching and lists:keyfind instead. All atom creation is done when registering the command, improving safety.
1 parent 1e1a969 commit 55bd9f8

File tree

6 files changed

+287
-96
lines changed

6 files changed

+287
-96
lines changed

include/clique_specs.hrl

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
%% This record represents the specification for a key-value argument
2+
%% or flag on the command line.
3+
-record(clique_spec,
4+
{
5+
key :: atom(),
6+
name :: string(),
7+
shortname :: char() | undefined,
8+
datatype :: cuttlefish_datatypes:datatype() | undefined,
9+
validator :: fun((term()) -> ok | err()) | undefined,
10+
typecast :: fun((string()) -> err() | term()) | undefined
11+
}).
12+
13+
-type spec() :: #clique_spec{}.

src/clique_command.erl

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
%%
1919
%% -------------------------------------------------------------------
2020
-module(clique_command).
21+
-include("clique_specs.hrl").
2122

2223
-define(cmd_table, clique_commands).
2324

@@ -40,7 +41,9 @@ init() ->
4041

4142
%% @doc Register a cli command (i.e.: "riak-admin handoff status")
4243
-spec register([string()], list(), list(), fun()) -> true.
43-
register(Cmd, Keys, Flags, Fun) ->
44+
register(Cmd, Keys0, Flags0, Fun) ->
45+
Keys = make_specs(Keys0),
46+
Flags = make_specs(Flags0),
4447
ets:insert(?cmd_table, {Cmd, Keys, Flags, Fun}).
4548

4649
-spec run(err()) -> err();
@@ -86,6 +89,11 @@ split_command(Cmd0) ->
8689
clique_parser:is_not_flag(Str)
8790
end, Cmd0).
8891

92+
93+
-spec make_specs([{atom(), proplist()}]) -> [spec()].
94+
make_specs(Specs) ->
95+
[ clique_spec:make(Spec) || Spec <- Specs ].
96+
8997
%% NB This is a bit sneaky. We normally only accept key/value args like
9098
%% "handoff.inbound=off" and flag-style arguments like "--node [email protected]" or "--all",
9199
%% but the builtin "show" and "describe" commands work a bit differently.

src/clique_config.erl

+17-14
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
%%
1919
%% -------------------------------------------------------------------
2020
-module(clique_config).
21+
-include("clique_specs.hrl").
2122

2223
%% API
2324
-export([init/0,
@@ -43,7 +44,9 @@
4344
-type err() :: {error, term()}.
4445
-type status() :: clique_status:status().
4546
-type proplist() :: [{atom(), term()}].
46-
-type flags() :: [{atom() | char(), term()}].
47+
-type flagspecs() :: [spec()].
48+
-type flags() :: proplist().
49+
-type args() :: clique_parser:args().
4750

4851
-type envkey() :: {string(), {atom(), atom()}}.
4952
-type cuttlefish_flag_spec() :: {flag, atom(), atom()}.
@@ -238,15 +241,15 @@ run_callback({Args, Flags, Status}) ->
238241
Status.
239242

240243
-spec get_config(err()) -> err();
241-
({proplist(), flags()}) ->
244+
({args(), flags()}) ->
242245
{proplist(), proplist(), flags()} | err().
243246
get_config({error, _}=E) ->
244247
E;
245248
get_config({[], _Flags}) ->
246249
{error, set_no_args};
247250
get_config({Args, Flags}) ->
248251
[{schema, Schema}] = ets:lookup(?schema_table, schema),
249-
Conf = [{cuttlefish_variable:tokenize(atom_to_list(K)), V} || {K, V} <- Args],
252+
Conf = [{cuttlefish_variable:tokenize(K), V} || {K, V} <- Args],
250253
case cuttlefish_generator:minimal_map(Schema, Conf) of
251254
{error, _, Msg} ->
252255
{error, {invalid_config, Msg}};
@@ -326,18 +329,18 @@ set_remote_app_config(AppConfig) ->
326329
[clique_status:alert([clique_status:text(Alert)])]
327330
end.
328331

329-
-spec config_flags() -> flags().
332+
-spec config_flags() -> flagspecs().
330333
config_flags() ->
331-
[{node, [{shortname, "n"},
332-
{longname, "node"},
333-
{typecast, fun clique_typecast:to_node/1},
334-
{description,
335-
"The node to apply the operation on"}]},
336-
337-
{all, [{shortname, "a"},
338-
{longname, "all"},
339-
{description,
340-
"Apply the operation to all nodes in the cluster"}]}].
334+
[clique_spec:make({node, [{shortname, "n"},
335+
{longname, "node"},
336+
{typecast, fun clique_typecast:to_node/1},
337+
{description,
338+
"The node to apply the operation on"}]}),
339+
340+
clique_spec:make({all, [{shortname, "a"},
341+
{longname, "all"},
342+
{description,
343+
"Apply the operation to all nodes in the cluster"}]})].
341344

342345

343346
-spec get_valid_mappings([string()]) -> err() | [{string(), cuttlefish_mapping:mapping()}].

src/clique_error.erl

+5-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ format(_Cmd, {error, {config_not_settable, Keys}}) ->
8181
format(_Cmd, {error, {nodedown, Node}}) ->
8282
status(io_lib:format("Target node is down: ~p~n", [Node]));
8383
format(_Cmd, {error, bad_node}) ->
84-
status("Invalid node name").
84+
status("Invalid node name");
85+
format(_Cmd, {error, {conversion, _}}=TypeError) ->
86+
%% Type-conversion error originating in cuttlefish
87+
status(cuttlefish_error:xlate(TypeError)).
88+
8589

8690
-spec status(string()) -> status().
8791
status(Str) ->

0 commit comments

Comments
 (0)