Skip to content

Commit

Permalink
Merge pull request #12 from zotonic/upgrade-euneus
Browse files Browse the repository at this point in the history
Upgrade euneus to 2.3 - add OTP27 to tests
  • Loading branch information
mworrell authored Aug 28, 2024
2 parents 5993145 + 80e9092 commit c42bf8b
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

strategy:
matrix:
otp_version: [24,25,26]
otp_version: [24,25,26,27]
os: [ubuntu-latest]

container:
Expand Down
5 changes: 3 additions & 2 deletions rebar.config
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{require_min_otp_vsn, "21"}.

{deps, [
{euneus, "0.6.0"}
{euneus, "~>2.3"},
{json_polyfill, "0.1.4"}
]}.

{dialyzer, [
{plt_extra_apps, [euneus]}
{plt_extra_apps, [euneus, json_polyfill]}
]}.

{erl_opts, [
Expand Down
9 changes: 6 additions & 3 deletions rebar.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{"1.2.0",
[{<<"euneus">>,{pkg,<<"euneus">>,<<"0.6.0">>},0}]}.
[{<<"euneus">>,{pkg,<<"euneus">>,<<"2.3.0">>},0},
{<<"json_polyfill">>,{pkg,<<"json_polyfill">>,<<"0.1.4">>},0}]}.
[
{pkg_hash,[
{<<"euneus">>, <<"92F0B3DD9440EE59DBE809E8A02C518CC15FA23DA456E351AE9720F06DEB6007">>}]},
{<<"euneus">>, <<"4F9B77EFD1A5C7DD9C3CA0AB0C51172035BC39BFF28B37AA9789C173D1DD397A">>},
{<<"json_polyfill">>, <<"ED9AD7A8CBDB8D1F1E59E22CDAE23A153A5BB93B5529AEBAEB189CA4B4C536C8">>}]},
{pkg_hash_ext,[
{<<"euneus">>, <<"1F9ECA0AC888C5F564A40C3662AC5CA92C474C0D225F5C6B665B2ABBF38C932D">>}]}
{<<"euneus">>, <<"335E8A4D6A7F822E4FEA72CA1D3DD56A456F4E13427AF2C0B77B872584CF8048">>},
{<<"json_polyfill">>, <<"48C397EE2547FA459EDE01A30EC0E85717ABED3010867A63EEAAC5F203274303">>}]}
].
2 changes: 1 addition & 1 deletion src/jsxrecord.app.src
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{application, jsxrecord, [
{description, "JSX wrapper to handle records and 'undefined'"},
{description, "JSON encoder wrapper to handle records and 'undefined'"},
{vsn, "git"},
{registered, []},
{applications, [ kernel, stdlib, syntax_tools, compiler ]},
Expand Down
82 changes: 43 additions & 39 deletions src/jsxrecord.erl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
%% @author Marc Worrell <[email protected]>
%% @copyright 2018-2023 Marc Worrell
%% @copyright 2018-2024 Marc Worrell
%% @doc JSON with records and 'undefined'/'null' mapping.
%% @end

%% Copyright 2018-2023 Marc Worrell
%% Copyright 2018-2024 Marc Worrell
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,7 +40,7 @@

-spec encode( term() ) -> binary().
encode(Source) ->
encode_json(Source).
iolist_to_binary(encode_json(Source)).

-spec decode( binary() | undefined ) -> term().
decode(undefined) ->
Expand Down Expand Up @@ -91,29 +91,27 @@ do_load_records(Modules, CurrRecordDefs) ->

encode_json(Term) ->
Options = #{
codecs => [ timestamp, datetime ],
nulls => [undefined, null],
list_encoder => fun encode_list/2,
unhandled_encoder => fun encode_tuple/2,
error_handler => fun jsx_error/3
skip_values => [],
proplists => true,
encode_list => fun encode_list/2,
encode_tuple => fun encode_tuple/2,
encode_pid => fun encode_unknown/2,
encode_port => fun encode_unknown/2,
encode_reference => fun encode_unknown/2,
encode_term => fun encode_unknown/2
},
case euneus:encode_to_binary(Term, Options) of
{ok, JSON} ->
JSON;
{error, Reason} ->
error(Reason)
end.
euneus:encode_to_iodata(Term, Options).

decode_json(<<>>) -> undefined;
decode_json(B) ->
Options = #{
objects => fun reconstitute_records/2
codecs => [ timestamp, datetime ],
null => undefined,
object_finish => fun reconstitute_records/2
},
case euneus:decode(B, Options) of
{ok, Term} ->
Term;
{error, Reason} ->
error(Reason)
end.
euneus:decode(B, Options).

