Deal with dialyzer issues
We end up: 1. making the main interface (`jsx:`) more readable and easier to maintain (this allows for easier future dropping of the use of `#config{}` for options), 2. fixing some config.-related `-spec(_).`s that weren't using proper elements, 3. having `any()` become a "specific type" - which seems like an improvement, 4. moving stuff like `list()` or `proplists:proplist()` into more appropriate types, 5. making the main config. element (`jsx_config`) "pretty" visible, 6. creating a visible distinction between a module's config. and the generic `jsx_config` elements
This commit is contained in:
parent
1e88fc5455
commit
3a8f364153
10 changed files with 106 additions and 52 deletions
15
rebar.config
15
rebar.config
|
@ -1,2 +1,17 @@
|
|||
{edoc_opts, [{preprocess, true}]}.
|
||||
{erl_opts, [debug_info]}.
|
||||
{dialyzer, [
|
||||
{warnings, [
|
||||
unknown,
|
||||
unmatched_returns,
|
||||
error_handling,
|
||||
underspecs
|
||||
]}
|
||||
]}.
|
||||
{profiles, [
|
||||
{test, [
|
||||
{dialyzer, [
|
||||
{plt_extra_apps, [eunit]}
|
||||
]}
|
||||
]}
|
||||
]}.
|
||||
|
|
32
src/jsx.erl
32
src/jsx.erl
|
@ -54,20 +54,20 @@
|
|||
|
||||
-type config() :: jsx_config:config().
|
||||
|
||||
-spec encode(Source::json_term()) -> json_text().
|
||||
-spec encode(Source::json_term()) -> json_text() | {incomplete, encoder()}.
|
||||
|
||||
encode(Source) -> encode(Source, []).
|
||||
|
||||
-spec encode(Source::json_term(), Config::jsx_to_json:config()) -> json_text() | {incomplete, encoder()}.
|
||||
-spec encode(Source::json_term(), Config::jsx_config:options()) -> json_text() | {incomplete, encoder()}.
|
||||
|
||||
encode(Source, Config) -> jsx_to_json:to_json(Source, Config).
|
||||
|
||||
|
||||
-spec decode(Source::json_text()) -> json_term().
|
||||
-spec decode(Source::json_text()) -> json_term() | {incomplete, decoder()}.
|
||||
|
||||
decode(Source) -> decode(Source, []).
|
||||
|
||||
-spec decode(Source::json_text(), Config::jsx_to_term:config()) -> json_term() | {incomplete, decoder()}.
|
||||
-spec decode(Source::json_text(), Config::jsx_config:options()) -> json_term() | {incomplete, decoder()}.
|
||||
|
||||
decode(Source, Config) -> jsx_to_term:to_term(Source, Config).
|
||||
|
||||
|
@ -76,7 +76,7 @@ decode(Source, Config) -> jsx_to_term:to_term(Source, Config).
|
|||
|
||||
format(Source) -> format(Source, []).
|
||||
|
||||
-spec format(Source::json_text(), Config::jsx_to_json:config()) -> json_text() | {incomplete, decoder()}.
|
||||
-spec format(Source::json_text(), Config::jsx_config:options()) -> json_text().
|
||||
|
||||
format(Source, Config) -> jsx_to_json:format(Source, Config).
|
||||
|
||||
|
@ -91,43 +91,44 @@ minify(Source) -> format(Source, []).
|
|||
prettify(Source) -> format(Source, [space, {indent, 2}]).
|
||||
|
||||
|
||||
-spec is_json(Source::any()) -> boolean().
|
||||
-spec is_json(Source::binary()) -> boolean() | {incomplete, decoder()}.
|
||||
|
||||
is_json(Source) -> is_json(Source, []).
|
||||
|
||||
-spec is_json(Source::any(), Config::jsx_verify:config()) -> boolean() | {incomplete, decoder()}.
|
||||
-spec is_json(Source::binary(), Config::jsx_config:options()) -> boolean() | {incomplete, decoder()}.
|
||||
|
||||
is_json(Source, Config) -> jsx_verify:is_json(Source, Config).
|
||||
|
||||
|
||||
-spec is_term(Source::any()) -> boolean().
|
||||
-spec is_term(Source::json_term() | end_stream | end_json) -> boolean() | {incomplete, encoder()}.
|
||||
|
||||
is_term(Source) -> is_term(Source, []).
|
||||
|
||||
-spec is_term(Source::any(), Config::jsx_verify:config()) -> boolean() | {incomplete, encoder()}.
|
||||
-spec is_term(Source::json_term() | end_stream | end_json,
|
||||
Config::jsx_config:options()) -> boolean() | {incomplete, encoder()}.
|
||||
|
||||
is_term(Source, Config) -> jsx_verify:is_term(Source, Config).
|
||||
|
||||
|
||||
-spec consult(File::file:name_all()) -> list(json_term()).
|
||||
-spec consult(File::file:name_all()) -> list(jsx_consult:json_value()).
|
||||
|
||||
consult(File) -> consult(File, []).
|
||||
|
||||
-spec consult(File::file:name_all(), Config::jsx_to_term:config()) -> list(json_term()).
|
||||
-spec consult(File::file:name_all(), Config::jsx_consult:config()) -> list(jsx_consult:json_value()).
|
||||
|
||||
consult(File, Config) -> jsx_consult:consult(File, Config).
|
||||
|
||||
|
||||
-type decoder() :: fun((json_text() | end_stream | end_json) -> any()).
|
||||
|
||||
-spec decoder(Handler::module(), State::any(), Config::list()) -> decoder().
|
||||
-spec decoder(Handler::module(), State::any(), Config::jsx_config:options()) -> decoder().
|
||||
|
||||
decoder(Handler, State, Config) -> jsx_decoder:decoder(Handler, State, Config).
|
||||
|
||||
|
||||
-type encoder() :: fun((json_term() | end_stream | end_json) -> any()).
|
||||
|
||||
-spec encoder(Handler::module(), State::any(), Config::list()) -> encoder().
|
||||
-spec encoder(Handler::module(), State::any(), Config::jsx_config:options()) -> encoder().
|
||||
|
||||
encoder(Handler, State, Config) -> jsx_encoder:encoder(Handler, State, Config).
|
||||
|
||||
|
@ -156,13 +157,14 @@ encoder(Handler, State, Config) -> jsx_encoder:encoder(Handler, State, Config).
|
|||
|
||||
-type parser() :: fun((token() | end_stream) -> any()).
|
||||
|
||||
-spec parser(Handler::module(), State::any(), Config::list()) -> parser().
|
||||
-spec parser(Handler::module(), State::any(), Config::jsx_config:options()) -> parser().
|
||||
|
||||
parser(Handler, State, Config) -> jsx_parser:parser(Handler, State, Config).
|
||||
|
||||
-opaque internal_state() :: tuple().
|
||||
|
||||
-spec resume(Term::json_text() | token(), InternalState::internal_state(), Config::list()) -> any().
|
||||
-spec resume(Term::json_text() | token(), InternalState::internal_state(),
|
||||
Config::jsx_config:options()) -> jsx:decoder() | {incomplete, jsx:decoder()}.
|
||||
|
||||
resume(Term, {decoder, State, Handler, Acc, Stack}, Config) ->
|
||||
jsx_decoder:resume(Term, State, Handler, Acc, Stack, jsx_config:parse_config(Config));
|
||||
|
|
|
@ -49,8 +49,55 @@
|
|||
-type config() :: #config{}.
|
||||
-export_type([config/0]).
|
||||
|
||||
-type option() :: valid_flag()
|
||||
| {valid_flag(), boolean()}
|
||||
| {strict, [strict_option()]}
|
||||
| {error_handler, fun((any(), any(), any()) -> ok)}
|
||||
| {incomplete_handler, fun((any(), any(), any()) -> ok)}
|
||||
| {return_maps, boolean()}
|
||||
| {labels, label_option()}
|
||||
| {space, non_neg_integer()}
|
||||
| {indent, non_neg_integer()}
|
||||
| {depth, non_neg_integer()}
|
||||
| {newline, binary()}
|
||||
| legacy_option()
|
||||
| {legacy_option(), boolean()}.
|
||||
-type legacy_option() :: strict_comments
|
||||
| strict_commas
|
||||
| strict_utf8
|
||||
| strict_single_quotes
|
||||
| strict_escapes
|
||||
| strict_control_codes.
|
||||
|
||||
-type options() :: [option()].
|
||||
-export_type([options/0]).
|
||||
|
||||
-type strict_option() :: comments
|
||||
| trailing_commas
|
||||
| utf8
|
||||
| single_quotes
|
||||
| escapes
|
||||
| control_codes.
|
||||
-type label_option() :: binary
|
||||
| atom
|
||||
| existing_atom
|
||||
| attempt_atom.
|
||||
|
||||
-type valid_flag() :: escaped_forward_slashes
|
||||
| escaped_strings
|
||||
| unescaped_jsonp
|
||||
| dirty_strings
|
||||
| multi_term
|
||||
| return_tail
|
||||
| repeat_keys
|
||||
| strict
|
||||
| stream
|
||||
| uescape
|
||||
| error_handler
|
||||
| incomplete_handler.
|
||||
|
||||
%% parsing of jsx config
|
||||
-spec parse_config(Config::proplists:proplist()) -> config().
|
||||
-spec parse_config(Config::options()) -> config().
|
||||
|
||||
parse_config(Config) -> parse_config(Config, #config{}).
|
||||
|
||||
|
@ -116,7 +163,7 @@ parse_strict(_Strict, _Rest, _Config) ->
|
|||
|
||||
|
||||
|
||||
-spec config_to_list(Config::config()) -> proplists:proplist().
|
||||
-spec config_to_list(Config::config()) -> options().
|
||||
|
||||
config_to_list(Config) ->
|
||||
reduce_config(lists:map(
|
||||
|
@ -153,7 +200,7 @@ reduce_config([Else|Input], Output, Strict) ->
|
|||
reduce_config(Input, [Else] ++ Output, Strict).
|
||||
|
||||
|
||||
-spec valid_flags() -> [atom()].
|
||||
-spec valid_flags() -> [valid_flag(), ...].
|
||||
|
||||
valid_flags() ->
|
||||
[
|
||||
|
@ -172,7 +219,7 @@ valid_flags() ->
|
|||
].
|
||||
|
||||
|
||||
-spec extract_config(Config::proplists:proplist()) -> proplists:proplist().
|
||||
-spec extract_config(Config::options()) -> options().
|
||||
|
||||
extract_config(Config) ->
|
||||
extract_parser_config(Config, []).
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
return_maps = false
|
||||
}).
|
||||
|
||||
-type config() :: list().
|
||||
-type config() :: proplists:proplist().
|
||||
-export_type([config/0]).
|
||||
|
||||
-type json_value() :: list(json_value())
|
||||
|
@ -43,6 +43,7 @@
|
|||
| integer()
|
||||
| float()
|
||||
| binary().
|
||||
-export_type([json_value/0]).
|
||||
|
||||
opts(Opts) -> [return_maps, multi_term] ++ Opts.
|
||||
|
||||
|
@ -61,8 +62,8 @@ consult(File, Config) when is_list(Config) ->
|
|||
end.
|
||||
|
||||
|
||||
-type state() :: {[], proplists:proplist(), {list(), #config{}}}.
|
||||
-spec init(Config::proplists:proplist()) -> state().
|
||||
-type state() :: {[], config(), {list(), #config{}}}.
|
||||
-spec init(Config::config()) -> state().
|
||||
|
||||
init(Config) -> {[], Config, jsx_to_term:start_term(Config)}.
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
-export([decoder/3, resume/6]).
|
||||
|
||||
|
||||
-spec decoder(Handler::module(), State::any(), Config::list()) -> jsx:decoder().
|
||||
-spec decoder(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:decoder().
|
||||
|
||||
decoder(Handler, State, Config) ->
|
||||
fun(JSON) -> start(JSON, {Handler, Handler:init(State)}, [], jsx_config:parse_config(Config)) end.
|
||||
|
|
|
@ -25,19 +25,19 @@
|
|||
|
||||
-export([encoder/3, encode/1, encode/2]).
|
||||
|
||||
-spec encoder(Handler::module(), State::any(), Config::list()) -> jsx:encoder().
|
||||
-spec encoder(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:encoder().
|
||||
|
||||
encoder(Handler, State, Config) ->
|
||||
Parser = jsx:parser(Handler, State, Config),
|
||||
fun(Term) -> Parser(encode(Term) ++ [end_json]) end.
|
||||
|
||||
|
||||
-spec encode(Term::any()) -> any().
|
||||
-spec encode(Term::any()) -> [any(), ...].
|
||||
|
||||
encode(Term) -> encode(Term, ?MODULE).
|
||||
|
||||
|
||||
-spec encode(Term::any(), EntryPoint::module()) -> any().
|
||||
-spec encode(Term::any(), EntryPoint::module()) -> [any(), ...].
|
||||
|
||||
encode(Map, _EntryPoint) when is_map(Map), map_size(Map) < 1 ->
|
||||
[start_object, end_object];
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
-export([init/1, handle_event/2]).
|
||||
|
||||
|
||||
-spec parser(Handler::module(), State::any(), Config::list()) -> jsx:parser().
|
||||
-spec parser(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:parser().
|
||||
|
||||
parser(Handler, State, Config) ->
|
||||
fun(Tokens) -> value(Tokens, {Handler, Handler:init(State)}, [], jsx_config:parse_config(Config)) end.
|
||||
|
@ -630,7 +630,7 @@ to_hex(X) -> X + 48. %% ascii "1" is [49], "2" is [50], etc...
|
|||
|
||||
|
||||
%% for raw input
|
||||
-spec init(proplists:proplist()) -> list().
|
||||
-spec init([]) -> [].
|
||||
|
||||
init([]) -> [].
|
||||
|
||||
|
|
|
@ -36,17 +36,16 @@
|
|||
newline = <<$\n>>
|
||||
}).
|
||||
|
||||
-type config() :: list().
|
||||
-export_type([config/0]).
|
||||
-type config() :: proplists:proplist().
|
||||
|
||||
|
||||
-spec to_json(Source::any(), Config::config()) -> binary().
|
||||
-spec to_json(Source::jsx:json_term(), Config::jsx_config:options()) -> binary().
|
||||
|
||||
to_json(Source, Config) when is_list(Config) ->
|
||||
(jsx:encoder(?MODULE, Config, jsx_config:extract_config(Config ++ [escaped_strings])))(Source).
|
||||
|
||||
|
||||
-spec format(Source::binary(), Config::config()) -> binary().
|
||||
-spec format(Source::binary(), Config::jsx_config:options()) -> jsx:json_text().
|
||||
|
||||
format(Source, Config) when is_binary(Source) andalso is_list(Config) ->
|
||||
(jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config ++ [escaped_strings])))(Source);
|
||||
|
@ -91,7 +90,7 @@ parse_config([], Config) ->
|
|||
|
||||
|
||||
-type state() :: {unicode:charlist(), #config{}}.
|
||||
-spec init(Config::proplists:proplist()) -> state().
|
||||
-spec init(Config::config()) -> state().
|
||||
|
||||
init(Config) -> {[], parse_config(Config)}.
|
||||
|
||||
|
@ -390,7 +389,7 @@ custom_newline_test_() ->
|
|||
[
|
||||
{"single key object", ?_assert(
|
||||
jsx:format(<<"{\"k\":\"v\"}">>, [space, {indent, 2}, {newline, <<$\r>>}])
|
||||
=:= <<"{\r \"k\": \"v\"\r}">>)
|
||||
=:= <<"{\r \"k\": \"v\"\r}">>)
|
||||
}
|
||||
].
|
||||
|
||||
|
|
|
@ -41,21 +41,9 @@
|
|||
return_maps = false
|
||||
}).
|
||||
|
||||
-type config() :: list().
|
||||
-export_type([config/0]).
|
||||
-type config() :: proplists:proplist().
|
||||
|
||||
-type json_value() :: list(json_value())
|
||||
| list({binary() | atom(), json_value()}) | [{},...]
|
||||
| {with_tail, json_value(), binary()}
|
||||
| map()
|
||||
| true
|
||||
| false
|
||||
| null
|
||||
| integer()
|
||||
| float()
|
||||
| binary().
|
||||
|
||||
-spec to_term(Source::binary(), Config::config()) -> json_value().
|
||||
-spec to_term(Source::binary(), Config::jsx_config:options()) -> jsx:json_term() | {incomplete, jsx:decoder()}.
|
||||
|
||||
to_term(Source, Config) when is_list(Config) ->
|
||||
(jsx:decoder(?MODULE, [return_maps] ++ Config, jsx_config:extract_config(Config)))(Source).
|
||||
|
@ -87,7 +75,7 @@ parse_config([], Config) ->
|
|||
|
||||
|
||||
-type state() :: {list(), #config{}}.
|
||||
-spec init(Config::proplists:proplist()) -> state().
|
||||
-spec init(Config::config()) -> state().
|
||||
|
||||
init(Config) -> start_term(Config).
|
||||
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
-export([is_json/2, is_term/2]).
|
||||
-export([init/1, handle_event/2]).
|
||||
|
||||
-type config() :: proplists:proplist().
|
||||
|
||||
-spec is_json(Source::binary(), Config::proplists:proplist()) -> true | false | {incomplete, jsx:decoder()}.
|
||||
-spec is_json(Source::binary(), Config::jsx_config:options()) -> true | false | {incomplete, jsx:decoder()}.
|
||||
|
||||
is_json(Source, Config) when is_list(Config) ->
|
||||
try (jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source)
|
||||
|
@ -35,7 +36,8 @@ is_json(Source, Config) when is_list(Config) ->
|
|||
end.
|
||||
|
||||
|
||||
-spec is_term(Source::any(), Config::proplists:proplist()) -> true | false | {incomplete, jsx:encoder()}.
|
||||
-spec is_term(Source::jsx:json_term() | end_stream | end_json,
|
||||
Config::jsx_config:options()) -> true | false | {incomplete, jsx:encoder()}.
|
||||
|
||||
is_term(Source, Config) when is_list(Config) ->
|
||||
try (jsx:encoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source)
|
||||
|
@ -68,7 +70,7 @@ parse_config([], Config) ->
|
|||
|
||||
%% we don't actually need any state for this
|
||||
-type state() :: [].
|
||||
-spec init(Config::proplists:proplist()) -> state().
|
||||
-spec init(Config::config()) -> state().
|
||||
|
||||
init(Config) -> parse_config(Config).
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue