Last active
February 1, 2018 15:43
-
-
Save seriyps/ada87b9896e745f57ddce87672e3728c to your computer and use it in GitHub Desktop.
Print erlang records as records, not tuples; print function source code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% 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])). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% 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