encode_list([{K, _} | _] = Proplist, Opts) when ?IS_PROPLIST_KEY(K) ->
Map = proplists:to_map(Proplist),
Expand All @@ -132,48 +130,54 @@ encode_tuple(R, _Opts) when is_tuple(R), is_atom(element(1, R)) ->
Def, 2, R, #{ ?RECORD_TYPE => T }
));
error ->
euneus_encoder:throw_unsupported_type_error(R)
encode_json(#{
?RECORD_TYPE => <<"_tuple">>,
<<"_list">> => tuple_to_list(R)
})
end;
encode_tuple(T, _Opts) ->
euneus_encoder:throw_unsupported_type_error(T).
encode_json(#{
?RECORD_TYPE => <<"_tuple">>,
<<"_list">> => tuple_to_list(T)
}).

jsx_error(throw, {{token, Token}, Rest, Opts, Input, Pos, Buffer}, _Stacktrace) ->
?LOG_ERROR(#{
encode_unknown(Term, _State) ->
?LOG_WARNING(#{
in => jsxrecord,
text => <<"Error mapping value to JSON">>,
text => <<"Error mapping value to JSON - replaced with 'null'">>,
result => error,
reason => json_token,
token => Token
token => Term
}),
Replacement = null,
euneus_decoder:resume(Token, Replacement, Rest, Opts, Input, Pos, Buffer);
jsx_error(Class, Reason, Stacktrace) ->
euneus_decoder:handle_error(Class, Reason, Stacktrace).
<<"null">>.

reconstitute_records(M1, _Opts) ->
case maps:find(?RECORD_TYPE, M1) of
{ok, Type} ->
reconstitute_records(Acc, OldAcc) ->
MaybeRecord = case maps:from_list(Acc) of
#{ ?RECORD_TYPE := <<"_tuple">>, <<"_list">> := Elts } ->
list_to_tuple(Elts);
#{ ?RECORD_TYPE := Type } = Map ->
case maps:find(Type, record_defs_int()) of
{ok, Def} ->
Rec = lists:foldl(
fun({F, Default}, Acc) ->
V1 = case maps:get(F, M1, Default) of
fun({F, Default}, RecAcc) ->
V1 = case maps:get(F, Map, Default) of
V when is_map(V), is_list(Default) ->
make_proplist(V);
V ->
V
end,
[ V1 | Acc ]
[ V1 | RecAcc ]
end,
[ binary_to_atom(Type, utf8) ],
Def),
list_to_tuple( lists:reverse(Rec) );
error ->
M1
Map
end;
error ->
M1
end.
Map ->
Map
end,
{MaybeRecord, OldAcc}.

make_proplist(Map) ->
L = maps:to_list(Map),
Expand Down
18 changes: 16 additions & 2 deletions test/jsxrecord_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ all() ->
times,
proplist,
record_proplist,
mixed_list
mixed_list,
unknown_term
].

%%--------------------------------------------------------------------
Expand All @@ -60,7 +61,15 @@ records(_Config) ->

records_nested(_Config) ->
#test{ a = #test{} } = decode( encode( #test{ a = #test{} } ) ),
<<"{\"a\":{\"_type\":\"test\",\"a\":1,\"b\":2,\"c\":null}}">> = encode(#{ a => #test{} }),
JSON = encode(#{ a => #test{} }),
#{
<<"a">> := #{
<<"_type">> := <<"test">>,
<<"a">> := 1,
<<"b">> := 2,
<<"c">> := null
}
} = json:decode(JSON),
#{ <<"a">> := #test{} } = decode( encode(#{ a => #test{} }) ),
ok.

Expand Down Expand Up @@ -124,6 +133,11 @@ mixed_list(_Config) ->
[ match = re:run(JSON, RE, [{capture, none}]) || RE <- E ],
ok.

unknown_term(_Config) ->
<<"null">> = encode(self()),
<<"null">> = encode(make_ref()),
<<"[null,1,null]">> = encode([ make_ref(), 1, self() ]).

%%--------------------------------------------------------------------
%% SUPPORT FUNCTIONS
%%--------------------------------------------------------------------
Expand Down

0 comments on commit c42bf8b

Please sign in to comment.