Current section

Files

Jump to
pprint src pprint_ffi.erl
Raw

src/pprint_ffi.erl

-module(pprint_ffi).
-export([decode_nil/1, decode_tuple/1, decode_custom_type/1]).
-define(is_digit_char(X), (X > 47 andalso X < 58)).
-define(is_lowercase_char(X), (X > 96 andalso X < 123)).
-define(is_underscore_char(X), (X == 95)).
decode_nil(X) ->
case X of
nil -> {ok, nil};
_ -> decode_error("Nil", X)
end.
decode_tuple(X) ->
case X of
Tuple when is_tuple(Tuple) -> {ok, tuple_to_list(Tuple)};
_ -> decode_error("Tuple", X)
end.
decode_custom_type(X) ->
case X of
% Variants with no fields are encoded as a single atom named as the variant.
Atom when is_atom(Atom) ->
case inspect_maybe_gleam_atom(erlang:atom_to_binary(Atom), none, <<>>) of
{ok, AtomName} -> {ok, {t_custom, AtomName, []}};
{error, nil} -> decode_error("CustomType", X)
end;
% Variants with fields are encoded as tuples where the first items is an
% atom with the variant's name.
Tuple when is_tuple(Tuple) ->
case tuple_to_list(Tuple) of
[Atom | Elements] when is_atom(Atom) ->
case inspect_maybe_gleam_atom(erlang:atom_to_binary(Atom), none, <<>>) of
{ok, AtomName} -> {ok, {t_custom, AtomName, lists:map(fun(E) -> {positional, E} end, Elements)}};
{error, nil} -> decode_error("CustomType", X)
end;
_ -> decode_error("CustomType", X)
end;
_ -> decode_error("CustomType", X)
end.
decode_error(Expected, Got) ->
ExpectedString = list_to_binary(Expected),
GotString = gleam_stdlib:classify_dynamic(Got),
DecodeError = {decode_error, ExpectedString, GotString, []},
{error, [DecodeError]}.
% This is copy pasted from gleam's stdlib and performs some additional checks to
% make sure the given atom is a gleam's custom type atom. Stdlib doesn't export
% it so I had to copy it.
% It returns `{ok, CamelCaseAtomName}` in case it really is a valid Gleam atom
% name.
inspect_maybe_gleam_atom(<<>>, none, _) ->
{error, nil};
inspect_maybe_gleam_atom(<<First, _Rest/binary>>, none, _) when ?is_digit_char(First) ->
{error, nil};
inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) ->
{error, nil};
inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) ->
{error, nil};
inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) ->
{error, nil};
inspect_maybe_gleam_atom(<<First, _Rest/binary>>, _PrevChar, _Acc)
when not (?is_lowercase_char(First) orelse ?is_underscore_char(First) orelse ?is_digit_char(First)) ->
{error, nil};
inspect_maybe_gleam_atom(<<First, Rest/binary>>, none, Acc) ->
inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, (uppercase(First))>>);
inspect_maybe_gleam_atom(<<"_", Rest/binary>>, _PrevChar, Acc) ->
inspect_maybe_gleam_atom(Rest, $_, Acc);
inspect_maybe_gleam_atom(<<First, Rest/binary>>, $_, Acc) ->
inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, (uppercase(First))>>);
inspect_maybe_gleam_atom(<<First, Rest/binary>>, _PrevChar, Acc) ->
inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, First>>);
inspect_maybe_gleam_atom(<<>>, _PrevChar, Acc) ->
{ok, Acc};
inspect_maybe_gleam_atom(_, _, _) ->
{error, nil}.
uppercase(X) -> X - 32.