123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- -module(rest).
- -author('Dmitry Bushmelev').
- -behaviour(application).
- -behaviour(supervisor).
- -export([init/1,start/2, stop/1]).
- -export([behaviour_info/1, parse_transform/2, generate_to_json/3,
- generate_from_json/3, from_json/1, to_json/1, to_binary/1]).
- stop(_State) -> ok.
- start(_StartType, _StartArgs) -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
- init([]) ->
- users:init(),
- kvs:join(),
- cowboy:start_clear(http, [{port,application:get_env(n2o,port,8005)}],
- #{env=>#{dispatch=> points() }}),
- {ok, {{one_for_one, 5, 10}, []}}.
- points() -> cowboy_router:compile([{'_', [
- {"/rest/kvs/0/[...]", rest_kvs, []},
- {"/rest/kvs/1/:id/[...]", rest_kvs, []},
- {"/rest/:resource", rest_cowboy, []},
- {"/rest/:resource/:id", rest_cowboy, []}
- ]}]).
- behaviour_info(callbacks) -> [{exists, 1}, {get, 0}, {get, 1}, {post, 1}, {delete, 1}, {from_json, 2}, {to_json, 1}];
- behaviour_info(_) -> undefined.
- parse_transform(Forms, _Options) ->
- % io:format("~p~n", [Forms]),
- RecordName = rest_record(Forms),
- RecordFields = record_fields(RecordName, Forms),
- Forms1 = generate({from_json, 2}, RecordName, RecordFields, Forms),
- Forms2 = generate({to_json, 1}, RecordName, RecordFields, Forms1),
- % io:format("~p~n", [Forms2]),
- Forms2.
- rest_record([]) -> [];
- rest_record([{attribute, _, rest_record, RecordName} | _Forms]) -> RecordName;
- rest_record([_ | Forms]) -> rest_record(Forms).
- record_field({record_field, _, {atom, _, Field} }) -> Field;
- record_field({record_field, _, {atom, _, Field}, _}) -> Field;
- record_field({typed_record_field, {record_field,_,{atom, _, Field},_}, _}) -> Field.
- record_fields(RecordName, [{attribute, _, record, {RecordName, Fields}} | _Forms]) ->
- [record_field(Field) || Field <- Fields];
- record_fields(RecordName, [_ | Forms]) -> record_fields(RecordName, Forms);
- record_fields(__cordName, []) -> [].
- last_export_line(Exports) ->
- case lists:reverse(Exports) of
- [{_, Line, _, _} | _] -> Line;
- _ -> 0 end.
- generate({FunName, _Arity} = Fun, Record, Fields, Forms) ->
- Exports = lists:filter(fun({attribute, _, export, _}) -> true; (_) -> false end, Forms),
- case exported(Fun, Exports) of
- true -> Forms;
- false ->
- Line = last_export_line(Exports),
- Gen = list_to_atom("generate_" ++ atom_to_list(FunName)),
- lists:flatten([?MODULE:Gen(export(Form, Fun, Line), Record, Fields) || Form <- Forms])
- end.
- exported(Fun, Exports) -> lists:member(Fun, lists:flatten([E || {attribute, _, export, E} <- Exports])).
- field_var(Field) -> list_to_atom("V_" ++ atom_to_list(Field)).
- from_json_prelude(Line) ->
- {clause, Line,
- [{nil, Line}, {var, Line, 'Acc'}],
- [],
- [{var, Line, 'Acc'}]}.
- from_json_coda(Line) ->
- {clause, Line,
- [{cons, Line, {var, Line, '_'}, {var, Line, 'Json'}}, {var, Line, 'Acc'}],
- [],
- [{call, Line, {atom, Line, from_json}, [{var, Line, 'Json'}, {var, Line, 'Acc'}]}]}.
- from_json_clauses(_, _, []) -> [];
- from_json_clauses(Line, Record, [Field | Fields]) ->
- [{clause, Line,
- [{cons, Line,
- {tuple, Line,
- [{bin, Line,
- [{bin_element, Line, {string, Line, atom_to_list(Field)}, default, default}]},
- {var, Line, field_var(Field)}]},
- {var, Line, 'Json'}},
- {var, Line, 'Acc'}],
- [],
- [{call, Line,
- {atom, Line, from_json},
- [{var, Line, 'Json'},
- {record, Line,
- {var, Line, 'Acc'},
- Record,
- [{record_field, Line,
- {atom, Line, Field},
- {call, Line,
- {remote, Line, {atom, Line, ?MODULE}, {atom, Line, from_json}},
- [{var, Line, field_var(Field)}]}}]}]}]}
- | from_json_clauses(Line, Record, Fields)].
- generate_from_json({eof, Line}, Record, Fields) ->
- [{function, Line, from_json, 2,
- [from_json_prelude(Line)] ++ from_json_clauses(Line, Record, Fields) ++ [from_json_coda(Line)]},
- {eof, Line + 1}];
- generate_from_json(Form, _, _) -> Form.
- export({attribute, LastExportLine, export, Exports}, Fun, LastExportLine) ->
- {attribute, LastExportLine, export, [Fun | Exports]};
- export(Form, _, _) -> Form.
- to_json_cons(Line, []) -> {nil, Line};
- to_json_cons(Line, [Field | Fields]) ->
- {cons, Line,
- {tuple, Line,
- [{atom, Line, Field},
- {call, Line,
- {remote, Line, {atom, Line, ?MODULE}, {atom, Line, to_json}},
- [{var, Line, field_var(Field)}]}]},
- to_json_cons(Line, Fields)}.
- generate_to_json({eof, Line}, Record, Fields) ->
- [{function, Line, to_json, 1,
- [{clause, Line,
- [{record, Line, Record,
- [{record_field, Line, {atom, Line, F}, {var, Line, field_var(F)}} || F <- Fields]}],
- [],
- [to_json_cons(Line, Fields)]}]},
- {eof, Line + 1}];
- generate_to_json(Form, _, _) -> Form.
- from_json(<<Data/binary>>) -> binary_to_list(Data);
- from_json({struct, Props}) -> from_json(Props);
- from_json([{Key, _} | _] = Props) when Key =/= struct -> lists:foldr(fun props_skip/2, [], Props);
- from_json([_|_] = NonEmptyList) -> [from_json(X) || X <- NonEmptyList];
- from_json(Any) -> Any.
- props_skip({<<BinaryKey/binary>>, Value}, Acc) ->
- try Key = list_to_existing_atom(binary_to_list(BinaryKey)),
- props_skip({Key, Value}, Acc)
- catch _:_ -> Acc end;
- props_skip({Key, Value}, Acc) -> [{Key, from_json(Value)} | Acc].
- to_json(X) when is_tuple(X) -> Module = hd(tuple_to_list(X)), Module:to_json(X);
- to_json(Data) ->
- case is_string(Data) of
- true -> rest:to_binary(Data);
- false -> json_match(Data)
- end.
- json_match([{_, _} | _] = Props) ->
- [ {rest:to_binary(Key), to_json(Value)} || {Key, Value} <- Props];
- json_match([_ | _] = NonEmptyList) -> [to_json(X) || X <- NonEmptyList];
- json_match(Any) -> Any.
- is_char(C) -> is_integer(C) andalso C >= 0 andalso C =< 255.
- is_string([N | _] = PossibleString) when is_number(N) -> lists:all(fun is_char/1, PossibleString);
- is_string(_) -> false.
- to_binary(A) when is_atom(A) -> atom_to_binary(A,latin1);
- to_binary(B) when is_binary(B) -> B;
- to_binary(I) when is_integer(I) -> to_binary(integer_to_list(I));
- to_binary(F) when is_float(F) -> float_to_binary(F,[{decimals,9},compact]);
- to_binary(L) when is_list(L) -> iolist_to_binary(L).
|