Skip to main content

src/molt@internal@document@reshape.erl

-module(molt@internal@document@reshape).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/document/reshape.gleam").
-export([rename/3, move/3, move_keys/5, merge/4]).

-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/reshape.gleam", 516).
?DOC(false).
-spec rewrite_kv_key(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment())
) -> greenwood:node_(molt@types:toml_kind()).
rewrite_kv_key(Kv, New_key) ->
    Key_names = gleam@list:map(New_key, fun(Seg) -> case Seg of
                {key_segment, Name} ->
                    Name;

                _ ->
                    molt@internal@path:to_string([Seg])
            end end),
    Value_and_after = molt@internal@cst@elements:value_tokens(
        erlang:element(3, Kv)
    ),
    New_key_elements = case Key_names of
        [Single] ->
            [{token_element, molt@internal@utils:make_key_token(Single)}];

        _ ->
            [{node_element,
                    greenwood:node(
                        key,
                        molt@internal@cst@builder:build_key_tokens(Key_names)
                    )}]
    end,
    Children = lists:append(
        [New_key_elements,
            [{token_element, {token, whitespace, <<" "/utf8>>}},
                {token_element, {token, equals, <<""/utf8>>}}],
            Value_and_after]
    ),
    {node, erlang:element(2, Kv), Children, erlang:element(4, Kv)}.

-file("src/molt/internal/document/reshape.gleam", 487).
?DOC(false).
-spec rewrite_header_prefix(
    list(greenwood:element(molt@types:toml_kind())),
    list(binary()),
    list(binary())
) -> list(greenwood:element(molt@types:toml_kind())).
rewrite_header_prefix(Children, From_keys, To_keys) ->
    From_len = erlang:length(From_keys),
    gleam@list:map(Children, fun(El) -> case El of
                {node_element, N} when (erlang:element(2, N) =:= table) orelse (erlang:element(
                    2,
                    N
                ) =:= array_of_tables) ->
                    Tp = molt@internal@cst@elements:extract_key_segments(
                        erlang:element(3, N)
                    ),
                    gleam@bool:guard(
                        gleam@list:take(Tp, From_len) /= From_keys,
                        El,
                        fun() ->
                            New_path = lists:append(
                                To_keys,
                                gleam@list:drop(Tp, From_len)
                            ),
                            {node_element,
                                molt@internal@cst@builder:rewrite_header_path(
                                    N,
                                    New_path
                                )}
                        end
                    );

                {node_element, N@1} when erlang:element(2, N@1) =:= key_value ->
                    case molt@internal@cst@elements:key_path(
                        erlang:element(3, N@1)
                    ) of
                        {some, Kp} ->
                            gleam@bool:guard(
                                gleam@list:take(Kp, From_len) /= From_keys,
                                El,
                                fun() ->
                                    New_kp = lists:append(
                                        To_keys,
                                        gleam@list:drop(Kp, From_len)
                                    ),
                                    New_key = gleam@list:map(
                                        New_kp,
                                        fun(Field@0) -> {key_segment, Field@0} end
                                    ),
                                    {node_element, rewrite_kv_key(N@1, New_key)}
                                end
                            );

                        none ->
                            El
                    end;

                _ ->
                    El
            end end).

