Skip to content

Instantly share code, notes, and snippets.

@seriyps
Last active February 1, 2018 15:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seriyps/ada87b9896e745f57ddce87672e3728c to your computer and use it in GitHub Desktop.
Save seriyps/ada87b9896e745f57ddce87672e3728c to your computer and use it in GitHub Desktop.
Print erlang records as records, not tuples; print function source code
%% Prints source code of a function.
%%
%% Requires debug_info
%% Will not work for modules mocked by meck
%%
%% > io:format("~s~n", [rf:print_function(dict, new, 0)]).
%% new() ->
%% Empty = mk_seg(16),
%% #dict{empty = Empty, segs = {Empty}}.
-module(rf).
-export([print_function/3]).
print_function(Mod, Fun, Arity) when is_atom(Fun), is_integer(Arity) ->
try
do_print_function(Mod, Fun, Arity)
catch E:R ->
error_logger:warning_msg(
"Error ~p:~p~n~p",
[E, R, erlang:get_stacktrace()]),
io_lib:format("fun ~p:~p/~p", [Mod, Fun, Arity])
end.
do_print_function(Mod, Fun, Arity) when is_atom(Mod) ->
case code:which(Mod) of
preloaded -> error(preloaded);
Filename ->
do_print_function(Filename, Fun, Arity)
end;
do_print_function(Filename, Fun, Arity) when is_list(Filename) ->
{ok, {_Mod, [{abstract_code, {_Version, Forms}}]}} =
beam_lib:chunks(Filename, [abstract_code]),
[FunDef] = [D || {function,_ , Fun0, Arity0, _} = D <- Forms,
Fun0 == Fun, Arity0 == Arity],
erl_prettypr:format(erl_syntax:form_list([FunDef])).
%% Print record as record, not tuple.
%%
%% Requires debug_info
%% This one is kind of slow! Might be used for debugging, but too slow for
%% logging in production!
%% Will not work for modules mocked by meck!
%%
%% > c(rr).
%% {ok,rr}
%% > io:format("~s~n", [rr:print_record(dict:new(), dict)]).
%% #dict{size = 0, n = 16, maxn = 16, bso = 8, exp_size = 80, con_size = 48,
%% empty = {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%% segs = {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}
%% 13> io:format("~s~n", [rr:print_record(element(2, file:read_file_info("/etc/resolv.conf")), file)]).
%% #file_info{size = 329, type = regular, access = read, atime = {{2018,1,30},{9,21,31}},
%% mtime = {{2018,1,24},{10,7,3}}, ctime = {{2018,1,24},{10,7,3}}, mode = 33188,
%% links = 1, major_device = 23, minor_device = 0, inode = 894, uid = 0, gid = 0}
-module(rr).
-export([print_record/2]).
print_record(Record, Module) ->
try
do_print_record(Record, Module)
catch E:R ->
error_logger:warning_msg(
"Error: ~p:~p~n~p",
[E, R, erlang:get_stacktrace()]),
io_lib:format("~500p", [Record])
end.
do_print_record(Record, Module) when is_tuple(Record), is_atom(Module) ->
case code:which(Module) of
preloaded -> Record;
Filename ->
do_print_record(Record, Filename)
end;
do_print_record(Record, Filename) when is_tuple(Record), is_list(Filename) ->
{ok, {_Mod, [{abstract_code, {_Version, Forms}}]}} =
beam_lib:chunks(Filename, [abstract_code]),
RecordName = element(1, Record),
[RecordDef] = [D || {attribute,_,record, {RecordName1, _} = D}
<- Forms, RecordName1 == RecordName],
try do_print_record_pretty(Record, RecordDef)
catch E:R ->
error_logger:warning_msg("prettyprint error ~p:~p", [E, R]),
do_print_record_raw(Record, RecordDef)
end.
%% Handcrafted simple record printer
do_print_record_raw(Record, {Name, FieldDefs}) ->
%% NFields = tuple_size(Record) - 1,
Name = element(1, Record),
FieldVals = tl(tuple_to_list(Record)),
FieldKV = print_pairs(FieldVals, FieldDefs),
["#", atom_to_list(Name), "{", FieldKV, "}"].
print_pairs([Val], [Def]) ->
%% trailing comma
[pair(Val, Def)];
print_pairs([Val | VRest], [Def | DRest]) ->
[pair(Val, Def), ", " | print_pairs(VRest, DRest)];
print_pairs([], []) -> [].
pair(Val, Def) ->
io_lib:format("~p = ~500p", [field_to_name(Def), Val]).
%% Pretty printer with erl_syntax and erl_prettypr
do_print_record_pretty(Record, {Name, FieldDefs}) when element(1, Record) == Name ->
Values = tl(tuple_to_list(Record)),
Names = lists:map(fun field_to_name/1, FieldDefs),
Fields = lists:zip(Names, Values),
FieldsExpr = [erl_syntax:record_field(
erl_syntax:atom(FieldName),
erl_syntax:abstract(FieldValue))
|| {FieldName, FieldValue} <- Fields],
RecordExpr = erl_syntax:record_expr(erl_syntax:atom(Name), FieldsExpr),
erl_prettypr:format(erl_syntax:revert(RecordExpr),
[{paper, 120}, {ribbon, 110}]).
field_to_name({record_field, _, {atom, _, Name}, _Default}) -> Name;
field_to_name({record_field, _, {atom, _, Name}}) -> Name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment