Skip to content

Commit 635a269

Browse files
committed
Add JSON formatting
1 parent 8fe81fe commit 635a269

File tree

2 files changed

+130
-33
lines changed

2 files changed

+130
-33
lines changed

deps/rabbit/src/rabbit_cli_commands.erl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-include_lib("kernel/include/logger.hrl").
44

55
-include_lib("rabbit_common/include/logging.hrl").
6+
-include_lib("rabbit_common/include/resource.hrl").
67

78
-export([argparse_def/0, run_command/1, do_run_command/1]).
89
-export([cmd_list_exchanges/1]).
@@ -89,10 +90,11 @@ expand_argparse_def(Def) when is_map(Def) ->
8990
Def;
9091
expand_argparse_def(Defs) when is_list(Defs) ->
9192
lists:foldl(
92-
fun(argparse_def_record_stream, Acc) ->
93+
fun
94+
(argparse_def_record_stream, Acc) ->
9395
Def = rabbit_cli_io:argparse_def(record_stream),
9496
rabbit_cli:merge_argparse_def(Acc, Def);
95-
(Def, Acc) ->
97+
(Def, Acc) ->
9698
Def1 = expand_argparse_def(Def),
9799
rabbit_cli:merge_argparse_def(Acc, Def1)
98100
end, #{}, Defs).
@@ -129,11 +131,11 @@ do_run_command(#{command := #{handler := {Mod, Fun}}} = Context) ->
129131
erlang:apply(Mod, Fun, [Context]).
130132

131133
cmd_list_exchanges(#{arg_map := ArgMap, io := IO}) ->
132-
InfoKeys = rabbit_exchange:info_keys(),
134+
InfoKeys = rabbit_exchange:info_keys() -- [user_who_performed_action],
133135
Fields = lists:map(
134136
fun
135137
(name = Key) ->
136-
#{name => Key, type => resource};
138+
#{name => Key, type => string};
137139
(type = Key) ->
138140
#{name => Key, type => string};
139141
(durable = Key) ->
@@ -146,8 +148,6 @@ cmd_list_exchanges(#{arg_map := ArgMap, io := IO}) ->
146148
#{name => Key, type => term};
147149
(policy = Key) ->
148150
#{name => Key, type => string};
149-
(user_who_performed_action = Key) ->
150-
#{name => Key, type => string};
151151
(Key) ->
152152
#{name => Key, type => term}
153153
end, InfoKeys),
@@ -156,9 +156,14 @@ cmd_list_exchanges(#{arg_map := ArgMap, io := IO}) ->
156156
Exchanges = rabbit_exchange:list(),
157157
lists:foreach(
158158
fun(Exchange) ->
159-
Record0 = rabbit_exchange:info(Exchange),
159+
Record0 = rabbit_exchange:info(Exchange, InfoKeys),
160160
Record1 = lists:sublist(Record0, length(Fields)),
161-
Record2 = [Value || {_Key, Value} <- Record1],
161+
Record2 = [case Value of
162+
#resource{name = N} ->
163+
N;
164+
_ ->
165+
Value
166+
end || {_Key, Value} <- Record1],
162167
rabbit_cli_io:push_new_record(IO, Stream, Record2)
163168
end, Exchanges),
164169
rabbit_cli_io:end_record_stream(IO, Stream),

deps/rabbit/src/rabbit_cli_io.erl

Lines changed: 117 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ argparse_def(record_stream) ->
4545
long => "-format",
4646
short => $f,
4747
type => {atom, [plain, json]},
48-
nargs => 1,
48+
default => plain,
4949
help => "Format output acccording to <FORMAT>"}
5050
]
5151
}.
@@ -80,20 +80,17 @@ init(#{progname := Progname}) ->
8080
{ok, State}.
8181

8282
handle_call(
83-
{start_record_stream, Name, Fields, _ArgMap},
83+
{start_record_stream, Name, Fields, ArgMap},
8484
From,
8585
#?MODULE{record_streams = Streams} = State) ->
86-
Stream = #{name => Name, fields => Fields},
86+
Stream = #{name => Name, fields => Fields, arg_map => ArgMap},
87+
Streams1 = Streams#{Name => Stream},
88+
State1 = State#?MODULE{record_streams = Streams1},
8789
gen_server:reply(From, {ok, Stream}),
8890

89-
FieldNames = [atom_to_list(FieldName)
90-
|| #{name := FieldName} <- Fields],
91-
Header = string:join(FieldNames, "\t"),
92-
io:format("~ts~n", [Header]),
91+
{ok, State2} = format_record_stream_start(Name, State1),
9392

94-
Streams1 = Streams#{Name => Stream},
95-
State1 = State#?MODULE{record_streams = Streams1},
96-
{noreply, State1};
93+
{noreply, State2};
9794
handle_call(stop, _From, State) ->
9895
{stop, normal, ok, State};
9996
handle_call(_Request, _From, State) ->
@@ -109,16 +106,12 @@ handle_cast(
109106
Help = argparse:help(ArgparseDef, Options),
110107
io:format("~s~n", [Help]),
111108
{noreply, State};
112-
handle_cast(
113-
{push_new_record, Name, Record},
114-
#?MODULE{record_streams = Streams} = State) ->
115-
#{fields := Fields} = maps:get(Name, Streams),
116-
Values = format_fields(Fields, Record),
117-
Line = string:join(Values, "\t"),
118-
io:format("~ts~n", [Line]),
119-
{noreply, State};
120-
handle_cast({end_record_stream, _Name}, State) ->
121-
{noreply, State};
109+
handle_cast({push_new_record, Name, Record}, State) ->
110+
{ok, State1} = format_record(Name, Record, State),
111+
{noreply, State1};
112+
handle_cast({end_record_stream, Name}, State) ->
113+
{ok, State1} = format_record_stream_end(Name, State),
114+
{noreply, State1};
122115
handle_cast(_Request, State) ->
123116
{noreply, State}.
124117

@@ -131,13 +124,108 @@ terminate(_Reason, _State) ->
131124
code_change(_OldVsn, State, _Extra) ->
132125
{ok, State}.
133126

127+
format_record_stream_start(
128+
Name,
129+
#?MODULE{record_streams = Streams} = State) ->
130+
Stream = maps:get(Name, Streams),
131+
format_record_stream_start1(Stream, State).
132+
133+
format_record_stream_start1(
134+
#{name := Name, fields := Fields, arg_map := #{format := plain}} = Stream,
135+
#?MODULE{record_streams = Streams} = State) ->
136+
FieldNames = [atom_to_list(FieldName) || #{name := FieldName} <- Fields],
137+
FieldWidths = [case Field of
138+
#{type := string, name := FieldName} ->
139+
lists:max([length(atom_to_list(FieldName)), 20]);
140+
#{name := FieldName} ->
141+
length(atom_to_list(FieldName))
142+
end || Field <- Fields],
143+
Format0 = [rabbit_misc:format("~~-~b.. ts", [Width])
144+
|| Width <- FieldWidths],
145+
Format1 = string:join(Format0, " "),
146+
case isatty(standard_io) of
147+
true ->
148+
io:format("\033[1m" ++ Format1 ++ "\033[0m~n", FieldNames);
149+
false ->
150+
io:format(Format1 ++ "~n", FieldNames)
151+
end,
152+
Stream1 = Stream#{format => Format1},
153+
Streams1 = Streams#{Name => Stream1},
154+
State1 = State#?MODULE{record_streams = Streams1},
155+
{ok, State1};
156+
format_record_stream_start1(
157+
#{name := Name, arg_map := #{format := json}} = Stream,
158+
#?MODULE{record_streams = Streams} = State) ->
159+
Stream1 = Stream#{emitted_fields => 0},
160+
Streams1 = Streams#{Name => Stream1},
161+
State1 = State#?MODULE{record_streams = Streams1},
162+
{ok, State1}.
163+
164+
format_record(Name, Record, #?MODULE{record_streams = Streams} = State) ->
165+
Stream = maps:get(Name, Streams),
166+
format_record1(Stream, Record, State).
167+
168+
format_record1(
169+
#{fields := Fields, arg_map := #{format := plain},
170+
format := Format},
171+
Record,
172+
State) ->
173+
Values = format_fields(Fields, Record),
174+
io:format(Format ++ "~n", Values),
175+
{ok, State};
176+
format_record1(
177+
#{fields := Fields, arg_map := #{format := json},
178+
name := Name, emitted_fields := Emitted} = Stream,
179+
Record,
180+
#?MODULE{record_streams = Streams} = State) ->
181+
Fields1 = [FieldName || #{name := FieldName} <- Fields],
182+
Struct = lists:zip(Fields1, Record),
183+
Json = json:encode(
184+
Struct,
185+
fun
186+
([{_, _} | _] = Value, Encode) ->
187+
json:encode_key_value_list(Value, Encode);
188+
(Value, Encode) ->
189+
json:encode_value(Value, Encode)
190+
end),
191+
case Emitted of
192+
0 ->
193+
io:format("[~n ~ts", [Json]);
194+
_ ->
195+
io:format(",~n ~ts", [Json])
196+
end,
197+
Stream1 = Stream#{emitted_fields => Emitted + 1},
198+
Streams1 = Streams#{Name => Stream1},
199+
State1 = State#?MODULE{record_streams = Streams1},
200+
{ok, State1}.
201+
202+
format_record_stream_end(
203+
Name,
204+
#?MODULE{record_streams = Streams} = State) ->
205+
Stream = maps:get(Name, Streams),
206+
{ok, State1} = format_record_stream_end1(Stream, State),
207+
#?MODULE{record_streams = Streams1} = State1,
208+
Streams2 = maps:remove(Name, Streams1),
209+
State2 = State1#?MODULE{record_streams = Streams2},
210+
{ok, State2}.
211+
212+
format_record_stream_end1(#{arg_map := #{format := plain}}, State) ->
213+
{ok, State};
214+
format_record_stream_end1(#{arg_map := #{format := json}}, State) ->
215+
io:format("~n]~n", []),
216+
{ok, State}.
217+
134218
format_fields(Fields, Values) ->
135219
format_fields(Fields, Values, []).
136220

137221
format_fields([#{type := string} | Rest1], [Value | Rest2], Acc) ->
138222
String = io_lib:format("~ts", [Value]),
139223
Acc1 = [String | Acc],
140224
format_fields(Rest1, Rest2, Acc1);
225+
format_fields([#{type := binary} | Rest1], [Value | Rest2], Acc) ->
226+
String = io_lib:format("~-20.. ts", [Value]),
227+
Acc1 = [String | Acc],
228+
format_fields(Rest1, Rest2, Acc1);
141229
format_fields([#{type := integer} | Rest1], [Value | Rest2], Acc) ->
142230
String = io_lib:format("~b", [Value]),
143231
Acc1 = [String | Acc],
@@ -146,14 +234,18 @@ format_fields([#{type := boolean} | Rest1], [Value | Rest2], Acc) ->
146234
String = io_lib:format("~ts", [if Value -> ""; true -> "" end]),
147235
Acc1 = [String | Acc],
148236
format_fields(Rest1, Rest2, Acc1);
149-
format_fields([#{type := resource} | Rest1], [Value | Rest2], Acc) ->
150-
#resource{name = Name} = Value,
151-
String = io_lib:format("~ts", [Name]),
152-
Acc1 = [String | Acc],
153-
format_fields(Rest1, Rest2, Acc1);
154237
format_fields([#{type := term} | Rest1], [Value | Rest2], Acc) ->
155238
String = io_lib:format("~0p", [Value]),
156239
Acc1 = [String | Acc],
157240
format_fields(Rest1, Rest2, Acc1);
158241
format_fields([], [], Acc) ->
159242
lists:reverse(Acc).
243+
244+
isatty(IoDevice) ->
245+
Opts = io:getopts(IoDevice),
246+
case proplists:get_value(stdout, Opts) of
247+
true ->
248+
true;
249+
_ ->
250+
false
251+
end.

0 commit comments

Comments
 (0)