-file("src/molt/internal/document/reshape.gleam", 422).
?DOC(false).
-spec move_implicit_table(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
move_implicit_table(Doc, Idx, From, To) ->
    case {molt@internal@path:split_at_last_index(From),
        molt@internal@path:split_at_last_index(To)} of
        {{error, nil}, {error, nil}} ->
            From_keys = molt@internal@path:path_to_table_header(From),
            To_keys = molt@internal@path:path_to_table_header(To),
            New_children = rewrite_header_prefix(
                erlang:element(3, erlang:element(4, Doc)),
                From_keys,
                To_keys
            ),
            New_tree = begin
                _record = erlang:element(4, Doc),
                {node,
                    erlang:element(2, _record),
                    New_children,
                    erlang:element(4, _record)}
            end,
            From_key = molt@internal@document@index:path_to_index_key(From),
            To_key = molt@internal@document@index:path_to_index_key(To),
            Idx@1 = molt@internal@document@index:relocate_subtree(
                Idx,
                From_key,
                To_key
            ),
            {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx@1)};

        {{ok, {From_entry, From_rel}}, {ok, {To_entry, To_rel}}} when From_entry =:= To_entry ->
            From_keys@1 = molt@internal@path:path_to_table_header(From_rel),
            To_keys@1 = molt@internal@path:path_to_table_header(To_rel),
            gleam@result:'try'(
                begin
                    _pipe = molt@internal@cst@query:get_cursor(
                        erlang:element(4, Doc),
                        From_entry
                    ),
                    gleam@result:replace_error(
                        _pipe,
                        molt@error:not_found_path(From)
                    )
                end,
                fun(Cursor) ->
                    Entry = erlang:element(2, Cursor),
                    New_entry = {node,
                        erlang:element(2, Entry),
                        rewrite_header_prefix(
                            erlang:element(3, Entry),
                            From_keys@1,
                            To_keys@1
                        ),
                        erlang:element(4, Entry)},
                    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
            );

        {_, _} ->
            {error,
                {invalid_operation,
                    <<"move"/utf8>>,
                    {some,
                        <<<<<<"cannot move an implicit table across array of tables entries: "/utf8,
                                    (molt@internal@path:to_string(From))/binary>>/binary,
                                " -> "/utf8>>/binary,
                            (molt@internal@path:to_string(To))/binary>>}}}
    end.

