rest.erl 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. -module(rest).
  2. %%-author('Dmitry Bushmelev').
  3. -export([behaviour_info/1, parse_transform/2, generate_to_json/3, binarize/1,
  4. generate_from_json/3, from_json/2, to_json/1, to_binary/1, parse/1, atomize/1]).
  5. behaviour_info(callbacks) -> [{exists, 1}, {get, 0}, {get, 1}, {post, 1}, {delete, 1}, {from_json, 2}, {to_json, 1}];
  6. behaviour_info(_) -> undefined.
  7. parse_transform(Forms, _Options) ->
  8. %io:format("~p~n", [Forms]),
  9. RecordName = rest_record(Forms),
  10. RecordFields = record_fields(RecordName, Forms),
  11. Forms1 = generate({from_json, 2}, RecordName, RecordFields, Forms),
  12. Forms2 = generate({to_json, 1}, RecordName, RecordFields, Forms1),
  13. %io:format("~p~n", [Forms2]),
  14. Forms2.
  15. rest_record([]) -> [];
  16. rest_record([{attribute, _, rest_record, RecordName} | _Forms]) -> RecordName;
  17. rest_record([_ | Forms]) -> rest_record(Forms).
  18. record_field({record_field, _, {atom, _, Field} }, _RecordName) ->
  19. %io:format("Case 1: ~p~n", [Field]),
  20. Field;
  21. record_field({record_field, _, {atom, _, Field}, _Type}, _RecordName) ->
  22. %io:format("Case 2: ~p~n", [Field]),
  23. Field;
  24. record_field({typed_record_field, {record_field, _, {atom, _, Field}, _}, Type}, RecordName) ->
  25. Rec = allow(Type),
  26. erlang:put({RecordName, Field}, Rec),
  27. %case Rec of
  28. % undefined -> io:format("Case 3: ~p~n", [Field]);
  29. % _ -> io:format("Case 3: ~p, Link: ~p~n", [Field, Rec])
  30. %end,
  31. Field.
  32. allow({type, _, union, Components}) -> findType(Components);
  33. allow(Type) -> findType([Type]).
  34. findType([]) -> undefined;
  35. findType([{type, _, record, [{atom, _, X}]}|_T]) -> X;
  36. findType([{remote_type, _, [{atom, _, _}, {atom, _, X}, _]}|_T]) -> X;
  37. findType([_H|T]) ->
  38. %io:format("Unknown Type: ~p~n", [_H]),
  39. findType(T).
  40. record_fields(_RecordName, []) -> [];
  41. record_fields(RecordName, [{attribute, _, record, {RecordName, Fields}} | _Forms]) ->
  42. [record_field(Field, RecordName) || Field <- Fields];
  43. record_fields(RecordName, [_ | Forms]) -> record_fields(RecordName, Forms).
  44. last_export_line(Exports) ->
  45. case lists:reverse(Exports) of
  46. [{_, Line, _, _} | _] -> Line;
  47. _ -> 0
  48. end.
  49. generate({FunName, _Arity} = Fun, Record, Fields, Forms) ->
  50. Exports = lists:filter(
  51. fun({attribute, _, export, _}) -> true;
  52. (_) -> false
  53. end, Forms),
  54. case exported(Fun, Exports) of
  55. true -> Forms;
  56. false ->
  57. Line = last_export_line(Exports),
  58. Gen = list_to_atom("generate_" ++ atom_to_list(FunName)),
  59. lists:flatten([?MODULE:Gen(export(Form, Fun, Line), Record, Fields) || Form <- Forms])
  60. end.
  61. exported(Fun, Exports) ->
  62. lists:member(Fun, lists:flatten([E || {attribute, _, export, E} <- Exports])).
  63. field_var(Field) -> erlang:list_to_atom("V_" ++ erlang:atom_to_list(Field)).
  64. from_json_prelude(Line) ->
  65. {clause, Line,
  66. [{nil, Line}, {var, Line, 'Acc'}],
  67. [],
  68. [{var, Line, 'Acc'}]}.
  69. from_json_coda(Line) ->
  70. {clause, Line,
  71. [{cons, Line, {var, Line, '_'}, {var, Line, 'Json'}}, {var, Line, 'Acc'}],
  72. [],
  73. [{call, Line, {atom, Line, from_json}, [
  74. %{var, Line, 'Json'}, % here is fix for recursive binarized preprocessing to raw X:from_json
  75. {call, Line, {remote, Line, {atom, Line, ?MODULE}, {atom, Line, binarize}}, [{var, Line, 'Json'}]},
  76. {var, Line, 'Acc'}
  77. ]}]}.
  78. from_json_clauses(_, _, []) -> [];
  79. from_json_clauses(Line, Record, [Field | Fields]) ->
  80. [{clause, Line,
  81. [{cons, Line,
  82. {tuple, Line,
  83. [{bin, Line,
  84. [{bin_element, Line, {string, Line, erlang:atom_to_list(Field)}, default, default}]},
  85. {var, Line, field_var(Field)}]},
  86. {var, Line, 'Json'}},
  87. {var, Line, 'Acc'}],
  88. [],
  89. [{call, Line,
  90. {atom, Line, from_json},
  91. [{var, Line, 'Json'},
  92. {record, Line,
  93. {var, Line, 'Acc'},
  94. Record,
  95. [{record_field, Line,
  96. {atom, Line, Field},
  97. {call, Line,
  98. {remote, Line, {atom, Line, ?MODULE }, {atom, Line, from_json}},
  99. [{var, Line, field_var(Field)},
  100. {atom, Line,
  101. case erlang:get({Record, Field}) of
  102. undefined -> Record;
  103. FieldType -> FieldType
  104. end}]}
  105. }]}]}]}
  106. | from_json_clauses(Line, Record, Fields)].
  107. generate_from_json({eof, Line}, Record, Fields) ->
  108. [{function, Line, from_json, 2,
  109. [from_json_prelude(Line)] ++ from_json_clauses(Line, Record, Fields) ++ [from_json_coda(Line)]},
  110. {eof, Line + 1}];
  111. generate_from_json(Form, _, _) -> Form.
  112. export({attribute, LastExportLine, export, Exports}, Fun, LastExportLine) ->
  113. {attribute, LastExportLine, export, [Fun | Exports]};
  114. export(Form, _, _) -> Form.
  115. to_json_cons(Line, []) -> {nil, Line};
  116. to_json_cons(Line, [Field | Fields]) ->
  117. {cons, Line,
  118. {tuple, Line,
  119. [{atom, Line, Field},
  120. {call, Line,
  121. {remote, Line, {atom, Line, ?MODULE}, {atom, Line, to_json}},
  122. [{var, Line, field_var(Field)}]}]},
  123. to_json_cons(Line, Fields)}.
  124. generate_to_json({eof, Line}, Record, Fields) ->
  125. [{function, Line, to_json, 1,
  126. [{clause, Line,
  127. [{record, Line, Record,
  128. [{record_field, Line, {atom, Line, F}, {var, Line, field_var(F)}} || F <- Fields]}],
  129. [],
  130. [to_json_cons(Line, Fields)]}]},
  131. {eof, Line + 1}];
  132. generate_to_json(Form, _, _) -> Form.
  133. from_json(<<Data/binary>>, _) ->
  134. erlang:binary_to_list(Data);
  135. from_json({struct, Props}, X) ->
  136. from_json(Props, X);
  137. from_json([{Key, _}|_] = Props, X) when Key =/= struct ->
  138. X:from_json(binarize(Props), X:new());
  139. from_json(Any, _X) -> Any.
  140. atomize([{Key, _}|_]=Props) when Key =/= struct ->
  141. lists:map(fun ({K, V}) when erlang:is_atom(K) -> {K, V};
  142. ({K, V}) when erlang:is_binary(K) -> {erlang:list_to_existing_atom(erlang:binary_to_list(K)), V} end, Props);
  143. atomize(X) -> X.
  144. binarize([{Key, _}|_]=Props) when Key =/= struct ->
  145. lists:map(fun ({K, V}) when erlang:is_atom(K) -> {erlang:list_to_binary(erlang:atom_to_list(K)), allowed_value(V)};
  146. ({K, V}) when erlang:is_binary(K) -> {K, allowed_value(V)} end, Props);
  147. binarize(X) -> X.
  148. allowed_value(X) when erlang:is_reference(X) -> [];
  149. allowed_value(X) -> X.
  150. to_json(X) when erlang:is_tuple(X) ->
  151. Module = erlang:hd(erlang:tuple_to_list(X)),
  152. Module:to_json(X);
  153. to_json(Data) ->
  154. case is_string(Data) of
  155. true -> to_binary(Data);
  156. false -> json_match(Data)
  157. end.
  158. json_match([{_, _} | _] = Props) ->
  159. [{to_binary(Key), to_json(Value)} || {Key, Value} <- Props];
  160. json_match([_ | _] = NonEmptyList) ->
  161. [to_json(X) || X <- NonEmptyList];
  162. json_match(Any) -> Any.
  163. is_char(C) -> erlang:is_integer(C) andalso C >= 0 andalso C =< 255.
  164. is_string([N | _] = PossibleString) when erlang:is_number(N) -> lists:all(fun is_char/1, PossibleString);
  165. is_string(_) -> false.
  166. to_binary(A) when erlang:is_atom(A) -> erlang:atom_to_binary(A, latin1);
  167. to_binary(B) when erlang:is_binary(B) -> B;
  168. to_binary(I) when erlang:is_integer(I) -> to_binary(erlang:integer_to_list(I));
  169. to_binary(F) when erlang:is_float(F) -> erlang:float_to_binary(F, [{decimals, 9}, compact]);
  170. to_binary(L) when erlang:is_list(L) -> erlang:iolist_to_binary(L).
  171. parse(String) ->
  172. %{ok, Tokens, _EndLine} = erl_scan:string(String),
  173. {ok, Tokens, _EndLine} = erl_scan:string(String ++ "."),
  174. {ok, AbsForm} = erl_parse:parse_exprs(Tokens),
  175. {value, Value, _Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
  176. Value.