add error_handler option and tests

This commit is contained in:
alisdair sullivan 2013-03-04 17:00:34 -08:00
parent a854f4ae3c
commit 6ec70f8a46
3 changed files with 142 additions and 17 deletions

View file

@ -8,5 +8,6 @@
dirty_strings = false,
ignored_bad_escapes = false,
explicit_end = false,
pre_encode = false
pre_encode = false,
error_handler = false
}).

View file

@ -99,11 +99,14 @@ decoder(Handler, State, Config) ->
%% error, incomplete and event macros
-ifndef(error).
-define(error(_State, _Bin, _Handler, _Stack, _Config),
erlang:error(badarg)
-define(error(State, Bin, Handler, Acc, Stack, Config),
case Config#config.error_handler of
false -> erlang:error(badarg);
F -> F(State, Bin, Handler, Acc, Stack, Config)
end
).
-define(error(_State, _Bin, _Handler, _Acc, _Stack, _Config),
erlang:error(badarg)
-define(error(State, Bin, Handler, Stack, Config),
?error(State, Bin, Handler, null, Stack, Config)
).
-endif.
@ -149,7 +152,7 @@ acc_seq(Seq, C) -> [C] ++ Seq.
end_seq(Seq) -> unicode:characters_to_binary(lists:reverse(Seq)).
end_seq(Seq, Config=#config{dirty_strings=true}) -> list_to_binary(lists:reverse(Seq));
end_seq(Seq, #config{dirty_strings=true}) -> list_to_binary(lists:reverse(Seq));
end_seq(Seq, _) -> end_seq(Seq).
@ -173,7 +176,7 @@ maybe_bom(<<16#bb, Rest/binary>>, Handler, Stack, Config) ->
maybe_bom(<<>>, Handler, Stack, Config) ->
?incomplete(start, <<16#ef>>, Handler, Stack, Config);
maybe_bom(Bin, Handler, Stack, Config) ->
?error(start, <<16#ef>>, Handler, Stack, Config).
?error(start, <<16#ef, Bin/binary>>, Handler, Stack, Config).
definitely_bom(<<16#bf, Rest/binary>>, Handler, Stack, Config) ->
@ -181,7 +184,7 @@ definitely_bom(<<16#bf, Rest/binary>>, Handler, Stack, Config) ->
definitely_bom(<<>>, Handler, Stack, Config) ->
?incomplete(start, <<16#ef, 16#bb>>, Handler, Stack, Config);
definitely_bom(Bin, Handler, Stack, Config) ->
?error(start, <<16#ef, 16#bb>>, Handler, Stack, Config).
?error(start, <<16#ef, 16#bb, Bin/binary>>, Handler, Stack, Config).
value(<<?doublequote, Rest/binary>>, Handler, Stack, Config) ->
@ -544,7 +547,7 @@ string(<<X, Rest/binary>>, Handler, Acc, Stack, #config{replaced_bad_utf8=true}
when X >= 240, X =< 247 ->
strip_continuations(Rest, Handler, Acc, Stack, Config, 3);
%% incompletes and unexpected bytes, including orphan continuations
string(<<C, Rest/binary>>, Handler, Acc, Stack, #config{replaced_bad_utf8=true} = Config) ->
string(<<_, Rest/binary>>, Handler, Acc, Stack, #config{replaced_bad_utf8=true} = Config) ->
string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
string(Bin, Handler, Acc, Stack, Config) ->
case partial_utf(Bin) of
@ -733,8 +736,8 @@ decimal(Bin, Handler, Acc, Stack, Config) ->
e(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
exp(Rest, Handler, acc_seq(Acc, S), Stack, Config);
e(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?positive; S =:= ?negative ->
ex(Rest, Handler, acc_seq(Acc, S), Stack, Config);
e(<<Sign, Rest/binary>>, Handler, Acc, Stack, Config) when Sign =:= ?positive; Sign =:= ?negative ->
ex(Rest, Handler, acc_seq(Acc, Sign), Stack, Config);
e(<<>>, Handler, [$e|Acc], Stack, Config) ->
?incomplete(decimal, <<$e>>, Handler, Acc, Stack, Config);
e(Bin, Handler, Acc, Stack, Config) ->
@ -745,8 +748,8 @@ ex(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzer
exp(Rest, Handler, acc_seq(Acc, S), Stack, Config);
ex(<<>>, Handler, [S, $e|Acc], Stack, Config) ->
?incomplete(decimal, <<$e, S/utf8>>, Handler, Acc, Stack, Config);
ex(Bin, Handler, Acc, Stack, Config) ->
?error(decimal, <<$e, Bin/binary>>, Handler, Acc, Stack, Config).
ex(Bin, Handler, [S, $e|Acc], Stack, Config) ->
?error(decimal, <<$e, S, Bin/binary>>, Handler, Acc, Stack, Config).
exp(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
@ -777,10 +780,12 @@ finish_number(<<>>, Handler, {NumType, Acc}, Stack, Config) ->
end;
finish_number(Bin, Handler, {NumType, Acc}, Stack, Config) ->
case NumType of
zero -> ?error(zero, <<>>, Handler, Acc, Stack, Config);
integer -> ?error(integer, <<>>, Handler, Acc, Stack, Config);
decimal -> ?error(decimal, <<>>, Handler, Acc, Stack, Config);
exp -> ?error(exp, <<>>, Handler, Acc, Stack, Config)
integer -> ?error(integer, Bin, Handler, Acc, Stack, Config);
decimal -> ?error(decimal, Bin, Handler, Acc, Stack, Config);
exp -> ?error(exp, Bin, Handler, Acc, Stack, Config);
zero ->
[$0|OldAcc] = Acc,
?error(value, <<$0, Bin/binary>>, Handler, OldAcc, Stack, Config)
end.
@ -1960,5 +1965,100 @@ error_test_() ->
].
custom_error_handler_test_() ->
Decode = fun(JSON, Config) -> start(JSON, {jsx, []}, [], jsx_utils:parse_config(Config)) end,
Error = fun(State, Rest, _Handler, _Acc, _Stack, _Config) -> {State, Rest} end,
[
{"maybe_bom error", ?_assertEqual(
{start, <<16#ef, 0>>},
Decode(<<16#ef, 0>>, [{error_handler, Error}])
)},
{"definitely_bom error", ?_assertEqual(
{start, <<16#ef, 16#bb, 0>>},
Decode(<<16#ef, 16#bb, 0>>, [{error_handler, Error}])
)},
{"value error", ?_assertEqual(
{value, <<0>>},
Decode(<<0>>, [{error_handler, Error}])
)},
{"object error", ?_assertEqual(
{object, <<0>>},
Decode(<<"{"/utf8, 0>>, [{error_handler, Error}])
)},
{"colon error", ?_assertEqual(
{colon, <<0>>},
Decode(<<"{\"\""/utf8, 0>>, [{error_handler, Error}])
)},
{"key error", ?_assertEqual(
{key, <<0>>},
Decode(<<"{\"\":1,"/utf8, 0>>, [{error_handler, Error}])
)},
{"negative error", ?_assertEqual(
{value, <<"-"/utf8, 0>>},
Decode(<<"-"/utf8, 0>>, [{error_handler, Error}])
)},
{"zero error", ?_assertEqual(
{value, <<"0"/utf8, 0>>},
Decode(<<"0"/utf8, 0>>, [explicit_end, {error_handler, Error}])
)},
{"integer error", ?_assertEqual(
{integer, <<0>>},
Decode(<<"1"/utf8, 0>>, [explicit_end, {error_handler, Error}])
)},
{"decimal error", ?_assertEqual(
{decimal, <<0>>},
Decode(<<"1.0"/utf8, 0>>, [explicit_end, {error_handler, Error}])
)},
{"exp error", ?_assertEqual(
{exp, <<0>>},
Decode(<<"1.0e1"/utf8, 0>>, [explicit_end, {error_handler, Error}])
)},
{"e error", ?_assertEqual(
{decimal, <<$e, 0>>},
Decode(<<"1e"/utf8, 0>>, [{error_handler, Error}])
)},
{"ex error", ?_assertEqual(
{decimal, <<$e, ?positive, 0>>},
Decode(<<"1e+"/utf8, 0>>, [{error_handler, Error}])
)},
{"exp error", ?_assertEqual(
{decimal, <<$e>>},
Decode(<<"1.e"/utf8>>, [{error_handler, Error}])
)},
{"true error", ?_assertEqual(
{true, <<"ru"/utf8, 0>>},
Decode(<<"tru"/utf8, 0>>, [{error_handler, Error}])
)},
{"false error", ?_assertEqual(
{false, <<"als"/utf8, 0>>},
Decode(<<"fals"/utf8, 0>>, [{error_handler, Error}])
)},
{"null error", ?_assertEqual(
{null, <<"ul"/utf8, 0>>},
Decode(<<"nul"/utf8, 0>>, [{error_handler, Error}])
)},
{"maybe_done error", ?_assertEqual(
{maybe_done, <<0>>},
Decode(<<"[[]"/utf8, 0>>, [{error_handler, Error}])
)},
{"done error", ?_assertEqual(
{done, <<0>>},
Decode(<<"[]"/utf8, 0>>, [{error_handler, Error}])
)},
{"comment error", ?_assertEqual(
{comment, <<" ]"/utf8>>},
Decode(<<"[ / ]">>, [{error_handler, Error}, comments])
)},
{"single_comment error", ?_assertEqual(
{comment, <<"/"/utf8, 192>>},
Decode(<<"[ //"/utf8, 192>>, [{error_handler, Error}, comments])
)},
{"multi_comment error", ?_assertEqual(
{comment, <<"*"/utf8, 192>>},
Decode(<<"[ /*"/utf8, 192>>, [{error_handler, Error}, comments])
)}
].
-endif.

View file

@ -28,6 +28,10 @@
-export([json_escape_sequence/1]).
-export([clean_string/2]).
-ifdef(TEST).
-export([fake_error_handler/6]).
-endif.
-include("jsx_config.hrl").
@ -67,6 +71,11 @@ parse_config([{pre_encode, Encoder}|Rest] = Options, Config) when is_function(En
false -> parse_config(Rest, Config#config{pre_encode=Encoder})
; _ -> erlang:error(badarg, [Options, Config])
end;
parse_config([{error_handler, ErrorHandler}|Rest] = Options, Config) when is_function(ErrorHandler, 6) ->
case Config#config.error_handler of
false -> parse_config(Rest, Config#config{error_handler=ErrorHandler})
; _ -> erlang:error(badarg, [Options, Config])
end;
%% deprecated flags
parse_config([{pre_encoder, Encoder}|Rest] = Options, Config) when is_function(Encoder, 1) ->
case Config#config.pre_encode of
@ -102,6 +111,7 @@ valid_flags() ->
explicit_end,
relax,
pre_encode,
error_handler,
%% deprecated flags
pre_encoder, %% pre_encode
loose_unicode, %% replaced_bad_utf8
@ -610,8 +620,22 @@ config_test_() ->
{pre_encode, fun(_) -> false end}
])
)},
{"error_handler flag", ?_assertEqual(
#config{error_handler=fun ?MODULE:fake_error_handler/6},
parse_config([{error_handler, fun ?MODULE:fake_error_handler/6}])
)},
{"two error_handlers defined", ?_assertError(
badarg,
parse_config([
{error_handler, fun(_) -> true end},
{error_handler, fun(_) -> false end}
])
)},
{"bad option flag", ?_assertError(badarg, parse_config([error]))}
].
fake_error_handler(_, _, _, _, _, _) -> ok.
%% erlang refuses to encode certain codepoints, so fake them