Skip to main content

src/molt@internal@document@arrays.erl

-module(molt@internal@document@arrays).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/document/arrays.gleam").
-export([append/3, concat/3, insert/4]).
-export_type([array_position/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(false).

-type array_position() :: at_end | {before, integer()}.

-file("src/molt/internal/document/arrays.gleam", 355).
?DOC(false).
-spec insert_bounds_error(list(molt@types:path_segment()), integer(), integer()) -> molt@error:molt_error().
insert_bounds_error(Segments, Before, Len) ->
    {index_out_of_range, molt@internal@path:to_string(Segments), Before, Len}.

-file("src/molt/internal/document/arrays.gleam", 341).
?DOC(false).
-spec resolve_array_position(
    array_position(),
    list(greenwood:node_(molt@types:toml_kind())),
    list(molt@types:path_segment())
) -> {ok, integer()} | {error, molt@error:molt_error()}.
resolve_array_position(Position, Items, Segments) ->
    Len = erlang:length(Items),
    case Position of
        at_end ->
            {ok, Len};

        {before, I} ->
            _pipe = molt@internal@utils:resolve_insert_position(I, Len),
            gleam@result:replace_error(
                _pipe,
                insert_bounds_error(Segments, I, Len)
            )
    end.

-file("src/molt/internal/document/arrays.gleam", 288).
?DOC(false).
-spec do_modify_inline_array(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    array_position(),
    molt@value:value()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
do_modify_inline_array(Doc, Idx, Segments, Position, New_value) ->
    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'(
                case molt@internal@document@primitives:find_kv_value(
                    erlang:element(2, Cursor),
                    array
                ) of
                    {ok, Array_node} ->
                        Items = molt@internal@cst@elements:extract_array_items(
                            Array_node
                        ),
                        gleam@result:'try'(
                            resolve_array_position(Position, Items, Segments),
                            fun(I) ->
                                Layout = molt@internal@cst@elements:array_layout(
                                    Array_node
                                ),
                                New_item = molt@internal@cst@elements:build_array_item(
                                    molt@value:to_cst(New_value),
                                    Layout
                                ),
                                {Head, Tail} = gleam@list:split(Items, I),
                                New_array = molt@internal@cst@elements:rebuild_array(
                                    Array_node,
                                    lists:append([Head, [New_item], Tail])
                                ),
                                {ok, {node_element, New_array}}
                            end
                        );

                    {error, nil} ->
                        Got = case molt@internal@document@primitives:find_kv_value(
                            erlang:element(2, Cursor),
                            inline_table
                        ) of
                            {ok, _} ->
                                <<"inline table"/utf8>>;

                            {error, nil} ->
                                <<"scalar value"/utf8>>
                        end,
                        {error,
                            {type_mismatch,
                                {some, molt@internal@path:to_string(Segments)},
                                <<"array"/utf8>>,
                                Got}}
                end,
                fun(New_value_el) ->
                    molt@internal@document@primitives:cursor_replace_value(
                        Doc,
                        Idx,
                        Segments,
                        New_value_el
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/arrays.gleam", 268).
?DOC(false).
-spec build_aot_entry_node(
    list(molt@types:path_segment()),
    list({binary(), molt@value:value()})
) -> greenwood:node_(molt@types:toml_kind()).
build_aot_entry_node(Segments, Entries) ->
    Key_path = molt@internal@path:path_to_table_header(Segments),
    Header = molt@internal@cst@builder:build_empty_array_of_tables(Key_path),
    Kv_children = gleam@list:map(
        Entries,
        fun(Entry) ->
            {K, V} = Entry,
            {node_element,
                molt@internal@cst@builder:build_kv_node(K, molt@value:to_cst(V))}
        end
    ),
    {node,
        erlang:element(2, Header),
        lists:append(erlang:element(3, Header), Kv_children),
        erlang:element(4, Header)}.

-file("src/molt/internal/document/arrays.gleam", 256).
?DOC(false).
-spec table_entries_or_mismatch(
    list(molt@types:path_segment()),
    molt@value:value()
) -> {ok, list({binary(), molt@value:value()})} |
    {error, molt@error:molt_error()}.
table_entries_or_mismatch(Segments, Val) ->
    _pipe = molt@value:table_to_list(Val),
    gleam@result:replace_error(
        _pipe,
        {type_mismatch,
            {some, molt@internal@path:to_string(Segments)},
            <<"table"/utf8>>,
            molt@value:type_of(Val)}
    ).

-file("src/molt/internal/document/arrays.gleam", 228).
?DOC(false).
-spec do_modify_aot(
    molt@types:document(),
    list(molt@types:path_segment()),
    array_position(),
    molt@value:value()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
do_modify_aot(Doc, Segments, Position, Val) ->
    gleam@result:'try'(
        table_entries_or_mismatch(Segments, Val),
        fun(Entries) ->
            Entry_node = build_aot_entry_node(Segments, Entries),
            case Position of
                at_end ->
                    _pipe = molt@cst:insert_array_of_tables_entry(
                        erlang:element(4, Doc),
                        Segments,
                        Entry_node,
                        entry_at_end
                    ),
                    gleam@result:map(
                        _pipe,
                        fun(Tree) ->
                            molt@internal@document@primitives:rebuild(Doc, Tree)
                        end
                    );

                {before, I} ->
                    _pipe@1 = molt@cst:insert_array_of_tables_entry(
                        erlang:element(4, Doc),
                        Segments,
                        Entry_node,
                        {before_index, I}
                    ),
                    gleam@result:map(
                        _pipe@1,
                        fun(Tree@1) ->
                            molt@internal@document@primitives:rebuild(
                                Doc,
                                Tree@1
                            )
                        end
                    )
            end
        end
    ).

-file("src/molt/internal/document/arrays.gleam", 23).
?DOC(false).
-spec append(molt@types:document(), binary(), molt@value:value()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
append(Doc, P, Val) ->
    gleam@bool:guard(
        molt@value:type_of(Val) =:= <<"invalid"/utf8>>,
        {error, {invalid_toml_value, P, molt@value:invalid_text(Val)}},
        fun() ->
            gleam@result:'try'(
                molt@internal@path:parse(P),
                fun(Segments) ->
                    molt@internal@document@index:with_index(
                        Doc,
                        fun(Idx) ->
                            case molt@internal@document@index:resolve(
                                Idx,
                                Segments
                            ) of
                                {hit, _, {index_array_of_tables, _, _}} ->
                                    do_modify_aot(Doc, Segments, at_end, Val);

                                {hit, _, {index_array_value, _}} ->
                                    do_modify_inline_array(
                                        Doc,
                                        Idx,
                                        Segments,
                                        at_end,
                                        Val
                                    );

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

                                {miss, _, Ancestor, _} ->
                                    case Ancestor of
                                        {index_array_value, _} ->
                                            do_modify_inline_array(
                                                Doc,
                                                Idx,
                                                Segments,
                                                at_end,
                                                Val
                                            );

                                        _ ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"array"/utf8>>,
                                                    molt@internal@utils:index_entry_to_string(
                                                        Ancestor
                                                    )}}
                                    end;

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

-file("src/molt/internal/document/arrays.gleam", 133).
?DOC(false).
-spec concat_aot(
    molt@types:document(),
    list(molt@types:path_segment()),
    list(molt@value:value())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
concat_aot(Doc, Segments, Vals) ->
    gleam@result:'try'(
        gleam@list:try_map(
            Vals,
            fun(Val) ->
                gleam@result:'try'(
                    table_entries_or_mismatch(Segments, Val),
                    fun(Entries) ->
                        {ok, build_aot_entry_node(Segments, Entries)}
                    end
                )
            end
        ),
        fun(Entry_nodes) ->
            gleam@result:'try'(
                gleam@list:try_fold(
                    Entry_nodes,
                    erlang:element(4, Doc),
                    fun(Tree, Entry_node) ->
                        molt@cst:insert_array_of_tables_entry(
                            Tree,
                            Segments,
                            Entry_node,
                            entry_at_end
                        )
                    end
                ),
                fun(New_tree) ->
                    {ok,
                        molt@internal@document@primitives:rebuild(Doc, New_tree)}
                end
            )
        end
    ).

-file("src/molt/internal/document/arrays.gleam", 88).
?DOC(false).
-spec concat(molt@types:document(), binary(), list(molt@value:value())) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
concat(Doc, P, Vals) ->
    gleam@result:'try'(
        molt@internal@path:parse(P),
        fun(Segments) ->
            molt@internal@document@index:with_index(
                Doc,
                fun(Idx) ->
                    case molt@internal@document@index:resolve(Idx, Segments) of
                        {hit, _, {index_array_of_tables, _, _}} ->
                            concat_aot(Doc, Segments, Vals);

                        {hit, _, {index_array_value, _}} ->
                            gleam@list:try_fold(
                                Vals,
                                Doc,
                                fun(D, Val) -> append(D, P, Val) end
                            );

                        {miss, _, {index_array_value, _}, _} ->
                            gleam@list:try_fold(
                                Vals,
                                Doc,
                                fun(D, Val) -> append(D, P, Val) end
                            );

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

                        {miss, _, Ancestor, _} ->
                            {error,
                                {type_mismatch,
                                    {some, P},
                                    <<"array"/utf8>>,
                                    molt@internal@utils:index_entry_to_string(
                                        Ancestor
                                    )}};

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

-file("src/molt/internal/document/arrays.gleam", 157).
?DOC(false).
-spec insert(molt@types:document(), binary(), integer(), molt@value:value()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
insert(Doc, P, Before, Val) ->
    gleam@bool:guard(
        molt@value:type_of(Val) =:= <<"invalid"/utf8>>,
        {error, {invalid_toml_value, P, molt@value:invalid_text(Val)}},
        fun() ->
            gleam@result:'try'(
                molt@internal@path:parse(P),
                fun(Segments) ->
                    molt@internal@document@index:with_index(
                        Doc,
                        fun(Idx) ->
                            case molt@internal@document@index:resolve(
                                Idx,
                                Segments
                            ) of
                                {hit, _, {index_array_value, _}} ->
                                    do_modify_inline_array(
                                        Doc,
                                        Idx,
                                        Segments,
                                        {before, Before},
                                        Val
                                    );

                                {hit, _, {index_array_of_tables, Count, _}} ->
                                    gleam@result:'try'(
                                        begin
                                            _pipe = molt@internal@utils:resolve_insert_position(
                                                Before,
                                                Count
                                            ),
                                            gleam@result:replace_error(
                                                _pipe,
                                                insert_bounds_error(
                                                    Segments,
                                                    Before,
                                                    Count
                                                )
                                            )
                                        end,
                                        fun(I) ->
                                            Position = case I =:= Count of
                                                true ->
                                                    at_end;

                                                false ->
                                                    {before, I}
                                            end,
                                            do_modify_aot(
                                                Doc,
                                                Segments,
                                                Position,
                                                Val
                                            )
                                        end
                                    );

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

                                {miss, _, Ancestor, _} ->
                                    case Ancestor of
                                        {index_array_value, _} ->
                                            do_modify_inline_array(
                                                Doc,
                                                Idx,
                                                Segments,
                                                {before, Before},
                                                Val
                                            );

                                        _ ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"array"/utf8>>,
                                                    molt@internal@utils:index_entry_to_string(
                                                        Ancestor
                                                    )}}
                                    end;

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