Current section
Files
Jump to
Current section
Files
src/mochi@output.erl
-module(mochi@output).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/mochi/output.gleam").
-export([from_dynamic/1]).
-export_type([value/0, encode_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Internal typed JSON value used by the encoder.\n"
"\n"
" Resolvers and field extractors hand `Dynamic` to the encoder. Rather than\n"
" brute-forcing the runtime shape with a cascade of decoders on every node,\n"
" we classify once with `dynamic.classify` and convert into this tree. The\n"
" JSON encoder then walks the tree directly — no re-classification, no\n"
" re-parsing of its own output for pretty printing, no silent fallbacks\n"
" when something doesn't fit.\n"
).
-type value() :: {v_bool, boolean()} |
{v_int, integer()} |
{v_float, float()} |
{v_string, binary()} |
v_null |
{v_list, list(value())} |
{v_object, list({binary(), value()})}.
-type encode_error() :: {unsupported_value, binary(), binary()}.
-file("src/mochi/output.gleam", 112).
-spec int_to_str(integer()) -> binary().
int_to_str(I) ->
erlang:integer_to_binary(I).
-file("src/mochi/output.gleam", 73).
-spec walk_list(
list(gleam@dynamic:dynamic_()),
binary(),
integer(),
list(value())
) -> {ok, value()} | {error, encode_error()}.
walk_list(Items, Path, I, Acc) ->
case Items of
[] ->
{ok, {v_list, lists:reverse(Acc)}};
[X | Rest] ->
case walk(
X,
<<<<Path/binary, "/"/utf8>>/binary, (int_to_str(I))/binary>>
) of
{error, E} ->
{error, E};
{ok, V} ->
walk_list(Rest, Path, I + 1, [V | Acc])
end
end.
-file("src/mochi/output.gleam", 40).
-spec walk(gleam@dynamic:dynamic_(), binary()) -> {ok, value()} |
{error, encode_error()}.
walk(Value, Path) ->
case gleam_stdlib:classify_dynamic(Value) of
<<"Bool"/utf8>> ->
_pipe = gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_bool/1}
),
_pipe@1 = gleam@result:map(
_pipe,
fun(Field@0) -> {v_bool, Field@0} end
),
gleam@result:map_error(
_pipe@1,
fun(_) -> {unsupported_value, Path, <<"Bool"/utf8>>} end
);
<<"Int"/utf8>> ->
_pipe@2 = gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_int/1}
),
_pipe@3 = gleam@result:map(
_pipe@2,
fun(Field@0) -> {v_int, Field@0} end
),
gleam@result:map_error(
_pipe@3,
fun(_) -> {unsupported_value, Path, <<"Int"/utf8>>} end
);
<<"Float"/utf8>> ->
_pipe@4 = gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_float/1}
),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(Field@0) -> {v_float, Field@0} end
),
gleam@result:map_error(
_pipe@5,
fun(_) -> {unsupported_value, Path, <<"Float"/utf8>>} end
);
<<"String"/utf8>> ->
_pipe@6 = gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
_pipe@7 = gleam@result:map(
_pipe@6,
fun(Field@0) -> {v_string, Field@0} end
),
gleam@result:map_error(
_pipe@7,
fun(_) -> {unsupported_value, Path, <<"String"/utf8>>} end
);
<<"Nil"/utf8>> ->
{ok, v_null};
<<"List"/utf8>> ->
case gleam@dynamic@decode:run(
Value,
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
) of
{error, _} ->
{error, {unsupported_value, Path, <<"List"/utf8>>}};
{ok, Items} ->
walk_list(Items, Path, 0, [])
end;
<<"Dict"/utf8>> ->
case gleam@dynamic@decode:run(
Value,
gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_string/1},
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
) of
{error, _} ->
{error, {unsupported_value, Path, <<"Dict"/utf8>>}};
{ok, D} ->
walk_object(D, Path)
end;
Other ->
{error, {unsupported_value, Path, Other}}
end.
-file("src/mochi/output.gleam", 36).
?DOC(
" Convert a `Dynamic` into the typed value tree, surfacing unsupported\n"
" shapes as errors instead of silently dropping them.\n"
).
-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, value()} |
{error, encode_error()}.
from_dynamic(Value) ->
walk(Value, <<""/utf8>>).
-file("src/mochi/output.gleam", 97).
-spec walk_pairs(
list({binary(), gleam@dynamic:dynamic_()}),
binary(),
list({binary(), value()})
) -> {ok, value()} | {error, encode_error()}.
walk_pairs(Pairs, Path, Acc) ->
case Pairs of
[] ->
{ok, {v_object, lists:reverse(Acc)}};
[{K, V} | Rest] ->
case walk(V, <<<<Path/binary, "/"/utf8>>/binary, K/binary>>) of
{error, E} ->
{error, E};
{ok, Value} ->
walk_pairs(Rest, Path, [{K, Value} | Acc])
end
end.
-file("src/mochi/output.gleam", 89).
-spec walk_object(gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), binary()) -> {ok,
value()} |
{error, encode_error()}.
walk_object(D, Path) ->
Pairs = maps:to_list(D),
walk_pairs(Pairs, Path, []).