Current section
Files
Jump to
Current section
Files
src/encode.erl
%% @private
-module(encode).
-include_lib("eunit/include/eunit.hrl").
-export(['_encode'/1]).
-import(string, [concat/2]).
-spec start_encode(Encode, List) -> RetList | {error, Reason} when
Encode :: term(),
List :: list(),
RetList :: list(),
Reason :: atom().
start_encode(Encode, List) when is_map(Encode) -> encode_map(Encode, List);
start_encode(Encode, List) when is_list(Encode) -> determ_list_or_string(Encode, List);
start_encode(Encode, List) when is_number(Encode) -> encode_int(Encode, List);
start_encode(Encode, List) when is_tuple(Encode) -> encode_tuple(Encode, List);
start_encode(Encode, List) when is_atom(Encode) -> encode_atom(Encode, List);
start_encode(_,_) -> {error, badargs}.
-spec determ_list_or_string(Encode, List) -> RetList when
Encode :: list(),
List :: list(),
RetList :: list().
determ_list_or_string([], List) -> encode_list([], List);
determ_list_or_string(Encode, List) ->
Bool = is_printable_string(Encode),
if Bool ->
encode_string(Encode, List);
true ->
encode_list(Encode, List)
end.
-spec is_printable_string(String) -> Bool when
String :: string(),
Bool :: boolean().
is_printable_string([]) -> true;
is_printable_string([H | T]) when is_integer(H), H >= 32, H =< 126 ->
is_printable_string(T);
is_printable_string(_) -> false.
-spec encode_list(Encode, List) -> {ok, RetList} when
Encode :: list(),
List :: list(),
RetList :: list().
encode_list(Encode, _) when not(is_list(Encode)) -> {error, not_list};
encode_list(Encode, List) ->
{ok, TempHead} = iter_list(Encode, concat(List, "l")),
{ok, concat(TempHead, "e")}.
-spec iter_list(Encode, List) -> {ok, RetList} when
Encode :: list(),
List :: list(),
RetList :: list().
iter_list([], List) -> {ok, List};
iter_list([H | T], List) ->
{ok, NewHead} = start_encode(H, List),
iter_list(T, NewHead).
-spec encode_map(Encode, List) -> {ok, RetList} when
Encode :: map(),
List :: list(),
RetList :: list().
encode_map(Encode, _) when not(is_map(Encode)) -> {error, not_map};
encode_map(Encode, List) ->
ListOfKeys = maps:keys(Encode),
{ok, TempHead} = iter_map(Encode, concat(List, "d"), ListOfKeys),
{ok, concat(TempHead, "e")}.
-spec iter_map(Encode, List, Keys) -> {ok, RetList} when
Encode :: map(),
List :: list(),
Keys :: list(),
RetList :: list().
iter_map(_, List, []) -> {ok, List};
iter_map(Encode, List, [Key | Tail]) ->
{ok, TempHead} = start_encode(Key, List),
{ok, NewHead} = start_encode(maps:get(Key, Encode), TempHead),
iter_map(Encode, NewHead, Tail).
-spec encode_int(Encode, List) -> {ok, RetList} when
Encode :: integer(),
List :: list(),
RetList :: list().
encode_int(Encode, _) when not(is_integer(Encode)) -> {error, not_number};
encode_int(Encode, List) ->
IntList = integer_to_list(Encode),
{ok, concat(List, concat(concat("i", IntList), "e"))}.
-spec encode_string(Encode, List) -> {ok, RetList} when
Encode :: list(),
List :: list(),
RetList :: list().
encode_string([], _) -> {error, zero_length};
encode_string(Encode, _) when not(is_list(Encode)) -> {error, not_list};
encode_string(Encode, List) ->
Len = string:length(Encode),
{ok, concat(List, concat(concat(integer_to_list(Len), ":"), Encode))}.
-spec encode_tuple(Encode, List) -> {ok, RetList} | {error, Reason} when
Encode :: tuple() | list(),
List :: list(),
RetList :: list(),
Reason :: term().
encode_tuple(Encode, List) when is_tuple(Encode) -> encode_tuple(tuple_to_list(Encode), concat(List, "t"));
encode_tuple([], List) -> {ok, concat(List, "e")};
encode_tuple([Term | Encode], List) ->
case start_encode(Term, []) of
{ok, Result} ->
encode_tuple(Encode, concat(List, Result));
Err = {error, _} ->
Err
end.
-spec encode_atom(Encode, List) -> {ok, RetList} when
Encode :: atom(),
List :: list(),
RetList :: list().
encode_atom(Encode, List) ->
{ok, Result} = encode_string(atom_to_list(Encode), []),
{ok, concat(List, concat("a", Result))}.
-spec '_encode'(Encode) -> {ok, Value} | {error, Reason} when
Encode :: term(),
Value :: list(),
Reason :: atom().
'_encode'(Encode) ->
Value = start_encode(Encode, []),
case Value of
{error, _Reason} ->
Value;
Value ->
Value
end.
%% ----------------------------------
%% Unit tests
%% ----------------------------------
start_encode_test() ->
?assert(start_encode(["test", 23, "warden", #{"clyffe" => 43}], []) =:= {ok, "l4:testi23e6:wardend6:clyffei43eee"}),
?assert(start_encode(atom, []) =:= {ok, "a4:atom"}),
?assert(start_encode("warden",[]) =:= {ok, "6:warden"}),
?assert(start_encode(23, []) =:= {ok, "i23e"}),
?assert(start_encode(#{}, []) =:= {ok, "de"}).
determ_list_or_string_test() ->
?assert(determ_list_or_string("Warden", []) =:= {ok, "6:Warden"}),
?assert(determ_list_or_string(["Warden"], []) =:= {ok, "l6:Wardene"}),
?assert(determ_list_or_string(23, []) =:= {error, not_list}),
?assert(determ_list_or_string("", []) =:= {ok, "le"}).
encode_list_test() ->
?assert(encode_list([23,43,54], []) =:= {ok, "li23ei43ei54ee"}),
?assert(encode_list([],[]) =:= {ok, "le"}),
?assert(encode_list(#{23=> 32}, []) =:= {error, not_list}).
encode_map_test() ->
?assert(encode_map(#{}, []) =:= {ok, "de"}),
?assert(encode_map(#{23 => "23"}, []) =:= {ok, "di23e2:23e"}),
?assert(encode_map("Warden", []) =:= {error, not_map}).
encode_int_test() ->
?assert(encode_int(23, []) =:= {ok, "i23e"}),
?assert(encode_int("23", []) =:= {error, not_number}),
?assert(encode_int(3125452313, "l3:hej") =:= {ok, "l3:heji3125452313e"}).
encode_string_test() ->
?assert(encode_string("hej", []) =:= {ok, "3:hej"}),
?assert(encode_string("", []) =:= {error, zero_length}),
?assert(encode_string("23", []) =:= {ok, "2:23"}),
?assert(encode_string(#{}, []) =:= {error, not_list}).