-file("src/molt/internal/document/reshape.gleam", 26).
?DOC(false).
-spec rename(molt@types:document(), binary(), binary()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
rename(Doc, P, To) ->
    gleam@bool:guard(
        P =:= <<""/utf8>>,
        {error, {invalid_operation, <<"rename"/utf8>>, none}},
        fun() ->
            gleam@result:'try'(
                molt@internal@path:parse(P),
                fun(Segments) ->
                    gleam@bool:guard(
                        case molt@internal@path:split_last_segment(Segments) of
                            {_, {key_segment, _}} ->
                                false;

                            _ ->
                                true
                        end,
                        {error,
                            {type_mismatch,
                                {some, P},
                                <<"a key"/utf8>>,
                                <<"an index segment"/utf8>>}},
                        fun() ->
                            molt@internal@document@index:with_index(
                                Doc,
                                fun(Idx) ->
                                    case molt@internal@document@index:resolve(
                                        Idx,
                                        Segments
                                    ) of
                                        {miss, _, _, _} ->
                                            {error, molt@error:not_found(P)};

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

                                        {hit,
                                            _,
                                            {index_array_of_tables_entry,
                                                _,
                                                _,
                                                _}} ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"a named key"/utf8>>,
                                                    <<"array of tables entry"/utf8>>}};

                                        {hit, _, Entry} ->
                                            case molt@internal@document@index:get(
                                                Idx,
                                                molt@internal@document@index:rename_key(
                                                    Segments,
                                                    To
                                                )
                                            ) of
                                                {ok, Current} ->
                                                    {error,
                                                        {already_exists,
                                                            molt@internal@path:to_string(
                                                                lists:append(
                                                                    molt@internal@path:drop_last_segment(
                                                                        Segments
                                                                    ),
                                                                    [{key_segment,
                                                                            To}]
                                                                )
                                                            ),
                                                            Current}};

                                                _ ->
                                                    case Entry of
                                                        {index_implicit_table,
                                                            _} ->
                                                            {Parent, _} = molt@internal@path:split_last_segment(
                                                                Segments
                                                            ),
                                                            To_segments = lists:append(
                                                                Parent,
                                                                [{key_segment,
                                                                        To}]
                                                            ),
                                                            move_implicit_table(
                                                                Doc,
                                                                Idx,
                                                                Segments,
                                                                To_segments
                                                            );

                                                        {index_array_of_tables,
                                                            _,
                                                            _} ->
                                                            From_keys = molt@internal@path:path_to_table_header(
                                                                Segments
                                                            ),
                                                            {Parent@1, _} = molt@internal@path:split_last_segment(
                                                                Segments
                                                            ),
                                                            To_keys = molt@internal@path:path_to_table_header(
                                                                lists:append(
                                                                    Parent@1,
                                                                    [{key_segment,
                                                                            To}]
                                                                )
                                                            ),
                                                            New_children = rewrite_header_prefix(
                                                                erlang:element(
                                                                    3,
                                                                    erlang:element(
                                                                        4,
                                                                        Doc
                                                                    )
                                                                ),
                                                                From_keys,
                                                                To_keys
                                                            ),
                                                            {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
                                                                )};

                                                        _ ->
                                                            gleam@result:'try'(
                                                                molt@cst:rename(
                                                                    erlang:element(
                                                                        4,
                                                                        Doc
                                                                    ),
                                                                    Segments,
                                                                    To
                                                                ),
                                                                fun(New_tree) ->
                                                                    From_key = molt@internal@document@index:path_to_index_key(
                                                                        Segments
                                                                    ),
                                                                    To_key = molt@internal@document@index:rename_key(
                                                                        Segments,
                                                                        To
                                                                    ),
                                                                    Idx@1 = molt@internal@document@index:relocate_subtree(
                                                                        Idx,
                                                                        From_key,
                                                                        To_key
                                                                    ),
                                                                    {ok,
                                                                        molt@internal@document@primitives:patch(
                                                                            Doc,
                                                                            New_tree,
                                                                            Idx@1
                                                                        )}
                                                                end
                                                            )
                                                    end
                                            end
                                    end
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/reshape.gleam", 292).
?DOC(false).
-spec move_kv(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
move_kv(Doc, Idx, From, To) ->
    gleam@result:'try'(
        begin
            _pipe = molt@internal@cst@query:get_cursor(
                erlang:element(4, Doc),
                From
            ),
            gleam@result:replace_error(_pipe, molt@error:not_found_path(From))
        end,
        fun(Cursor) ->
            {To_container, _} = molt@internal@path:split_last_segment(To),
            To_kv_key = gleam@list:drop(To, erlang:length(To_container)),
            Kv_node = rewrite_kv_key(erlang:element(2, Cursor), To_kv_key),
            gleam@result:'try'(
                molt@cst:delete(erlang:element(4, Doc), From),
                fun(Tree_after_delete) ->
                    From_key = molt@internal@document@index:path_to_index_key(
                        From
                    ),
                    To_key = molt@internal@document@index:path_to_index_key(To),
                    Idx@1 = molt@internal@document@index:relocate_subtree(
                        Idx,
                        From_key,
                        To_key
                    ),
                    Doc@1 = molt@internal@document@primitives:patch(
                        Doc,
                        Tree_after_delete,
                        Idx@1
                    ),
                    gleam@result:'try'(
                        molt@internal@document@index:get_index(Doc@1),
                        fun(Idx2) ->
                            case molt@internal@document@index:resolve(
                                Idx2,
                                To_container
                            ) of
                                {hit, _, {index_table, _}} ->
                                    _pipe@1 = molt@cst:insert_kv(
                                        erlang:element(4, Doc@1),
                                        To_container,
                                        Kv_node,
                                        kv_at_end
                                    ),
                                    gleam@result:map(
                                        _pipe@1,
                                        fun(Tree) ->
                                            molt@internal@document@primitives:patch(
                                                Doc@1,
                                                Tree,
                                                Idx2
                                            )
                                        end
                                    );

                                {hit, _, {index_array_of_tables_entry, _, _, _}} ->
                                    _pipe@1 = molt@cst:insert_kv(
                                        erlang:element(4, Doc@1),
                                        To_container,
                                        Kv_node,
                                        kv_at_end
                                    ),
                                    gleam@result:map(
                                        _pipe@1,
                                        fun(Tree) ->
                                            molt@internal@document@primitives:patch(
                                                Doc@1,
                                                Tree,
                                                Idx2
                                            )
                                        end
                                    );

                                {hit, _, {index_implicit_table, _}} ->
                                    Full_kv = rewrite_kv_key(Kv_node, To),
                                    New_tree = molt@internal@document@primitives:insert_kv_before_first_header(
                                        erlang:element(4, Doc@1),
                                        Full_kv
                                    ),
                                    Idx3 = molt@internal@document@index:ensure_implicit_tables(
                                        Idx2,
                                        To
                                    ),
                                    {ok,
                                        molt@internal@document@primitives:patch(
                                            Doc@1,
                                            New_tree,
                                            Idx3
                                        )};

                                {miss, _, _, _} ->
                                    Full_kv = rewrite_kv_key(Kv_node, To),
                                    New_tree = molt@internal@document@primitives:insert_kv_before_first_header(
                                        erlang:element(4, Doc@1),
                                        Full_kv
                                    ),
                                    Idx3 = molt@internal@document@index:ensure_implicit_tables(
                                        Idx2,
                                        To
                                    ),
                                    {ok,
                                        molt@internal@document@primitives:patch(
                                            Doc@1,
                                            New_tree,
                                            Idx3
                                        )};

                                {fresh, _} ->
                                    Full_kv = rewrite_kv_key(Kv_node, To),
                                    New_tree = molt@internal@document@primitives:insert_kv_before_first_header(
                                        erlang:element(4, Doc@1),
                                        Full_kv
                                    ),
                                    Idx3 = molt@internal@document@index:ensure_implicit_tables(
                                        Idx2,
                                        To
                                    ),
                                    {ok,
                                        molt@internal@document@primitives:patch(
                                            Doc@1,
                                            New_tree,
                                            Idx3
                                        )};

                                _ ->
                                    {error,
                                        {invalid_operation,
                                            <<"move into inline value container"/utf8>>,
                                            none}}
                            end
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/reshape.gleam", 118).
?DOC(false).
-spec move(molt@types:document(), binary(), binary()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
move(Doc, From, To) ->
    gleam@result:'try'(
        molt@internal@path:parse(From),
        fun(From_segments) ->
            gleam@result:'try'(
                molt@internal@path:parse(To),
                fun(To_segments) ->
                    molt@internal@document@index:with_index(
                        Doc,
                        fun(Idx) ->
                            Resolution = molt@internal@document@index:resolve(
                                Idx,
                                From_segments
                            ),
                            gleam@bool:guard(case Resolution of
                                    {miss, _, _, _} ->
                                        true;

                                    {fresh, _} ->
                                        true;

                                    _ ->
                                        false
                                end, {error, molt@error:not_found(From)}, fun() ->
                                    Maybe_existing_at_to = molt@internal@document@index:get_path(
                                        Idx,
                                        To_segments
                                    ),
                                    gleam@bool:guard(
                                        gleam@result:is_ok(Maybe_existing_at_to),
                                        case molt@internal@path:split_last_segment(
                                            To_segments
                                        ) of
                                            {_, {key_segment, _}} ->
                                                {error,
                                                    {already_exists,
                                                        To,
                                                        gleam@result:unwrap(
                                                            Maybe_existing_at_to,
                                                            {index_scalar_value,
                                                                []}
                                                        )}};

                                            _ ->
                                                {error,
                                                    {type_mismatch,
                                                        {some, To},
                                                        <<"a key"/utf8>>,
                                                        <<"an index segment"/utf8>>}}
                                        end,
                                        fun() -> case Resolution of
                                                {hit,
                                                    _,
                                                    {index_implicit_table, _}} ->
                                                    move_implicit_table(
                                                        Doc,
                                                        Idx,
                                                        From_segments,
                                                        To_segments
                                                    );

                                                {hit, _, {index_table, _}} ->
                                                    gleam@result:'try'(
                                                        molt@cst:move(
                                                            erlang:element(
                                                                4,
                                                                Doc
                                                            ),
                                                            From_segments,
                                                            To_segments
                                                        ),
                                                        fun(New_tree) ->
                                                            From_key = molt@internal@document@index:path_to_index_key(
                                                                From_segments
                                                            ),
                                                            To_key = molt@internal@document@index:path_to_index_key(
                                                                To_segments
                                                            ),
                                                            Idx@1 = molt@internal@document@index:relocate_subtree(
                                                                Idx,
                                                                From_key,
                                                                To_key
                                                            ),
                                                            {ok,
                                                                molt@internal@document@primitives:patch(
                                                                    Doc,
                                                                    New_tree,
                                                                    Idx@1
                                                                )}
                                                        end
                                                    );

                                                {hit,
                                                    _,
                                                    {index_array_of_tables,
                                                        _,
                                                        _}} ->
                                                    gleam@result:'try'(
                                                        molt@cst:move(
                                                            erlang:element(
                                                                4,
                                                                Doc
                                                            ),
                                                            From_segments,
                                                            To_segments
                                                        ),
                                                        fun(New_tree) ->
                                                            From_key = molt@internal@document@index:path_to_index_key(
                                                                From_segments
                                                            ),
                                                            To_key = molt@internal@document@index:path_to_index_key(
                                                                To_segments
                                                            ),
                                                            Idx@1 = molt@internal@document@index:relocate_subtree(
                                                                Idx,
                                                                From_key,
                                                                To_key
                                                            ),
                                                            {ok,
                                                                molt@internal@document@primitives:patch(
                                                                    Doc,
                                                                    New_tree,
                                                                    Idx@1
                                                                )}
                                                        end
                                                    );

                                                {hit, _, _} ->
                                                    move_kv(
                                                        Doc,
                                                        Idx,
                                                        From_segments,
                                                        To_segments
                                                    );

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

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

-file("src/molt/internal/document/reshape.gleam", 338).
?DOC(false).
-spec move_single_key(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    binary(),
    molt@ops:conflict_strategy()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
move_single_key(Doc, Idx, From, To, Key, On_conflict) ->
    From_key_path = lists:append(From, [{key_segment, Key}]),
    To_key_path = lists:append(To, [{key_segment, Key}]),
    Maybe_existing = molt@internal@document@index:get_path(Idx, To_key_path),
    Exists = gleam@result:is_ok(Maybe_existing),
    gleam@bool:guard(
        Exists andalso (On_conflict =:= on_conflict_error),
        {error,
            {already_exists,
                molt@internal@path:to_string(To_key_path),
                gleam@result:unwrap(Maybe_existing, {index_scalar_value, []})}},
        fun() ->
            gleam@bool:guard(
                Exists andalso (On_conflict =:= on_conflict_skip),
                {ok, Doc},
                fun() ->
                    gleam@result:'try'(
                        case Exists andalso (On_conflict =:= on_conflict_overwrite) of
                            true ->
                                molt@internal@document@index:with_index(
                                    Doc,
                                    fun(Idx2) ->
                                        molt@internal@document@remove:prune(
                                            Doc,
                                            Idx2,
                                            To_key_path
                                        )
                                    end
                                );

                            false ->
                                {ok, Doc}
                        end,
                        fun(Doc@1) ->
                            molt@internal@document@index:with_index(
                                Doc@1,
                                fun(Idx_curr) ->
                                    case molt@cst:move(
                                        erlang:element(4, Doc@1),
                                        From_key_path,
                                        To_key_path
                                    ) of
                                        {ok, New_tree} ->
                                            From_ikey = molt@internal@document@index:path_to_index_key(
                                                From_key_path
                                            ),
                                            To_ikey = molt@internal@document@index:path_to_index_key(
                                                To_key_path
                                            ),
                                            Idx2@1 = molt@internal@document@index:relocate_subtree(
                                                Idx_curr,
                                                From_ikey,
                                                To_ikey
                                            ),
                                            {ok,
                                                molt@internal@document@primitives:patch(
                                                    Doc@1,
                                                    New_tree,
                                                    Idx2@1
                                                )};

                                        _ ->
                                            gleam@result:'try'(
                                                begin
                                                    _pipe = molt@internal@cst@query:get_cursor(
                                                        erlang:element(4, Doc@1),
                                                        From_key_path
                                                    ),
                                                    gleam@result:replace_error(
                                                        _pipe,
                                                        molt@error:not_found_path(
                                                            From_key_path
                                                        )
                                                    )
                                                end,
                                                fun(Src_cursor) ->
                                                    Val = molt@value:from_cst(
                                                        erlang:element(
                                                            2,
                                                            Src_cursor
                                                        )
                                                    ),
                                                    gleam@result:'try'(
                                                        molt@internal@document@remove:prune(
                                                            Doc@1,
                                                            Idx_curr,
                                                            From_key_path
                                                        ),
                                                        fun(Doc2) ->
                                                            molt@internal@document@index:with_index(
                                                                Doc2,
                                                                fun(Idx3) ->
                                                                    case molt@internal@document@index:resolve(
                                                                        Idx3,
                                                                        To_key_path
                                                                    ) of
                                                                        {hit,
                                                                            _,
                                                                            Current} ->
                                                                            {error,
                                                                                {already_exists,
                                                                                    molt@internal@path:to_string(
                                                                                        To_key_path
                                                                                    ),
                                                                                    Current}};

                                                                        {miss,
                                                                            Ancestor_path,
                                                                            Ancestor,
                                                                            _} ->
                                                                            gleam@result:'try'(
                                                                                molt@internal@document@index:insertion_site(
                                                                                    Idx3,
                                                                                    Ancestor_path,
                                                                                    Ancestor,
                                                                                    To_key_path
                                                                                ),
                                                                                fun(
                                                                                    Site
                                                                                ) ->
                                                                                    molt@internal@document@primitives:write_at_site(
                                                                                        Doc2,
                                                                                        Idx3,
                                                                                        Site,
                                                                                        To_key_path,
                                                                                        Val
                                                                                    )
                                                                                end
                                                                            );

                                                                        {fresh,
                                                                            _} ->
                                                                            molt@internal@document@primitives:write_at_site(
                                                                                Doc2,
                                                                                Idx3,
                                                                                {root_dotted_key,
                                                                                    To_key_path},
                                                                                To_key_path,
                                                                                Val
                                                                            )
                                                                    end
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                    end
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/reshape.gleam", 198).
?DOC(false).
-spec do_move_keys(
    molt@types:document(),
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    list(binary()),
    molt@ops:conflict_strategy()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
do_move_keys(Doc, From_segments, To_segments, Keys_list, On_conflict) ->
    molt@internal@document@index:with_index(
        Doc,
        fun(Idx) ->
            case molt@internal@document@index:resolve(Idx, From_segments) of
                {hit, _, {index_table, _}} ->
                    Existing_keys = gleam@list:filter(
                        Keys_list,
                        fun(Key) ->
                            molt@internal@document@index:has_path(
                                Idx,
                                lists:append(
                                    From_segments,
                                    [{key_segment, Key}]
                                )
                            )
                        end
                    ),
                    gleam@bool:guard(
                        Existing_keys =:= [],
                        {ok, Doc},
                        fun() ->
                            gleam@result:'try'(
                                molt@internal@document@structure:ensure_exists(
                                    Doc,
                                    molt@internal@path:to_string(To_segments),
                                    table
                                ),
                                fun(Doc@1) ->
                                    gleam@list:try_fold(
                                        Existing_keys,
                                        Doc@1,
                                        fun(D, Key@1) ->
                                            molt@internal@document@index:with_index(
                                                D,
                                                fun(Idx2) ->
                                                    move_single_key(
                                                        D,
                                                        Idx2,
                                                        From_segments,
                                                        To_segments,
                                                        Key@1,
                                                        On_conflict
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    );

                {hit, _, {index_implicit_table, _}} ->
                    Existing_keys = gleam@list:filter(
                        Keys_list,
                        fun(Key) ->
                            molt@internal@document@index:has_path(
                                Idx,
                                lists:append(
                                    From_segments,
                                    [{key_segment, Key}]
                                )
                            )
                        end
                    ),
                    gleam@bool:guard(
                        Existing_keys =:= [],
                        {ok, Doc},
                        fun() ->
                            gleam@result:'try'(
                                molt@internal@document@structure:ensure_exists(
                                    Doc,
                                    molt@internal@path:to_string(To_segments),
                                    table
                                ),
                                fun(Doc@1) ->
                                    gleam@list:try_fold(
                                        Existing_keys,
                                        Doc@1,
                                        fun(D, Key@1) ->
                                            molt@internal@document@index:with_index(
                                                D,
                                                fun(Idx2) ->
                                                    move_single_key(
                                                        D,
                                                        Idx2,
                                                        From_segments,
                                                        To_segments,
                                                        Key@1,
                                                        On_conflict
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    );

                {hit, _, {index_array_of_tables_entry, _, _, _}} ->
                    Existing_keys = gleam@list:filter(
                        Keys_list,
                        fun(Key) ->
                            molt@internal@document@index:has_path(
                                Idx,
                                lists:append(
                                    From_segments,
                                    [{key_segment, Key}]
                                )
                            )
                        end
                    ),
                    gleam@bool:guard(
                        Existing_keys =:= [],
                        {ok, Doc},
                        fun() ->
                            gleam@result:'try'(
                                molt@internal@document@structure:ensure_exists(
                                    Doc,
                                    molt@internal@path:to_string(To_segments),
                                    table
                                ),
                                fun(Doc@1) ->
                                    gleam@list:try_fold(
                                        Existing_keys,
                                        Doc@1,
                                        fun(D, Key@1) ->
                                            molt@internal@document@index:with_index(
                                                D,
                                                fun(Idx2) ->
                                                    move_single_key(
                                                        D,
                                                        Idx2,
                                                        From_segments,
                                                        To_segments,
                                                        Key@1,
                                                        On_conflict
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    );

                {hit, _, Entry} ->
                    {error,
                        {type_mismatch,
                            {some, molt@internal@path:to_string(From_segments)},
                            <<"table"/utf8>>,
                            molt@internal@utils:index_entry_to_string(Entry)}};

                {miss, _, _, _} ->
                    {error, molt@error:not_found_path(From_segments)};

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

-file("src/molt/internal/document/reshape.gleam", 180).
?DOC(false).
-spec move_keys(
    molt@types:document(),
    binary(),
    binary(),
    list(binary()),
    molt@ops:conflict_strategy()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
move_keys(Doc, From, To, Keys_list, On_conflict) ->
    gleam@result:'try'(
        molt@internal@path:parse(From),
        fun(From_segments) ->
            gleam@result:'try'(
                molt@internal@path:parse(To),
                fun(To_segments) ->
                    do_move_keys(
                        Doc,
                        From_segments,
                        To_segments,
                        Keys_list,
                        On_conflict
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/reshape.gleam", 248).
?DOC(false).
-spec merge(
    molt@types:document(),
    binary(),
    binary(),
    molt@ops:conflict_strategy()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
merge(Doc, From, Into, On_conflict) ->
    gleam@result:'try'(
        molt@internal@path:parse(From),
        fun(From_segments) ->
            gleam@result:'try'(
                molt@internal@path:parse(Into),
                fun(To_segments) ->
                    molt@internal@document@index:with_index(
                        Doc,
                        fun(Idx) ->
                            case molt@internal@document@index:resolve(
                                Idx,
                                From_segments
                            ) of
                                {hit, _, {index_table, Children}} ->
                                    gleam@result:'try'(
                                        do_move_keys(
                                            Doc,
                                            From_segments,
                                            To_segments,
                                            Children,
                                            On_conflict
                                        ),
                                        fun(Doc2) ->
                                            molt@internal@document@index:with_index(
                                                Doc2,
                                                fun(Idx2) ->
                                                    molt@internal@document@remove:prune(
                                                        Doc2,
                                                        Idx2,
                                                        From_segments
                                                    )
                                                end
                                            )
                                        end
                                    );

                                {hit, _, {index_implicit_table, Children}} ->
                                    gleam@result:'try'(
                                        do_move_keys(
                                            Doc,
                                            From_segments,
                                            To_segments,
                                            Children,
                                            On_conflict
                                        ),
                                        fun(Doc2) ->
                                            molt@internal@document@index:with_index(
                                                Doc2,
                                                fun(Idx2) ->
                                                    molt@internal@document@remove:prune(
                                                        Doc2,
                                                        Idx2,
                                                        From_segments
                                                    )
                                                end
                                            )
                                        end
                                    );

                                {hit,
                                    _,
                                    {index_array_of_tables_entry,
                                        _,
                                        _,
                                        Children}} ->
                                    gleam@result:'try'(
                                        do_move_keys(
                                            Doc,
                                            From_segments,
                                            To_segments,
                                            Children,
                                            On_conflict
                                        ),
                                        fun(Doc2) ->
                                            molt@internal@document@index:with_index(
                                                Doc2,
                                                fun(Idx2) ->
                                                    molt@internal@document@remove:prune(
                                                        Doc2,
                                                        Idx2,
                                                        From_segments
                                                    )
                                                end
                                            )
                                        end
                                    );

                                {hit, _, Entry} ->
                                    {error,
                                        {type_mismatch,
                                            {some, From},
                                            <<"table"/utf8>>,
                                            molt@internal@utils:index_entry_to_string(
                                                Entry
                                            )}};

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

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