Skip to main content

src/molt@internal@document@representation.erl

-module(molt@internal@document@representation).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/document/representation.gleam").
-export([set_representation/3]).

-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(false).

-file("src/molt/internal/document/representation.gleam", 379).
?DOC(false).
-spec has_comment({list(binary()), gleam@option:option(binary())}) -> boolean().
has_comment(Comments) ->
    {Leading, Trailing} = Comments,
    (Leading /= []) orelse (Trailing /= none).

-file("src/molt/internal/document/representation.gleam", 97).
?DOC(false).
-spec do_indices(integer(), list(integer())) -> list(integer()).
do_indices(I, Acc) ->
    gleam@bool:guard(I < 0, Acc, fun() -> do_indices(I - 1, [I | Acc]) end).

-file("src/molt/internal/document/representation.gleam", 93).
?DOC(false).
-spec indices_up_to(integer()) -> list(integer()).
indices_up_to(N) ->
    do_indices(N - 1, []).

-file("src/molt/internal/document/representation.gleam", 180).
?DOC(false).
-spec capture_entry_comments(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment()),
    integer()
) -> {ok, list({list(binary()), gleam@option:option(binary())})} |
    {error, molt@error:molt_error()}.
capture_entry_comments(Tree, Segments, Count) ->
    gleam@list:try_map(
        indices_up_to(Count),
        fun(I) ->
            Entry_path = lists:append(Segments, [{index_segment, I}]),
            gleam@result:'try'(
                molt@cst:leading_comments(Tree, Entry_path),
                fun(Leading) ->
                    gleam@result:'try'(
                        molt@cst:trailing_comment(Tree, Entry_path),
                        fun(Trailing) -> {ok, {Leading, Trailing}} end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/representation.gleam", 331).
?DOC(false).
-spec aot_to_inline_array(
    molt@types:document(),
    list(molt@types:path_segment()),
    integer()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
aot_to_inline_array(Doc, Segments, Count) ->
    Indices = indices_up_to(Count),
    gleam@result:'try'(
        gleam@list:try_map(
            Indices,
            fun(I) ->
                Entry_path = lists:append(Segments, [{index_segment, I}]),
                gleam@result:'try'(
                    molt@cst:get(erlang:element(4, Doc), Entry_path),
                    fun(Entry) ->
                        Kvs = molt@internal@cst@elements:get_kv_children(
                            erlang:element(3, Entry)
                        ),
                        {ok,
                            molt@internal@cst@elements:section_kvs_to_inline_table(
                                Kvs
                            )}
                    end
                )
            end
        ),
        fun(Inline_tables) ->
            gleam@result:'try'(
                capture_entry_comments(erlang:element(4, Doc), Segments, Count),
                fun(Entry_comments) ->
                    Array_node = case gleam@list:any(
                        Entry_comments,
                        fun has_comment/1
                    ) of
                        false ->
                            molt@internal@cst@elements:inline_tables_to_array(
                                Inline_tables
                            );

                        true ->
                            molt@internal@cst@elements:inline_tables_to_multiline_array(
                                Inline_tables,
                                Entry_comments
                            )
                    end,
                    {Parent, Last} = molt@internal@path:split_last_segment(
                        Segments
                    ),
                    Key = case Last of
                        {key_segment, K} ->
                            K;

                        {index_segment, _} ->
                            <<""/utf8>>
                    end,
                    Kv = molt@internal@cst@builder:build_kv_node(
                        Key,
                        {node_element, Array_node}
                    ),
                    gleam@result:'try'(
                        gleam@list:try_fold(
                            lists:reverse(Indices),
                            erlang:element(4, Doc),
                            fun(Tree, I@1) ->
                                molt@cst:delete(
                                    Tree,
                                    lists:append(
                                        Segments,
                                        [{index_segment, I@1}]
                                    )
                                )
                            end
                        ),
                        fun(Deleted) ->
                            gleam@result:'try'(
                                molt@cst:insert_kv(
                                    Deleted,
                                    Parent,
                                    Kv,
                                    kv_at_end
                                ),
                                fun(New_tree) ->
                                    {ok,
                                        molt@internal@document@primitives:rebuild(
                                            Doc,
                                            New_tree
                                        )}
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/representation.gleam", 201).
?DOC(false).
-spec block_table_to_inline(
    molt@types:document(),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
block_table_to_inline(Doc, Segments) ->
    gleam@result:'try'(
        molt@cst:get(erlang:element(4, Doc), Segments),
        fun(Table_node) ->
            Kvs = molt@internal@cst@elements:get_kv_children(
                erlang:element(3, Table_node)
            ),
            Inline = molt@internal@cst@elements:section_kvs_to_inline_table(Kvs),
            {Parent, Last} = molt@internal@path:split_last_segment(Segments),
            Key = case Last of
                {key_segment, K} ->
                    K;

                {index_segment, _} ->
                    <<""/utf8>>
            end,
            Base_kv = molt@internal@cst@builder:build_kv_node(
                Key,
                {node_element, Inline}
            ),
            Kv = begin
                _pipe = {node,
                    erlang:element(2, Base_kv),
                    erlang:element(3, Base_kv),
                    erlang:element(4, Table_node)},
                molt@internal@cst@builder:drop_leading_newlines(_pipe)
            end,
            gleam@result:'try'(
                molt@cst:delete(erlang:element(4, Doc), Segments),
                fun(Deleted) ->
                    gleam@result:'try'(
                        molt@cst:insert_kv(Deleted, Parent, Kv, kv_at_end),
                        fun(New_tree) ->
                            {ok,
                                molt@internal@document@primitives:rebuild(
                                    Doc,
                                    New_tree
                                )}
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/representation.gleam", 157).
?DOC(false).
-spec set_comments_if_any(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment()),
    list(binary()),
    gleam@option:option(binary())
) -> {ok, greenwood:node_(molt@types:toml_kind())} |
    {error, molt@error:molt_error()}.
set_comments_if_any(Tree, Segments, Leading, Trailing) ->
    gleam@result:'try'(case Leading of
            [] ->
                {ok, Tree};

            _ ->
                molt@cst:set_leading_comments(Tree, Segments, Leading)
        end, fun(With_leading) -> case Trailing of
                none ->
                    {ok, With_leading};

                {some, _} ->
                    molt@cst:set_trailing_comment(
                        With_leading,
                        Segments,
                        Trailing
                    )
            end end).

-file("src/molt/internal/document/representation.gleam", 317).
?DOC(false).
-spec build_aot_entry_from_inline(
    list(binary()),
    greenwood:node_(molt@types:toml_kind())
) -> greenwood:node_(molt@types:toml_kind()).
build_aot_entry_from_inline(Key_only, Inline_table) ->
    Section_kvs = begin
        _pipe = molt@internal@cst@elements:extract_inline_entries(Inline_table),
        gleam@list:map(
            _pipe,
            fun molt@internal@cst@elements:kv_to_section_form/1
        )
    end,
    Base = molt@internal@cst@builder:build_empty_array_of_tables(Key_only),
    {node,
        erlang:element(2, Base),
        lists:append(
            erlang:element(3, Base),
            gleam@list:map(
                Section_kvs,
                fun(Field@0) -> {node_element, Field@0} end
            )
        ),
        erlang:element(4, Base)}.

-file("src/molt/internal/document/representation.gleam", 384).
?DOC(false).
-spec type_mismatch(list(molt@types:path_segment()), binary()) -> molt@error:molt_error().
type_mismatch(Segments, Expected) ->
    {type_mismatch,
        {some, molt@internal@path:to_string(Segments)},
        Expected,
        <<"value"/utf8>>}.

-file("src/molt/internal/document/representation.gleam", 304).
?DOC(false).
-spec element_inline_table(greenwood:node_(molt@types:toml_kind())) -> {ok,
        greenwood:node_(molt@types:toml_kind())} |
    {error, nil}.
element_inline_table(Elem) ->
    gleam@list:find_map(erlang:element(3, Elem), fun(Child) -> case Child of
                {node_element, N} when erlang:element(2, N) =:= inline_table ->
                    {ok, N};

                _ ->
                    {error, nil}
            end end).

-file("src/molt/internal/document/representation.gleam", 232).
?DOC(false).
-spec inline_array_to_block_aot(
    molt@types:document(),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
inline_array_to_block_aot(Doc, Segments) ->
    gleam@result:'try'(
        begin
            _pipe = molt@internal@cst@query:get_cursor(
                erlang:element(4, Doc),
                Segments
            ),
            gleam@result:replace_error(
                _pipe,
                molt@error:not_found_path(Segments)
            )
        end,
        fun(Cursor) ->
            gleam@result:'try'(
                begin
                    _pipe@1 = molt@internal@document@primitives:find_kv_value(
                        erlang:element(2, Cursor),
                        array
                    ),
                    gleam@result:replace_error(
                        _pipe@1,
                        type_mismatch(Segments, <<"array"/utf8>>)
                    )
                end,
                fun(Array_node) ->
                    gleam@result:'try'(
                        gleam@list:try_map(
                            molt@internal@cst@elements:extract_array_items(
                                Array_node
                            ),
                            fun(Elem) -> _pipe@2 = element_inline_table(Elem),
                                gleam@result:replace_error(
                                    _pipe@2,
                                    type_mismatch(
                                        Segments,
                                        <<"array of inline tables"/utf8>>
                                    )
                                ) end
                        ),
                        fun(Inline_tables) ->
                            gleam@result:'try'(
                                molt@cst:leading_comments(
                                    erlang:element(4, Doc),
                                    Segments
                                ),
                                fun(Whole_leading) ->
                                    gleam@result:'try'(
                                        molt@cst:trailing_comment(
                                            erlang:element(4, Doc),
                                            Segments
                                        ),
                                        fun(Whole_trailing) ->
                                            Count = erlang:length(Inline_tables),
                                            gleam@result:'try'(
                                                capture_entry_comments(
                                                    erlang:element(4, Doc),
                                                    Segments,
                                                    Count
                                                ),
                                                fun(Element_comments) ->
                                                    Key_only = molt@internal@path:path_to_table_header(
                                                        Segments
                                                    ),
                                                    Entries = gleam@list:map(
                                                        Inline_tables,
                                                        fun(It) ->
                                                            build_aot_entry_from_inline(
                                                                Key_only,
                                                                It
                                                            )
                                                        end
                                                    ),
                                                    gleam@result:'try'(
                                                        molt@cst:delete(
                                                            erlang:element(
                                                                4,
                                                                Doc
                                                            ),
                                                            Segments
                                                        ),
                                                        fun(Deleted) ->
                                                            gleam@result:'try'(
                                                                gleam@list:try_fold(
                                                                    Entries,
                                                                    Deleted,
                                                                    fun(
                                                                        Tree,
                                                                        Entry
                                                                    ) ->
                                                                        molt@cst:insert_table_node(
                                                                            Tree,
                                                                            Entry
                                                                        )
                                                                    end
                                                                ),
                                                                fun(Inserted) ->
                                                                    gleam@result:'try'(
                                                                        gleam@list:try_fold(
                                                                            gleam@list:index_map(
                                                                                Element_comments,
                                                                                fun(
                                                                                    C,
                                                                                    I
                                                                                ) ->
                                                                                    {I,
                                                                                        C}
                                                                                end
                                                                            ),
                                                                            Inserted,
                                                                            fun(
                                                                                Tree@1,
                                                                                Indexed
                                                                            ) ->
                                                                                {I@1,
                                                                                    {Elem_leading,
                                                                                        Elem_trailing}} = Indexed,
                                                                                Leading = case I@1 of
                                                                                    0 ->
                                                                                        lists:append(
                                                                                            Whole_leading,
                                                                                            Elem_leading
                                                                                        );

                                                                                    _ ->
                                                                                        Elem_leading
                                                                                end,
                                                                                Trailing = case {I@1,
                                                                                    Elem_trailing} of
                                                                                    {0,
                                                                                        none} ->
                                                                                        Whole_trailing;

                                                                                    {_,
                                                                                        T} ->
                                                                                        T
                                                                                end,
                                                                                set_comments_if_any(
                                                                                    Tree@1,
                                                                                    lists:append(
                                                                                        Segments,
                                                                                        [{index_segment,
                                                                                                I@1}]
                                                                                    ),
                                                                                    Leading,
                                                                                    Trailing
                                                                                )
                                                                            end
                                                                        ),
                                                                        fun(
                                                                            New_tree
                                                                        ) ->
                                                                            {ok,
                                                                                molt@internal@document@primitives:rebuild(
                                                                                    Doc,
                                                                                    New_tree
                                                                                )}
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/representation.gleam", 104).
?DOC(false).
-spec inline_table_to_block(
    molt@types:document(),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
inline_table_to_block(Doc, Segments) ->
    gleam@result:'try'(
        begin
            _pipe = molt@internal@cst@query:get_cursor(
                erlang:element(4, Doc),
                Segments
            ),
            gleam@result:replace_error(
                _pipe,
                molt@error:not_found_path(Segments)
            )
        end,
        fun(Cursor) ->
            gleam@result:'try'(
                begin
                    _pipe@1 = molt@internal@document@primitives:find_kv_value(
                        erlang:element(2, Cursor),
                        inline_table
                    ),
                    gleam@result:replace_error(
                        _pipe@1,
                        {type_mismatch,
                            {some, molt@internal@path:to_string(Segments)},
                            <<"inline table"/utf8>>,
                            molt@internal@utils:toml_kind(
                                erlang:element(2, erlang:element(2, Cursor))
                            )}
                    )
                end,
                fun(Inline_table) ->
                    Section_kvs = begin
                        _pipe@2 = molt@internal@cst@elements:extract_inline_entries(
                            Inline_table
                        ),
                        gleam@list:map(
                            _pipe@2,
                            fun molt@internal@cst@elements:kv_to_section_form/1
                        )
                    end,
                    gleam@result:'try'(
                        molt@cst:leading_comments(
                            erlang:element(4, Doc),
                            Segments
                        ),
                        fun(Leading) ->
                            gleam@result:'try'(
                                molt@cst:trailing_comment(
                                    erlang:element(4, Doc),
                                    Segments
                                ),
                                fun(Trailing) ->
                                    molt@internal@document@index:with_index(
                                        Doc,
                                        fun(Idx) ->
                                            gleam@result:'try'(
                                                molt@internal@document@remove:prune(
                                                    Doc,
                                                    Idx,
                                                    Segments
                                                ),
                                                fun(Doc2) ->
                                                    Key_only = molt@internal@path:path_to_table_header(
                                                        Segments
                                                    ),
                                                    Header = molt@internal@cst@builder:build_empty_table(
                                                        Key_only
                                                    ),
                                                    gleam@result:'try'(
                                                        molt@cst:insert_table_node(
                                                            erlang:element(
                                                                4,
                                                                Doc2
                                                            ),
                                                            Header
                                                        ),
                                                        fun(With_header) ->
                                                            Section_cst_path = gleam@list:map(
                                                                Key_only,
                                                                fun(Field@0) -> {key_segment, Field@0} end
                                                            ),
                                                            gleam@result:'try'(
                                                                gleam@list:try_fold(
                                                                    Section_kvs,
                                                                    With_header,
                                                                    fun(
                                                                        Tree,
                                                                        Kv
                                                                    ) ->
                                                                        molt@cst:insert_kv(
                                                                            Tree,
                                                                            Section_cst_path,
                                                                            Kv,
                                                                            kv_at_end
                                                                        )
                                                                    end
                                                                ),
                                                                fun(Filled) ->
                                                                    gleam@result:'try'(
                                                                        set_comments_if_any(
                                                                            Filled,
                                                                            Segments,
                                                                            Leading,
                                                                            Trailing
                                                                        ),
                                                                        fun(
                                                                            New_tree
                                                                        ) ->
                                                                            {ok,
                                                                                molt@internal@document@primitives:rebuild(
                                                                                    Doc2,
                                                                                    New_tree
                                                                                )}
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/representation.gleam", 48).
?DOC(false).
-spec convert(
    molt@types:document(),
    binary(),
    list(molt@types:path_segment()),
    molt@ops:form()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
convert(Doc, P, Segments, Form) ->
    molt@internal@document@index:with_index(
        Doc,
        fun(Idx) ->
            case {Form, molt@internal@document@index:resolve(Idx, Segments)} of
                {block, {hit, _, {index_inline_table_value, _}}} ->
                    inline_table_to_block(Doc, Segments);

                {block, {hit, _, {index_table, _}}} ->
                    {ok, Doc};

                {block, {hit, _, {index_array_of_tables, _, _}}} ->
                    {ok, Doc};

                {block, {hit, _, {index_array_value, _}}} ->
                    inline_array_to_block_aot(Doc, Segments);

                {inline, {hit, _, {index_table, _}}} ->
                    block_table_to_inline(Doc, Segments);

                {inline, {hit, _, {index_array_of_tables, Count, _}}} ->
                    aot_to_inline_array(Doc, Segments, Count);

                {inline, {hit, _, {index_inline_table_value, _}}} ->
                    {ok, Doc};

                {_, {hit, _, Entry}} ->
                    {error,
                        {type_mismatch,
                            {some, P},
                            <<"inline or block table / array of tables"/utf8>>,
                            molt@internal@utils:index_entry_to_string(Entry)}};

                {_, {miss, _, _, _}} ->
                    {error, molt@error:not_found(P)};

                {_, {fresh, _}} ->
                    {error, molt@error:not_found(P)}
            end
        end
    ).

-file("src/molt/internal/document/representation.gleam", 27).
?DOC(false).
-spec set_representation(molt@types:document(), binary(), molt@ops:form()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
set_representation(Doc, P, Form) ->
    gleam@result:'try'(
        molt@internal@path:parse(P),
        fun(Segments) ->
            gleam@result:'try'(
                convert(Doc, P, Segments, Form),
                fun(Converted) ->
                    case molt@internal@validate:count(
                        erlang:element(4, Converted)
                    ) of
                        0 ->
                            {ok, Converted};

                        _ ->
                            {error,
                                {invalid_operation,
                                    <<"representation"/utf8>>,
                                    {some,
                                        <<<<"conversion would produce invalid TOML at \""/utf8,
                                                P/binary>>/binary,
                                            "\""/utf8>>}}}
                    end
                end
            )
        end
    ).