Skip to main content

src/molt@internal@document@remove.erl

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

-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/remove.gleam", 176).
?DOC(false).
-spec remove_missing(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key(),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
remove_missing(Doc, Idx, Key, Segments) ->
    case molt@internal@document@index:find_deepest_ancestor_entry(Idx, Key) of
        {ok, {index_scalar_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'(
                        begin
                            _pipe@1 = greenwood@zipper:delete(Cursor),
                            gleam@option:to_result(
                                _pipe@1,
                                molt@error:not_found_path(Segments)
                            )
                        end,
                        fun(Cursor@1) ->
                            _pipe@2 = greenwood@zipper:unzip(Cursor@1),
                            _pipe@3 = molt@internal@document@primitives:patch(
                                Doc,
                                _pipe@2,
                                Idx
                            ),
                            {ok, _pipe@3}
                        end
                    )
                end
            );

        {ok, {index_array_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'(
                        begin
                            _pipe@1 = greenwood@zipper:delete(Cursor),
                            gleam@option:to_result(
                                _pipe@1,
                                molt@error:not_found_path(Segments)
                            )
                        end,
                        fun(Cursor@1) ->
                            _pipe@2 = greenwood@zipper:unzip(Cursor@1),
                            _pipe@3 = molt@internal@document@primitives:patch(
                                Doc,
                                _pipe@2,
                                Idx
                            ),
                            {ok, _pipe@3}
                        end
                    )
                end
            );

        {ok, {index_inline_table_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'(
                        begin
                            _pipe@1 = greenwood@zipper:delete(Cursor),
                            gleam@option:to_result(
                                _pipe@1,
                                molt@error:not_found_path(Segments)
                            )
                        end,
                        fun(Cursor@1) ->
                            _pipe@2 = greenwood@zipper:unzip(Cursor@1),
                            _pipe@3 = molt@internal@document@primitives:patch(
                                Doc,
                                _pipe@2,
                                Idx
                            ),
                            {ok, _pipe@3}
                        end
                    )
                end
            );

        _ ->
            {error, molt@error:not_found_path(Segments)}
    end.

-file("src/molt/internal/document/remove.gleam", 326).
?DOC(false).
-spec counters_match(list({list(binary()), integer()}), list(integer())) -> boolean().
counters_match(Scopes, Counters) ->
    case {Scopes, Counters} of
        {[], []} ->
            true;

        {[{_, Idx} | S], [C | Cs]} ->
            (Idx =:= C) andalso counters_match(S, Cs);

        {_, _} ->
            false
    end.

-file("src/molt/internal/document/remove.gleam", 311).
?DOC(false).
-spec bump_counters(
    list({list(binary()), integer()}),
    list(integer()),
    list(binary())
) -> list(integer()).
bump_counters(Scopes, Counters, Kp) ->
    case {Scopes, Counters} of
        {[{Family, _} | S_rest], [C | C_rest]} ->
            case Family =:= Kp of
                true ->
                    [C + 1 | gleam@list:map(C_rest, fun(_) -> -1 end)];

                false ->
                    [C | bump_counters(S_rest, C_rest, Kp)]
            end;

        {_, _} ->
            Counters
    end.

-file("src/molt/internal/document/remove.gleam", 280).
?DOC(false).
-spec remove_scoped_family(
    list(greenwood:element(molt@types:toml_kind())),
    list({list(binary()), integer()}),
    list(binary()),
    list(integer()),
    list(greenwood:element(molt@types:toml_kind()))
) -> list(greenwood:element(molt@types:toml_kind())).
remove_scoped_family(Children, Scopes, Target, Counters, Acc) ->
    case Children of
        [] ->
            lists:reverse(Acc);

        [{node_element, N} | Rest] when (erlang:element(2, N) =:= array_of_tables) orelse (erlang:element(
            2,
            N
        ) =:= table) ->
            Kp = molt@internal@cst@elements:extract_key_segments(
                erlang:element(3, N)
            ),
            Counters@1 = bump_counters(Scopes, Counters, Kp),
            Drop = (gleam@list:take(Kp, erlang:length(Target)) =:= Target)
            andalso counters_match(Scopes, Counters@1),
            Acc@1 = case Drop of
                true ->
                    Acc;

                false ->
                    [{node_element, N} | Acc]
            end,
            remove_scoped_family(Rest, Scopes, Target, Counters@1, Acc@1);

        [El | Rest@1] ->
            remove_scoped_family(Rest@1, Scopes, Target, Counters, [El | Acc])
    end.

-file("src/molt/internal/document/remove.gleam", 261).
?DOC(false).
-spec do_index_scopes(
    list(molt@types:path_segment()),
    list(binary()),
    list({list(binary()), integer()})
) -> list({list(binary()), integer()}).
do_index_scopes(Remaining, Keys, Acc) ->
    case Remaining of
        [] ->
            lists:reverse(Acc);

        [{key_segment, K} | Rest] ->
            do_index_scopes(Rest, lists:append(Keys, [K]), Acc);

        [{index_segment, I} | Rest@1] ->
            do_index_scopes(Rest@1, Keys, [{Keys, I} | Acc])
    end.

-file("src/molt/internal/document/remove.gleam", 257).
?DOC(false).
-spec index_scopes(list(molt@types:path_segment())) -> list({list(binary()),
    integer()}).
index_scopes(Segments) ->
    do_index_scopes(Segments, [], []).

-file("src/molt/internal/document/remove.gleam", 65).
?DOC(false).
-spec remove_existing_array_of_tables(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
remove_existing_array_of_tables(Doc, Idx, Segments) ->
    case molt@internal@path:contains_index(Segments) of
        false ->
            Target_path = molt@internal@path:path_to_table_header(Segments),
            New_tree = greenwood:remove_children(
                erlang:element(4, Doc),
                fun(El) -> case El of
                        {node_element, N} when erlang:element(2, N) =:= array_of_tables ->
                            molt@internal@cst@elements:extract_key_segments(
                                erlang:element(3, N)
                            )
                            =:= Target_path;

                        _ ->
                            false
                    end end
            ),
            {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx)};

        true ->
            Norm = molt@internal@document@index:resolve_negative_indices(
                Idx,
                Segments
            ),
            Scopes = index_scopes(Norm),
            Target = molt@internal@path:path_to_table_header(Norm),
            New_children = remove_scoped_family(
                erlang:element(3, erlang:element(4, Doc)),
                Scopes,
                Target,
                gleam@list:map(Scopes, fun(_) -> -1 end),
                []
            ),
            {ok,
                molt@internal@document@primitives:rebuild(
                    Doc,
                    begin
                        _record = erlang:element(4, Doc),
                        {node,
                            erlang:element(2, _record),
                            New_children,
                            erlang:element(4, _record)}
                    end
                )}
    end.

-file("src/molt/internal/document/remove.gleam", 337).
?DOC(false).
-spec is_kv_under_prefix(
    greenwood:node_(molt@types:toml_kind()),
    list(binary())
) -> boolean().
is_kv_under_prefix(Kv, Prefix) ->
    case molt@internal@cst@elements:key_path(erlang:element(3, Kv)) of
        {some, Key_parts} ->
            gleam@list:take(Key_parts, erlang:length(Prefix)) =:= Prefix;

        none ->
            false
    end.

-file("src/molt/internal/document/remove.gleam", 239).
?DOC(false).
-spec under_prefix(list(binary())) -> fun((greenwood:element(molt@types:toml_kind())) -> boolean()).
under_prefix(Prefix) ->
    fun(El) -> case El of
            {node_element, N} when (erlang:element(2, N) =:= table) orelse (erlang:element(
                2,
                N
            ) =:= array_of_tables) ->
                gleam@list:take(
                    molt@internal@cst@elements:extract_key_segments(
                        erlang:element(3, N)
                    ),
                    erlang:length(Prefix)
                )
                =:= Prefix;

            {node_element, N@1} when erlang:element(2, N@1) =:= key_value ->
                is_kv_under_prefix(N@1, Prefix);

            _ ->
                false
        end end.

-file("src/molt/internal/document/remove.gleam", 204).
?DOC(false).
-spec remove_implicit_table(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
remove_implicit_table(Doc, Idx, Segments) ->
    case molt@internal@path:split_at_last_index(Segments) of
        {error, nil} ->
            Prefix = molt@internal@path:path_to_table_header(Segments),
            New_tree = greenwood:remove_children(
                erlang:element(4, Doc),
                under_prefix(Prefix)
            ),
            Key = molt@internal@document@index:path_to_index_key(Segments),
            Idx@1 = molt@internal@document@index:prune_empty_implicit_ancestors(
                Idx,
                Key
            ),
            {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx@1)};

        {ok, {Entry_path, Rel_segments}} ->
            Prefix@1 = molt@internal@path:path_to_table_header(Rel_segments),
            gleam@result:'try'(
                begin
                    _pipe = molt@internal@cst@query:get_cursor(
                        erlang:element(4, Doc),
                        Entry_path
                    ),
                    gleam@result:replace_error(
                        _pipe,
                        molt@error:not_found_path(Segments)
                    )
                end,
                fun(Cursor) ->
                    New_entry = greenwood:remove_children(
                        erlang:element(2, Cursor),
                        under_prefix(Prefix@1)
                    ),
                    New_tree@1 = begin
                        _pipe@1 = greenwood@zipper:set_focus(Cursor, New_entry),
                        greenwood@zipper:unzip(_pipe@1)
                    end,
                    {ok,
                        molt@internal@document@primitives:rebuild(
                            Doc,
                            New_tree@1
                        )}
                end
            )
    end.

-file("src/molt/internal/document/remove.gleam", 166).
?DOC(false).
-spec drop_leading_newlines(greenwood:node_(molt@types:toml_kind())) -> greenwood:node_(molt@types:toml_kind()).
drop_leading_newlines(Node) ->
    case erlang:element(4, Node) of
        bare ->
            Node;

        {trivia, Leading, Trailing} ->
            Stripped = gleam@list:drop_while(
                Leading,
                fun(T) -> erlang:element(2, T) =:= newline end
            ),
            {node,
                erlang:element(2, Node),
                erlang:element(3, Node),
                {trivia, Stripped, Trailing}}
    end.

-file("src/molt/internal/document/remove.gleam", 109).
?DOC(false).
-spec remove_value(
    molt@types:document(),
    list(molt@types:path_segment()),
    boolean()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
remove_value(Doc, Segments, Strip_first_blank) ->
    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) ->
            Is_first_with_right_sibling = case {Strip_first_blank,
                erlang:element(3, Cursor)} of
                {true, [Crumb | _]} ->
                    not gleam@list:any(
                        erlang:element(4, Crumb),
                        fun(El) -> case El of
                                {node_element, _} ->
                                    true;

                                _ ->
                                    false
                            end end
                    )
                    andalso gleam@list:any(
                        erlang:element(5, Crumb),
                        fun(El@1) -> case El@1 of
                                {node_element, _} ->
                                    true;

                                _ ->
                                    false
                            end end
                    );

                {_, _} ->
                    false
            end,
            case greenwood@zipper:delete(Cursor) of
                {some, Cursor@1} ->
                    Cursor@2 = case Is_first_with_right_sibling of
                        false ->
                            Cursor@1;

                        true ->
                            greenwood@zipper:set_focus(
                                Cursor@1,
                                drop_leading_newlines(
                                    erlang:element(2, Cursor@1)
                                )
                            )
                    end,
                    {ok,
                        molt@internal@document@primitives:rebuild(
                            Doc,
                            greenwood@zipper:unzip(Cursor@2)
                        )};

                none ->
                    gleam@result:'try'(
                        molt@cst:delete(erlang:element(4, Doc), Segments),
                        fun(New_tree) ->
                            {ok,
                                molt@internal@document@primitives:rebuild(
                                    Doc,
                                    New_tree
                                )}
                        end
                    )
            end
        end
    ).

