Skip to main content

src/molt@internal@document@index.erl

-module(molt@internal@document@index).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/document/index.gleam").
-export([path_to_index_key/1, build_tree_index/1, get_index/1, with_index/2, has_key/2, has_path/2, get/2, get_path/2, key_to_path/1, resolve/2, insertion_site/4, rename_key/2, root_children/1, find_deepest_ancestor_entry/2, entry_for_value/2, entry_for_element/2, insert_entry/3, remove_subtree/2, prune_empty_implicit_ancestors/2, update_entry/3, ensure_implicit_tables/2, build_doc_index/1, relocate_subtree/3, resolve_negative_indices/2]).
-export_type([resolution/0, insertion_site/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 resolution() :: {hit,
        list(molt@types:path_segment()),
        molt@types:index_entry()} |
    {miss,
        list(molt@types:path_segment()),
        molt@types:index_entry(),
        list(molt@types:path_segment())} |
    {fresh, list(molt@types:path_segment())}.

-type insertion_site() :: {concrete_append, list(molt@types:path_segment())} |
    {root_dotted_key, list(molt@types:path_segment())} |
    {inline_descend, list(molt@types:path_segment())}.

-file("src/molt/internal/document/index.gleam", 830).
?DOC(false).
-spec is_key_segment(molt@types:path_segment()) -> boolean().
is_key_segment(Seg) ->
    case Seg of
        {key_segment, _} ->
            true;

        _ ->
            false
    end.

-file("src/molt/internal/document/index.gleam", 782).
?DOC(false).
-spec add_canonical_aot_entries(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
add_canonical_aot_entries(Idx, Raw) ->
    Canonical_counts = gleam@dict:fold(
        Raw,
        maps:new(),
        fun(Acc, Key, Entry) -> case Entry of
                {index_array_of_tables, _, _} ->
                    {index_key, Key@1} = Key,
                    gleam@bool:guard(
                        not molt@internal@path:contains_index(Key@1),
                        Acc,
                        fun() ->
                            Canonical_key = begin
                                _pipe = gleam@list:filter(
                                    Key@1,
                                    fun is_key_segment/1
                                ),
                                {index_key, _pipe}
                            end,
                            gleam@dict:upsert(
                                Acc,
                                Canonical_key,
                                fun(V) -> gleam@option:unwrap(V, 0) + 1 end
                            )
                        end
                    );

                _ ->
                    Acc
            end end
    ),
    gleam@dict:fold(Raw, Idx, fun(Idx@1, Key@2, Entry@1) -> case Entry@1 of
                {index_array_of_tables, _, _} ->
                    {index_key, Key@3} = Key@2,
                    gleam@bool:guard(
                        not molt@internal@path:contains_index(Key@3),
                        Idx@1,
                        fun() ->
                            Canonical_key@1 = begin
                                _pipe@1 = gleam@list:filter(
                                    Key@3,
                                    fun is_key_segment/1
                                ),
                                {index_key, _pipe@1}
                            end,
                            gleam@bool:guard(
                                gleam_stdlib:map_get(
                                    Canonical_counts,
                                    Canonical_key@1
                                )
                                /= {ok, 1},
                                Idx@1,
                                fun() ->
                                    gleam@dict:upsert(
                                        Idx@1,
                                        Canonical_key@1,
                                        fun(V@1) ->
                                            gleam@option:unwrap(V@1, Entry@1)
                                        end
                                    )
                                end
                            )
                        end
                    );

                _ ->
                    Idx@1
            end end).

-file("src/molt/internal/document/index.gleam", 747).
?DOC(false).
-spec entry_add_child(molt@types:index_entry(), binary()) -> molt@types:index_entry().
entry_add_child(Entry, Child) ->
    case Entry of
        {index_table, Children} ->
            case gleam@list:contains(Children, Child) of
                true ->
                    Entry;

                false ->
                    {index_table, [Child | Children]}
            end;

        {index_implicit_table, Children@1} ->
            case gleam@list:contains(Children@1, Child) of
                true ->
                    Entry;

                false ->
                    {index_implicit_table, [Child | Children@1]}
            end;

        {index_array_of_tables, Count, Children@2} ->
            case gleam@list:contains(Children@2, Child) of
                true ->
                    Entry;

                false ->
                    {index_array_of_tables, Count, [Child | Children@2]}
            end;

        {index_array_of_tables_entry, Parent, I, Children@3} ->
            case gleam@list:contains(Children@3, Child) of
                true ->
                    Entry;

                false ->
                    {index_array_of_tables_entry,
                        Parent,
                        I,
                        [Child | Children@3]}
            end;

        _ ->
            Entry
    end.

-file("src/molt/internal/document/index.gleam", 731).
?DOC(false).
-spec add_child_to_entry(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key(),
    binary()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
add_child_to_entry(Idx, Parent, Child) ->
    case Parent of
        {index_key, []} ->
            Idx;

        _ ->
            case gleam_stdlib:map_get(Idx, Parent) of
                {ok, Entry} ->
                    gleam@dict:insert(
                        Idx,
                        Parent,
                        entry_add_child(Entry, Child)
                    );

                {error, nil} ->
                    Idx
            end
    end.

-file("src/molt/internal/document/index.gleam", 708).
?DOC(false).
-spec enrich_children(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
enrich_children(Raw) ->
    _pipe = gleam@dict:fold(Raw, Raw, fun(Idx, Key, _) -> case Key of
                {index_key, [{key_segment, Child}]} ->
                    add_child_to_entry(Idx, {index_key, []}, Child);

                {index_key, [{key_segment, Child@1} | Parent]} ->
                    Parent@1 = {index_key, Parent},
                    gleam@bool:guard(
                        not gleam@dict:has_key(Idx, Parent@1),
                        Idx,
                        fun() -> add_child_to_entry(Idx, Parent@1, Child@1) end
                    );

                _ ->
                    Idx
            end end),
    add_canonical_aot_entries(_pipe, Raw).

-file("src/molt/internal/document/index.gleam", 160).
?DOC(false).
-spec path_to_index_key(list(molt@types:path_segment())) -> molt@types:index_key().
path_to_index_key(Path) ->
    {index_key, lists:reverse(Path)}.

-file("src/molt/internal/document/index.gleam", 690).
?DOC(false).
-spec classify_value_tokens(
    list(greenwood:element(molt@types:toml_kind())),
    list(molt@types:path_segment())
) -> molt@types:index_entry().
classify_value_tokens(Nodes, Container) ->
    case Nodes of
        [] ->
            {index_scalar_value, Container};

        [{token_element, {token, whitespace, _}} | Rest] ->
            classify_value_tokens(Rest, Container);

        [{token_element, {token, newline, _}} | Rest] ->
            classify_value_tokens(Rest, Container);

        [{node_element, N} | _] when erlang:element(2, N) =:= inline_table ->
            {index_inline_table_value, Container};

        [{node_element, N@1} | _] when erlang:element(2, N@1) =:= array ->
            {index_array_value, Container};

        _ ->
            {index_scalar_value, Container}
    end.

-file("src/molt/internal/document/index.gleam", 682).
?DOC(false).
-spec classify_kv_value(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment())
) -> molt@types:index_entry().
classify_kv_value(Kv, Container) ->
    _pipe = molt@internal@cst@elements:value_tokens(erlang:element(3, Kv)),
    classify_value_tokens(_pipe, Container).

-file("src/molt/internal/document/index.gleam", 625).
?DOC(false).
-spec register_dotted_parents(
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
register_dotted_parents(Path, Segments, Index) ->
    case Segments of
        [] ->
            Index;

        [_] ->
            Index;

        _ ->
            Prefix_segments = molt@internal@utils:all_prefixes(Segments),
            gleam@list:fold(
                Prefix_segments,
                Index,
                fun(Index@1, Prefix) ->
                    Full = lists:append(Path, Prefix),
                    Key = path_to_index_key(Full),
                    case gleam_stdlib:map_get(Index@1, Key) of
                        {error, nil} ->
                            gleam@dict:insert(
                                Index@1,
                                Key,
                                {index_implicit_table, []}
                            );

                        {ok, _} ->
                            Index@1
                    end
                end
            )
    end.

-file("src/molt/internal/document/index.gleam", 606).
?DOC(false).
-spec index_kv_node(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
index_kv_node(Kv, Path, Index) ->
    case molt@internal@cst@elements:key_path(erlang:element(3, Kv)) of
        none ->
            Index;

        {some, Segments} ->
            Key_segments = gleam@list:map(
                Segments,
                fun(Field@0) -> {key_segment, Field@0} end
            ),
            Full_path = lists:append(Path, Key_segments),
            Index@1 = register_dotted_parents(Path, Key_segments, Index),
            Entry = classify_kv_value(Kv, Path),
            gleam@dict:insert(Index@1, path_to_index_key(Full_path), Entry)
    end.

-file("src/molt/internal/document/index.gleam", 473).
?DOC(false).
-spec index_table_kvs(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(greenwood:element(molt@types:toml_kind())),
    list(molt@types:path_segment())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
index_table_kvs(Index, Nodes, Path) ->
    gleam@list:fold(Nodes, Index, fun(Index@1, Node) -> case Node of
                {node_element, Kv} when erlang:element(2, Kv) =:= key_value ->
                    index_kv_node(Kv, Path, Index@1);

                _ ->
                    Index@1
            end end).

-file("src/molt/internal/document/index.gleam", 670).
?DOC(false).
-spec register_header_parents(
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
register_header_parents(Path, Index) ->
    Prefixes = molt@internal@utils:all_prefixes(Path),
    gleam@list:fold(
        Prefixes,
        Index,
        fun(Index@1, Prefix) ->
            Key = path_to_index_key(Prefix),
            case gleam_stdlib:map_get(Index@1, Key) of
                {error, nil} ->
                    gleam@dict:insert(Index@1, Key, {index_implicit_table, []});

                {ok, _} ->
                    Index@1
            end
        end
    ).

-file("src/molt/internal/document/index.gleam", 655).
?DOC(false).
-spec register_array_of_tables_path(
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
register_array_of_tables_path(Path, Index) ->
    Index@1 = register_header_parents(Path, Index),
    Key = path_to_index_key(Path),
    Count = case gleam_stdlib:map_get(Index@1, Key) of
        {ok, {index_array_of_tables, C, _}} ->
            C + 1;

        _ ->
            1
    end,
    gleam@dict:insert(Index@1, Key, {index_array_of_tables, Count, []}).

-file("src/molt/internal/document/index.gleam", 542).
?DOC(false).
-spec do_resolve_header_path(
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> list(molt@types:path_segment()).
do_resolve_header_path(Remaining, Built, Index) ->
    case Remaining of
        [] ->
            Built;

        [Segment] ->
            lists:append(Built, [Segment]);

        [Segment@1 | Rest] ->
            Candidate = lists:append(Built, [Segment@1]),
            case gleam_stdlib:map_get(Index, path_to_index_key(Candidate)) of
                {ok, {index_array_of_tables, Count, _}} ->
                    With_index = lists:append(
                        Candidate,
                        [{index_segment, Count - 1}]
                    ),
                    do_resolve_header_path(Rest, With_index, Index);

                _ ->
                    do_resolve_header_path(Rest, Candidate, Index)
            end
    end.

-file("src/molt/internal/document/index.gleam", 538).
?DOC(false).
-spec resolve_header_path(
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> list(molt@types:path_segment()).
resolve_header_path(Key_path, Index) ->
    do_resolve_header_path(Key_path, [], Index).

-file("src/molt/internal/document/index.gleam", 648).
?DOC(false).
-spec register_table_path(
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
register_table_path(Path, Index) ->
    Index@1 = register_header_parents(Path, Index),
    gleam@dict:insert(Index@1, path_to_index_key(Path), {index_table, []}).

-file("src/molt/internal/document/index.gleam", 486).
?DOC(false).
-spec index_tables(
    list(greenwood:element(molt@types:toml_kind())),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
index_tables(Nodes, Index) ->
    gleam@list:fold(Nodes, Index, fun(Index@1, Node) -> case Node of
                {node_element, N} when erlang:element(2, N) =:= table ->
                    Path = begin
                        _pipe = gleam@list:map(
                            molt@internal@cst@elements:extract_key_segments(
                                erlang:element(3, N)
                            ),
                            fun(Field@0) -> {key_segment, Field@0} end
                        ),
                        resolve_header_path(_pipe, Index@1)
                    end,
                    index_table_kvs(
                        register_table_path(Path, Index@1),
                        erlang:element(3, N),
                        Path
                    );

                {node_element, N@1} when erlang:element(2, N@1) =:= array_of_tables ->
                    Path@1 = begin
                        _pipe@1 = gleam@list:map(
                            molt@internal@cst@elements:extract_key_segments(
                                erlang:element(3, N@1)
                            ),
                            fun(Field@0) -> {key_segment, Field@0} end
                        ),
                        resolve_header_path(_pipe@1, Index@1)
                    end,
                    Index@2 = register_array_of_tables_path(Path@1, Index@1),
                    Entry_index = case gleam_stdlib:map_get(
                        Index@2,
                        path_to_index_key(Path@1)
                    ) of
                        {ok, {index_array_of_tables, Count, _}} ->
                            Count - 1;

                        _ ->
                            0
                    end,
                    Segment = begin
                        _pipe@2 = {index_segment, Entry_index},
                        gleam@list:wrap(_pipe@2)
                    end,
                    Entry_path = lists:append(Path@1, Segment),
                    Index@3 = gleam@dict:insert(
                        Index@2,
                        path_to_index_key(Entry_path),
                        {index_array_of_tables_entry, Path@1, Entry_index, []}
                    ),
                    index_table_kvs(Index@3, erlang:element(3, N@1), Entry_path);

                _ ->
                    Index@1
            end end).

-file("src/molt/internal/document/index.gleam", 57).
?DOC(false).
-spec build_tree_index(greenwood:node_(molt@types:toml_kind())) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
build_tree_index(Tree) ->
    _pipe = index_table_kvs(maps:new(), erlang:element(3, Tree), []),
    _pipe@1 = index_tables(erlang:element(3, Tree), _pipe),
    enrich_children(_pipe@1).

-file("src/molt/internal/document/index.gleam", 837).
?DOC(false).
-spec ensure_index(molt@types:document()) -> {ok, molt@types:document()} |
    {error, molt@error:molt_error()}.
ensure_index(Doc) ->
    gleam@bool:guard(
        erlang:element(3, Doc) > 0,
        {error, invalid_document},
        fun() ->
            gleam@bool:guard(
                erlang:element(5, Doc) /= none,
                {ok, Doc},
                fun() ->
                    {ok,
                        {document,
                            erlang:element(2, Doc),
                            erlang:element(3, Doc),
                            erlang:element(4, Doc),
                            {some, build_tree_index(erlang:element(4, Doc))}}}
                end
            )
        end
    ).

-file("src/molt/internal/document/index.gleam", 73).
?DOC(false).
-spec get_index(molt@types:document()) -> {ok,
        gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())} |
    {error, molt@error:molt_error()}.
get_index(Doc) ->
    gleam@result:'try'(
        ensure_index(Doc),
        fun(Doc@1) ->
            {ok, gleam@option:unwrap(erlang:element(5, Doc@1), maps:new())}
        end
    ).

-file("src/molt/internal/document/index.gleam", 65).
?DOC(false).
-spec with_index(
    molt@types:document(),
    fun((gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())) -> {ok,
            OFP} |
        {error, molt@error:molt_error()})
) -> {ok, OFP} | {error, molt@error:molt_error()}.
with_index(Doc, Callback) ->
    gleam@result:'try'(get_index(Doc), fun(Index) -> Callback(Index) end).

-file("src/molt/internal/document/index.gleam", 82).
?DOC(false).
-spec has_key(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> boolean().
has_key(Index, Key) ->
    gleam@dict:has_key(Index, Key).

-file("src/molt/internal/document/index.gleam", 78).
?DOC(false).
-spec has_path(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> boolean().
has_path(Index, Path) ->
    has_key(Index, path_to_index_key(Path)).

-file("src/molt/internal/document/index.gleam", 93).
?DOC(false).
-spec get(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> {ok, molt@types:index_entry()} | {error, nil}.
get(Index, Key) ->
    gleam_stdlib:map_get(Index, Key).

-file("src/molt/internal/document/index.gleam", 86).
?DOC(false).
-spec get_path(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> {ok, molt@types:index_entry()} | {error, nil}.
get_path(Index, Path) ->
    get(Index, path_to_index_key(Path)).

-file("src/molt/internal/document/index.gleam", 165).
?DOC(false).
-spec key_to_path(molt@types:index_key()) -> list(molt@types:path_segment()).
key_to_path(Key) ->
    {index_key, Reversed} = Key,
    lists:reverse(Reversed).

-file("src/molt/internal/document/index.gleam", 847).
?DOC(false).
-spec parent_key(molt@types:index_key()) -> {ok, molt@types:index_key()} |
    {error, nil}.
parent_key(Key) ->
    case Key of
        {index_key, [_ | Rest]} ->
            {ok, {index_key, Rest}};

        {index_key, []} ->
            {error, nil}
    end.

-file("src/molt/internal/document/index.gleam", 879).
?DOC(false).
-spec find_deepest_ancestor(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> {ok, {molt@types:index_key(), molt@types:index_entry()}} | {error, nil}.
find_deepest_ancestor(Idx, Key) ->
    case gleam_stdlib:map_get(Idx, Key) of
        {ok, Entry} ->
            {ok, {Key, Entry}};

        {error, nil} ->
            case parent_key(Key) of
                {ok, Parent} ->
                    find_deepest_ancestor(Idx, Parent);

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

-file("src/molt/internal/document/index.gleam", 101).
?DOC(false).
-spec resolve(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> resolution().
resolve(Idx, Path) ->
    Key = path_to_index_key(Path),
    case get(Idx, Key) of
        {ok, Entry} ->
            {hit, Path, Entry};

        {error, nil} ->
            case find_deepest_ancestor(Idx, Key) of
                {ok, {Ancestor_key, Ancestor}} ->
                    Ancestor_path = key_to_path(Ancestor_key),
                    Tail = gleam@list:drop(Path, erlang:length(Ancestor_path)),
                    {miss, Ancestor_path, Ancestor, Tail};

                {error, nil} ->
                    {fresh, Path}
            end
    end.

-file("src/molt/internal/document/index.gleam", 857).
?DOC(false).
-spec find_concrete_ancestor_site(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> insertion_site().
find_concrete_ancestor_site(Idx, Implicit_path, Full_path) ->
    Parent = gleam@list:take(Implicit_path, erlang:length(Implicit_path) - 1),
    case Parent of
        [] ->
            {root_dotted_key, Full_path};

        _ ->
            case get_path(Idx, Parent) of
                {ok, {index_table, _}} ->
                    {concrete_append, Parent};

                {ok, {index_array_of_tables_entry, _, _, _}} ->
                    {concrete_append, Parent};

                {ok, {index_implicit_table, _}} ->
                    find_concrete_ancestor_site(Idx, Parent, Full_path);

                _ ->
                    {root_dotted_key, Full_path}
            end
    end.

-file("src/molt/internal/document/index.gleam", 122).
?DOC(false).
-spec insertion_site(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    molt@types:index_entry(),
    list(molt@types:path_segment())
) -> {ok, insertion_site()} | {error, molt@error:molt_error()}.
insertion_site(Idx, Ancestor_path, Ancestor, Full_path) ->
    case Ancestor of
        {index_table, _} ->
            {ok, {concrete_append, Ancestor_path}};

        {index_array_of_tables_entry, _, _, _} ->
            {ok, {concrete_append, Ancestor_path}};

        {index_implicit_table, _} ->
            {ok, find_concrete_ancestor_site(Idx, Ancestor_path, Full_path)};

        {index_scalar_value, _} ->
            {ok, {inline_descend, Full_path}};

        {index_array_value, _} ->
            {ok, {inline_descend, Full_path}};

        {index_inline_table_value, _} ->
            {ok, {inline_descend, Full_path}};

        {index_array_of_tables, _, _} ->
            {error,
                {type_mismatch,
                    {some, molt@internal@path:to_string(Ancestor_path)},
                    <<"a table (use an index to select an entry)"/utf8>>,
                    <<"array of tables"/utf8>>}}
    end.

-file("src/molt/internal/document/index.gleam", 152).
?DOC(false).
-spec rename_key(list(molt@types:path_segment()), binary()) -> molt@types:index_key().
rename_key(Path, To) ->
    case path_to_index_key(Path) of
        {index_key, [_ | Rest]} ->
            {index_key, [{key_segment, To} | Rest]};

        Key ->
            Key
    end.

-file("src/molt/internal/document/index.gleam", 171).
?DOC(false).
-spec root_children(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> list(binary()).
root_children(Idx) ->
    gleam@dict:fold(Idx, [], fun(Acc, Key, _) -> case Key of
                {index_key, [{key_segment, Name}]} ->
                    [Name | Acc];

                _ ->
                    Acc
            end end).

-file("src/molt/internal/document/index.gleam", 183).
?DOC(false).
-spec find_deepest_ancestor_entry(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> {ok, molt@types:index_entry()} | {error, nil}.
find_deepest_ancestor_entry(Idx, Key) ->
    gleam@result:'try'(
        find_deepest_ancestor(Idx, Key),
        fun(_use0) ->
            {_, Entry} = _use0,
            {ok, Entry}
        end
    ).

-file("src/molt/internal/document/index.gleam", 195).
?DOC(false).
-spec entry_for_value(molt@value:value(), list(molt@types:path_segment())) -> molt@types:index_entry().
entry_for_value(Val, Container) ->
    case molt@value:type_of(Val) of
        <<"array"/utf8>> ->
            {index_array_value, Container};

        <<"inline_table"/utf8>> ->
            {index_inline_table_value, Container};

        <<"table"/utf8>> ->
            {index_inline_table_value, Container};

        _ ->
            {index_scalar_value, Container}
    end.

-file("src/molt/internal/document/index.gleam", 209).
?DOC(false).
-spec entry_for_element(
    greenwood:element(molt@types:toml_kind()),
    list(molt@types:path_segment())
) -> molt@types:index_entry().
entry_for_element(El, Container) ->
    case El of
        {node_element, N} when erlang:element(2, N) =:= inline_table ->
            {index_inline_table_value, Container};

        {node_element, N@1} when erlang:element(2, N@1) =:= array ->
            {index_array_value, Container};

        _ ->
            {index_scalar_value, Container}
    end.

-file("src/molt/internal/document/index.gleam", 317).
?DOC(false).
-spec add_child_to_parent(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
add_child_to_parent(Idx, Key) ->
    case Key of
        {index_key, [{key_segment, Child} | Parent_rev]} ->
            Parent = {index_key, Parent_rev},
            case gleam_stdlib:map_get(Idx, Parent) of
                {ok, Entry} ->
                    gleam@dict:insert(
                        Idx,
                        Parent,
                        entry_add_child(Entry, Child)
                    );

                {error, nil} ->
                    Idx
            end;

        _ ->
            Idx
    end.

-file("src/molt/internal/document/index.gleam", 222).
?DOC(false).
-spec insert_entry(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key(),
    molt@types:index_entry()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
insert_entry(Idx, Key, Entry) ->
    Idx@1 = gleam@dict:insert(Idx, Key, Entry),
    add_child_to_parent(Idx@1, Key).

-file("src/molt/internal/document/index.gleam", 311).
?DOC(false).
-spec has_suffix(
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> boolean().
has_suffix(Longer, Suffix) ->
    Drop_count = erlang:length(Longer) - erlang:length(Suffix),
    gleam@list:drop(Longer, Drop_count) =:= Suffix.

-file("src/molt/internal/document/index.gleam", 302).
?DOC(false).
-spec is_descendant_of(molt@types:index_key(), list(molt@types:path_segment())) -> boolean().
is_descendant_of(Child, Parent_rev) ->
    {index_key, Child_rev} = Child,
    (erlang:length(Child_rev) > erlang:length(Parent_rev)) andalso has_suffix(
        Child_rev,
        Parent_rev
    ).

-file("src/molt/internal/document/index.gleam", 350).
?DOC(false).
-spec entry_remove_child(molt@types:index_entry(), binary()) -> molt@types:index_entry().
entry_remove_child(Entry, Child) ->
    case Entry of
        {index_table, Children} ->
            {index_table, gleam@list:filter(Children, fun(C) -> C /= Child end)};

        {index_implicit_table, Children@1} ->
            {index_implicit_table,
                gleam@list:filter(Children@1, fun(C@1) -> C@1 /= Child end)};

        {index_array_of_tables, Count, Children@2} ->
            {index_array_of_tables,
                Count,
                gleam@list:filter(Children@2, fun(C@2) -> C@2 /= Child end)};

        {index_array_of_tables_entry, Parent, I, Children@3} ->
            {index_array_of_tables_entry,
                Parent,
                I,
                gleam@list:filter(Children@3, fun(C@3) -> C@3 /= Child end)};

        _ ->
            Entry
    end.

-file("src/molt/internal/document/index.gleam", 334).
?DOC(false).
-spec remove_child_from_parent(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
remove_child_from_parent(Idx, Key) ->
    case Key of
        {index_key, [{key_segment, Child} | Parent_rev]} ->
            Parent = {index_key, Parent_rev},
            case gleam_stdlib:map_get(Idx, Parent) of
                {ok, Entry} ->
                    gleam@dict:insert(
                        Idx,
                        Parent,
                        entry_remove_child(Entry, Child)
                    );

                {error, nil} ->
                    Idx
            end;

        _ ->
            Idx
    end.

-file("src/molt/internal/document/index.gleam", 232).
?DOC(false).
-spec remove_subtree(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
remove_subtree(Idx, Key) ->
    Idx@1 = gleam@dict:delete(Idx, Key),
    Idx@2 = remove_child_from_parent(Idx@1, Key),
    {index_key, Prefix_rev} = Key,
    gleam@dict:fold(
        Idx@2,
        Idx@2,
        fun(Acc, Candidate, _) ->
            case is_descendant_of(Candidate, Prefix_rev) of
                true ->
                    gleam@dict:delete(Acc, Candidate);

                false ->
                    Acc
            end
        end
    ).

-file("src/molt/internal/document/index.gleam", 251).
?DOC(false).
-spec prune_empty_implicit_ancestors(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
prune_empty_implicit_ancestors(Idx, Key) ->
    case parent_key(Key) of
        {error, nil} ->
            Idx;

        {ok, Parent} ->
            case gleam_stdlib:map_get(Idx, Parent) of
                {ok, {index_implicit_table, []}} ->
                    Idx@1 = gleam@dict:delete(Idx, Parent),
                    Idx@2 = remove_child_from_parent(Idx@1, Parent),
                    prune_empty_implicit_ancestors(Idx@2, Parent);

                _ ->
                    Idx
            end
    end.

-file("src/molt/internal/document/index.gleam", 270).
?DOC(false).
-spec update_entry(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key(),
    molt@types:index_entry()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
update_entry(Idx, Key, Entry) ->
    gleam@dict:insert(Idx, Key, Entry).

-file("src/molt/internal/document/index.gleam", 280).
?DOC(false).
-spec ensure_implicit_tables(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
ensure_implicit_tables(Idx, Path) ->
    Prefixes = molt@internal@utils:all_prefixes(Path),
    gleam@list:fold(
        Prefixes,
        Idx,
        fun(Idx@1, Prefix) ->
            Key = path_to_index_key(Prefix),
            case gleam_stdlib:map_get(Idx@1, Key) of
                {error, nil} ->
                    insert_entry(Idx@1, Key, {index_implicit_table, []});

                {ok, _} ->
                    Idx@1
            end
        end
    ).

-file("src/molt/internal/document/index.gleam", 296).
?DOC(false).
-spec build_doc_index(molt@types:document()) -> gleam@option:option(gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())).
build_doc_index(Doc) ->
    {some, build_tree_index(erlang:element(4, Doc))}.

-file("src/molt/internal/document/index.gleam", 451).
?DOC(false).
-spec remap_container(
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> list(molt@types:path_segment()).
remap_container(Container, From_path, To_path) ->
    From_len = erlang:length(From_path),
    Container_len = erlang:length(Container),
    case gleam@list:take(Container, From_len) =:= From_path of
        true ->
            lists:append(To_path, gleam@list:drop(Container, From_len));

        false ->
            case (From_len >= Container_len) andalso (gleam@list:take(
                From_path,
                Container_len
            )
            =:= Container) of
                true ->
                    gleam@list:take(To_path, Container_len);

                false ->
                    Container
            end
    end.

-file("src/molt/internal/document/index.gleam", 421).
?DOC(false).
-spec relocate_container(
    molt@types:index_entry(),
    list(molt@types:path_segment()),
    list(molt@types:path_segment())
) -> molt@types:index_entry().
relocate_container(Entry, From_path, To_path) ->
    case Entry of
        {index_scalar_value, Container} ->
            {index_scalar_value, remap_container(Container, From_path, To_path)};

        {index_array_value, Container@1} ->
            {index_array_value,
                remap_container(Container@1, From_path, To_path)};

        {index_inline_table_value, Container@2} ->
            {index_inline_table_value,
                remap_container(Container@2, From_path, To_path)};

        _ ->
            Entry
    end.

-file("src/molt/internal/document/index.gleam", 378).
?DOC(false).
-spec relocate_subtree(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    molt@types:index_key(),
    molt@types:index_key()
) -> gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()).
relocate_subtree(Idx, From, To) ->
    {index_key, From_rev} = From,
    {index_key, To_rev} = To,
    From_len = erlang:length(From_rev),
    From_path = key_to_path(From),
    To_path = key_to_path(To),
    To_move = gleam@dict:fold(
        Idx,
        [],
        fun(Acc, Key, Entry) -> case Key =:= From of
                true ->
                    [{To, relocate_container(Entry, From_path, To_path)} | Acc];

                false ->
                    case is_descendant_of(Key, From_rev) of
                        true ->
                            {index_key, Key_rev} = Key,
                            Suffix = gleam@list:take(
                                Key_rev,
                                erlang:length(Key_rev) - From_len
                            ),
                            New_key = {index_key, lists:append(Suffix, To_rev)},
                            [{New_key,
                                    relocate_container(
                                        Entry,
                                        From_path,
                                        To_path
                                    )} |
                                Acc];

                        false ->
                            Acc
                    end
            end end
    ),
    Idx@1 = remove_subtree(Idx, From),
    gleam@list:fold(
        To_move,
        Idx@1,
        fun(Idx@2, Pair) ->
            {Key@1, Entry@1} = Pair,
            insert_entry(Idx@2, Key@1, Entry@1)
        end
    ).

-file("src/molt/internal/document/index.gleam", 579).
?DOC(false).
-spec do_resolve_negative_indices(
    list(molt@types:path_segment()),
    list(molt@types:path_segment()),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry())
) -> list(molt@types:path_segment()).
do_resolve_negative_indices(Remaining, Built, Index) ->
    case Remaining of
        [] ->
            Built;

        [{index_segment, I} | Rest] ->
            Resolved = case {I < 0,
                gleam_stdlib:map_get(Index, path_to_index_key(Built))} of
                {true, {ok, {index_array_of_tables, Count, _}}} ->
                    Count + I;

                {_, _} ->
                    I
            end,
            do_resolve_negative_indices(
                Rest,
                lists:append(Built, [{index_segment, Resolved}]),
                Index
            );

        [Segment | Rest@1] ->
            do_resolve_negative_indices(
                Rest@1,
                lists:append(Built, [Segment]),
                Index
            )
    end.

-file("src/molt/internal/document/index.gleam", 572).
?DOC(false).
-spec resolve_negative_indices(
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment())
) -> list(molt@types:path_segment()).
resolve_negative_indices(Idx, Path) ->
    do_resolve_negative_indices(Path, [], Idx).