Current section
Files
Jump to
Current section
Files
src/kraft_jsonrpc.erl
-module(kraft_jsonrpc).
% API
-export([decode/1]).
-export([encode/1]).
-export([format_error/1]).
%--- Types ---------------------------------------------------------------------
-type method() :: atom() | binary().
-type params() :: [any()] | map().
-type id() :: undefined | any().
-type result() :: any().
-type code() :: integer().
-type error_message() :: binary().
-type data() :: any().
-type message() ::
{call, method(), params(), id()}
| {notification, method(), params()}
| {result, result(), id()}
| {error, code(), error_message(), data(), id()}.
-export_type([message/0]).
%--- Macros --------------------------------------------------------------------
-define(V, jsonrpc => <<"2.0">>).
-define(is_valid(Message),
(map_get(jsonrpc, Message) == <<"2.0">>)
).
-define(is_method(Method),
(is_atom(Method) orelse is_binary(Method))
).
-define(is_message(Method),
(is_atom(Method) orelse is_binary(Method))
).
-define(is_params(Params),
(is_map(Params) orelse is_list(Params))
).
%--- API ----------------------------------------------------------------------
decode(Binary) ->
case decode_json(Binary) of
{ok, []} ->
{single, {internal_error, invalid_request, null}};
{ok, Messages} when is_list(Messages) ->
{batch, [unpack(M) || M <- Messages]};
{ok, Message} when is_map(Message) ->
{single, unpack(Message)};
{ok, _Other} ->
{single, {internal_error, invalid_request, id(_Other)}};
{error, parse_error} ->
{single, {internal_error, parse_error, null}}
end.
encode(Messages) when is_list(Messages) ->
encode_json([pack(M) || M <- Messages]);
encode(Message) ->
encode_json(pack(Message)).
% encode({batch, Messages}) ->
% encode_json([pack(M) || M <- Messages]);
% encode({single, Message}) ->
% encode_json(pack(Message)).
format_error({internal_error, parse_error, ID}) ->
{error, -32700, <<"Parse error">>, undefined, ID};
format_error({internal_error, invalid_request, ID}) ->
{error, -32600, <<"Invalid Request">>, undefined, ID};
format_error({internal_error, method_not_found, ID}) ->
{error, -32601, <<"Method not found">>, undefined, ID};
format_error({internal_error, invalid_params, ID}) ->
{error, -32602, <<"Invalid params">>, undefined, ID};
format_error({internal_error, internal_error, ID}) ->
{error, -32603, <<"Internal error">>, undefined, ID}.
%--- Internal -----------------------------------------------------------------
decode_json(Binary) ->
try
{ok, jsone:decode(Binary, [{keys, attempt_atom}])}
catch
error:badarg:ST ->
case ST of
[Call | _] when element(1, Call) == jsone_decode ->
{error, parse_error};
_ ->
erlang:raise(error, badarg, ST)
end
end.
unpack(#{method := Method, params := Params, id := ID} = M) when
?is_valid(M), ?is_method(Method), ?is_params(Params)
->
{call, Method, Params, ID};
unpack(#{method := Method, id := ID} = M) when
?is_valid(M), ?is_method(Method)
->
{call, Method, undefined, ID};
unpack(#{method := Method, params := Params} = M) when
?is_valid(M), ?is_method(Method), ?is_params(Params)
->
{notification, Method, Params};
unpack(#{method := Method} = M) when
?is_valid(M), ?is_method(Method)
->
{notification, Method, undefined};
unpack(#{method := Method, params := _Params, id := ID} = M) when
?is_valid(M), ?is_method(Method)
->
{internal_error, invalid_params, ID};
unpack(#{result := Result, id := ID} = M) when
?is_valid(M)
->
{result, Result, ID};
unpack(
#{error := #{code := Code, message := Message, data := Data}, id := ID} = M
) when
?is_valid(M)
->
{error, Code, Message, Data, ID};
unpack(#{error := #{code := Code, message := Message}, id := ID} = M) when
?is_valid(M)
->
{error, Code, Message, undefined, ID};
unpack(M) ->
{internal_error, invalid_request, id(M)}.
pack({call, Method, undefined, ID}) ->
#{?V, method => Method, id => ID};
pack({call, Method, Params, ID}) ->
#{?V, method => Method, params => Params, id => ID};
pack({notification, Method, undefined}) ->
#{?V, method => Method};
pack({notification, Method, Params}) ->
#{?V, method => Method, params => Params};
pack({result, Result, ID}) ->
#{?V, result => Result, id => ID};
pack({error, Code, Message, undefined, undefined}) ->
#{?V, error => #{code => Code, message => Message}, id => null};
pack({error, Code, Message, undefined, ID}) ->
#{?V, error => #{code => Code, message => Message}, id => ID};
pack({error, Code, Message, Data, undefined}) ->
#{
?V,
error => #{code => Code, message => Message, data => Data, id => null}
};
pack({error, Code, Message, Data, ID}) ->
#{?V, error => #{code => Code, message => Message, data => Data}, id => ID}.
encode_json(JSON) -> jsone:encode(JSON).
id(Object) when is_map(Object) -> maps:get(id, Object, undefined);
id(_Object) -> undefined.