-file("src/molt/internal/document/remove.gleam", 39).
?DOC(false).
-spec remove_existing(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    molt@types:index_entry()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
remove_existing(Doc, Idx, Segments, Entry) ->
    Key = molt@internal@document@index:path_to_index_key(Segments),
    Idx@1 = molt@internal@document@index:remove_subtree(Idx, Key),
    case Entry of
        {index_scalar_value, _} ->
            remove_value(Doc, Segments, false);

        {index_array_value, _} ->
            remove_value(Doc, Segments, false);

        {index_inline_table_value, _} ->
            remove_value(Doc, Segments, false);

        {index_table, _} ->
            remove_value(Doc, Segments, false);

        {index_array_of_tables_entry, _, _, _} ->
            remove_value(Doc, Segments, true);

        {index_implicit_table, _} ->
            remove_implicit_table(Doc, Idx@1, Segments);

        {index_array_of_tables, _, _} ->
            remove_existing_array_of_tables(Doc, Idx@1, Segments)
    end.

-file("src/molt/internal/document/remove.gleam", 24).
?DOC(false).
-spec prune(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
prune(Doc, Idx, Segments) ->
    Lookup = molt@internal@document@index:resolve_negative_indices(
        Idx,
        Segments
    ),
    Key = molt@internal@document@index:path_to_index_key(Lookup),
    case molt@internal@document@index:get(Idx, Key) of
        {ok, Entry} ->
            remove_existing(Doc, Idx, Segments, Entry);

        {error, nil} ->
            remove_missing(Doc, Idx, Key, Segments)
    end.

-file("src/molt/internal/document/remove.gleam", 15).
?DOC(false).
-spec delete(molt@types:document(), binary()) -> {ok, molt@types:document()} |
    {error, molt@error:molt_error()}.
delete(Doc, P) ->
    gleam@result:'try'(
        molt@internal@path:parse(P),
        fun(Segments) ->
            molt@internal@document@index:with_index(
                Doc,
                fun(Idx) -> prune(Doc, Idx, Segments) end
            )
        end
    ).