123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- -module(rest).
- %%-author('Dmitry Bushmelev').
- -export([behaviour_info/1, parse_transform/2, generate_to_json/3, binarize/1,
- generate_from_json/3, from_json/2, to_json/1, to_binary/1, parse/1, atomize/1]).
- 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} }, _RecordName) ->
- %io:format("Case 1: ~p~n", [Field]),
- Field;
- record_field({record_field, _, {atom, _, Field}, _Type}, _RecordName) ->
- %io:format("Case 2: ~p~n", [Field]),
- Field;
- record_field({typed_record_field, {record_field, _, {atom, _, Field}, _}, Type}, RecordName) ->
- Rec = allow(Type),
- erlang:put({RecordName, Field}, Rec),
- %case Rec of
- % undefined -> io:format("Case 3: ~p~n", [Field]);
- % _ -> io:format("Case 3: ~p, Link: ~p~n", [Field, Rec])
- %end,
- Field.
- allow({type, _, union, Components}) -> findType(Components);
- allow(Type) -> findType([Type]).
- findType([]) -> undefined;
- findType([{type, _, record, [{atom, _, X}]}|_T]) -> X;
- findType([{remote_type, _, [{atom, _, _}, {atom, _, X}, _]}|_T]) -> X;
- findType([_H|T]) ->
- %io:format("Unknown Type: ~p~n", [_H]),
- findType(T).
- record_fields(_RecordName, []) -> [];
- record_fields(RecordName, [{attribute, _, record, {RecordName, Fields}} | _Forms]) ->
- [record_field(Field, RecordName) || Field <- Fields];
- record_fields(RecordName, [_ | Forms]) -> record_fields(RecordName, Forms).
- 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) -> erlang:list_to_atom("V_" ++ erlang: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'}, % here is fix for recursive binarized preprocessing to raw X:from_json
- {call, Line, {remote, Line, {atom, Line, ?MODULE}, {atom, Line, binarize}}, [{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, erlang: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)},
- {atom, Line,
- case erlang:get({Record, Field}) of
- undefined -> Record;
- FieldType -> FieldType
- end}]}
- }]}]}]}
- | 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>>, _) ->
- erlang:binary_to_list(Data);
- from_json({struct, Props}, X) ->
- from_json(Props, X);
- from_json([{Key, _}|_] = Props, X) when Key =/= struct ->
- X:from_json(binarize(Props), X:new());
- from_json(Any, _X) -> Any.
- atomize([{Key, _}|_]=Props) when Key =/= struct ->
- lists:map(fun ({K, V}) when erlang:is_atom(K) -> {K, V};
- ({K, V}) when erlang:is_binary(K) -> {erlang:list_to_existing_atom(erlang:binary_to_list(K)), V} end, Props);
- atomize(X) -> X.
- binarize([{Key, _}|_]=Props) when Key =/= struct ->
- lists:map(fun ({K, V}) when erlang:is_atom(K) -> {erlang:list_to_binary(erlang:atom_to_list(K)), allowed_value(V)};
- ({K, V}) when erlang:is_binary(K) -> {K, allowed_value(V)} end, Props);
- binarize(X) -> X.
- allowed_value(X) when erlang:is_reference(X) -> [];
- allowed_value(X) -> X.
- to_json(X) when erlang:is_tuple(X) ->
- Module = erlang:hd(erlang:tuple_to_list(X)),
- Module:to_json(X);
- to_json(Data) ->
- case is_string(Data) of
- true -> to_binary(Data);
- false -> json_match(Data)
- end.
- json_match([{_, _} | _] = Props) ->
- [{to_binary(Key), to_json(Value)} || {Key, Value} <- Props];
- json_match([_ | _] = NonEmptyList) ->
- [to_json(X) || X <- NonEmptyList];
- json_match(Any) -> Any.
- is_char(C) -> erlang:is_integer(C) andalso C >= 0 andalso C =< 255.
- is_string([N | _] = PossibleString) when erlang:is_number(N) -> lists:all(fun is_char/1, PossibleString);
- is_string(_) -> false.
- to_binary(A) when erlang:is_atom(A) -> erlang:atom_to_binary(A, latin1);
- to_binary(B) when erlang:is_binary(B) -> B;
- to_binary(I) when erlang:is_integer(I) -> to_binary(erlang:integer_to_list(I));
- to_binary(F) when erlang:is_float(F) -> erlang:float_to_binary(F, [{decimals, 9}, compact]);
- to_binary(L) when erlang:is_list(L) -> erlang:iolist_to_binary(L).
- parse(String) ->
- %{ok, Tokens, _EndLine} = erl_scan:string(String),
- {ok, Tokens, _EndLine} = erl_scan:string(String ++ "."),
- {ok, AbsForm} = erl_parse:parse_exprs(Tokens),
- {value, Value, _Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
- Value.
|