rest.erl 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. -module(rest).
  2. -author('Dmitry Bushmelev').
  3. -behaviour(application).
  4. -behaviour(supervisor).
  5. -export([init/1,start/2, stop/1]).
  6. -export([behaviour_info/1, parse_transform/2, generate_to_json/3,
  7. generate_from_json/3, from_json/1, to_json/1, to_binary/1]).
  8. stop(_State) -> ok.
  9. start(_StartType, _StartArgs) -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
  10. init([]) ->
  11. users:init(),
  12. kvs:join(),
  13. cowboy:start_clear(http, [{port,application:get_env(n2o,port,8005)}],
  14. #{env=>#{dispatch=> points() }}),
  15. {ok, {{one_for_one, 5, 10}, []}}.
  16. points() -> cowboy_router:compile([{'_', [
  17. {"/rest/kvs/0/[...]", rest_kvs, []},
  18. {"/rest/kvs/1/:id/[...]", rest_kvs, []},
  19. {"/rest/:resource", rest_cowboy, []},
  20. {"/rest/:resource/:id", rest_cowboy, []}
  21. ]}]).
  22. behaviour_info(callbacks) -> [{exists, 1}, {get, 0}, {get, 1}, {post, 1}, {delete, 1}, {from_json, 2}, {to_json, 1}];
  23. behaviour_info(_) -> undefined.
  24. parse_transform(Forms, _Options) ->
  25. % io:format("~p~n", [Forms]),
  26. RecordName = rest_record(Forms),
  27. RecordFields = record_fields(RecordName, Forms),
  28. Forms1 = generate({from_json, 2}, RecordName, RecordFields, Forms),
  29. Forms2 = generate({to_json, 1}, RecordName, RecordFields, Forms1),
  30. % io:format("~p~n", [Forms2]),
  31. Forms2.
  32. rest_record([]) -> [];
  33. rest_record([{attribute, _, rest_record, RecordName} | _Forms]) -> RecordName;
  34. rest_record([_ | Forms]) -> rest_record(Forms).
  35. record_field({record_field, _, {atom, _, Field} }) -> Field;
  36. record_field({record_field, _, {atom, _, Field}, _}) -> Field;
  37. record_field({typed_record_field, {record_field,_,{atom, _, Field},_}, _}) -> Field.
  38. record_fields(RecordName, [{attribute, _, record, {RecordName, Fields}} | _Forms]) ->
  39. [record_field(Field) || Field <- Fields];
  40. record_fields(RecordName, [_ | Forms]) -> record_fields(RecordName, Forms);
  41. record_fields(__cordName, []) -> [].
  42. last_export_line(Exports) ->
  43. case lists:reverse(Exports) of
  44. [{_, Line, _, _} | _] -> Line;
  45. _ -> 0 end.
  46. generate({FunName, _Arity} = Fun, Record, Fields, Forms) ->
  47. Exports = lists:filter(fun({attribute, _, export, _}) -> true; (_) -> false end, Forms),
  48. case exported(Fun, Exports) of
  49. true -> Forms;
  50. false ->
  51. Line = last_export_line(Exports),
  52. Gen = list_to_atom("generate_" ++ atom_to_list(FunName)),
  53. lists:flatten([?MODULE:Gen(export(Form, Fun, Line), Record, Fields) || Form <- Forms])
  54. end.
  55. exported(Fun, Exports) -> lists:member(Fun, lists:flatten([E || {attribute, _, export, E} <- Exports])).
  56. field_var(Field) -> list_to_atom("V_" ++ atom_to_list(Field)).
  57. from_json_prelude(Line) ->
  58. {clause, Line,
  59. [{nil, Line}, {var, Line, 'Acc'}],
  60. [],
  61. [{var, Line, 'Acc'}]}.
  62. from_json_coda(Line) ->
  63. {clause, Line,
  64. [{cons, Line, {var, Line, '_'}, {var, Line, 'Json'}}, {var, Line, 'Acc'}],
  65. [],
  66. [{call, Line, {atom, Line, from_json}, [{var, Line, 'Json'}, {var, Line, 'Acc'}]}]}.
  67. from_json_clauses(_, _, []) -> [];
  68. from_json_clauses(Line, Record, [Field | Fields]) ->
  69. [{clause, Line,
  70. [{cons, Line,
  71. {tuple, Line,
  72. [{bin, Line,
  73. [{bin_element, Line, {string, Line, atom_to_list(Field)}, default, default}]},
  74. {var, Line, field_var(Field)}]},
  75. {var, Line, 'Json'}},
  76. {var, Line, 'Acc'}],
  77. [],
  78. [{call, Line,
  79. {atom, Line, from_json},
  80. [{var, Line, 'Json'},
  81. {record, Line,
  82. {var, Line, 'Acc'},
  83. Record,
  84. [{record_field, Line,
  85. {atom, Line, Field},
  86. {call, Line,
  87. {remote, Line, {atom, Line, ?MODULE}, {atom, Line, from_json}},
  88. [{var, Line, field_var(Field)}]}}]}]}]}
  89. | from_json_clauses(Line, Record, Fields)].
  90. generate_from_json({eof, Line}, Record, Fields) ->
  91. [{function, Line, from_json, 2,
  92. [from_json_prelude(Line)] ++ from_json_clauses(Line, Record, Fields) ++ [from_json_coda(Line)]},
  93. {eof, Line + 1}];
  94. generate_from_json(Form, _, _) -> Form.
  95. export({attribute, LastExportLine, export, Exports}, Fun, LastExportLine) ->
  96. {attribute, LastExportLine, export, [Fun | Exports]};
  97. export(Form, _, _) -> Form.
  98. to_json_cons(Line, []) -> {nil, Line};
  99. to_json_cons(Line, [Field | Fields]) ->
  100. {cons, Line,
  101. {tuple, Line,
  102. [{atom, Line, Field},
  103. {call, Line,
  104. {remote, Line, {atom, Line, ?MODULE}, {atom, Line, to_json}},
  105. [{var, Line, field_var(Field)}]}]},
  106. to_json_cons(Line, Fields)}.
  107. generate_to_json({eof, Line}, Record, Fields) ->
  108. [{function, Line, to_json, 1,
  109. [{clause, Line,
  110. [{record, Line, Record,
  111. [{record_field, Line, {atom, Line, F}, {var, Line, field_var(F)}} || F <- Fields]}],
  112. [],
  113. [to_json_cons(Line, Fields)]}]},
  114. {eof, Line + 1}];
  115. generate_to_json(Form, _, _) -> Form.
  116. from_json(<<Data/binary>>) -> binary_to_list(Data);
  117. from_json({struct, Props}) -> from_json(Props);
  118. from_json([{Key, _} | _] = Props) when Key =/= struct -> lists:foldr(fun props_skip/2, [], Props);
  119. from_json([_|_] = NonEmptyList) -> [from_json(X) || X <- NonEmptyList];
  120. from_json(Any) -> Any.
  121. props_skip({<<BinaryKey/binary>>, Value}, Acc) ->
  122. try Key = list_to_existing_atom(binary_to_list(BinaryKey)),
  123. props_skip({Key, Value}, Acc)
  124. catch _:_ -> Acc end;
  125. props_skip({Key, Value}, Acc) -> [{Key, from_json(Value)} | Acc].
  126. to_json(X) when is_tuple(X) -> Module = hd(tuple_to_list(X)), Module:to_json(X);
  127. to_json(Data) ->
  128. case is_string(Data) of
  129. true -> rest:to_binary(Data);
  130. false -> json_match(Data)
  131. end.
  132. json_match([{_, _} | _] = Props) ->
  133. [ {rest:to_binary(Key), to_json(Value)} || {Key, Value} <- Props];
  134. json_match([_ | _] = NonEmptyList) -> [to_json(X) || X <- NonEmptyList];
  135. json_match(Any) -> Any.
  136. is_char(C) -> is_integer(C) andalso C >= 0 andalso C =< 255.
  137. is_string([N | _] = PossibleString) when is_number(N) -> lists:all(fun is_char/1, PossibleString);
  138. is_string(_) -> false.
  139. to_binary(A) when is_atom(A) -> atom_to_binary(A,latin1);
  140. to_binary(B) when is_binary(B) -> B;
  141. to_binary(I) when is_integer(I) -> to_binary(integer_to_list(I));
  142. to_binary(F) when is_float(F) -> float_to_binary(F,[{decimals,9},compact]);
  143. to_binary(L) when is_list(L) -> iolist_to_binary(L).