Skip to main content

src/molt@internal@cst@insert.erl

-module(molt@internal@cst@insert).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/cst/insert.gleam").
-export([place/4, table_ordered/3, ensure_table/2, ensure_array_of_tables/2]).
-export_type([placement/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 placement() :: kv_region_end |
    {before_kv_key, binary()} |
    {family_scope_end, list(binary())} |
    {before_family_index, list(binary()), integer()}.

-file("src/molt/internal/cst/insert.gleam", 147).
?DOC(false).
-spec do_insert_before_index(
    list(greenwood:element(molt@types:toml_kind())),
    list(binary()),
    integer(),
    greenwood:node_(molt@types:toml_kind()),
    integer(),
    list(greenwood:element(molt@types:toml_kind()))
) -> list(greenwood:element(molt@types:toml_kind())).
do_insert_before_index(Children, Segments, Index, Entry, Current, Acc) ->
    case Children of
        [] ->
            lists:reverse([{node_element, Entry} | Acc]);

        [{node_element, N} = El | Rest] when erlang:element(2, N) =:= array_of_tables ->
            case molt@internal@cst@elements:extract_key_segments(
                erlang:element(3, N)
            )
            =:= Segments of
                true when Current =:= Index ->
                    lists:append(
                        lists:reverse(Acc),
                        [{node_element, Entry}, El | Rest]
                    );

                true ->
                    do_insert_before_index(
                        Rest,
                        Segments,
                        Index,
                        Entry,
                        Current + 1,
                        [El | Acc]
                    );

                false ->
                    do_insert_before_index(
                        Rest,
                        Segments,
                        Index,
                        Entry,
                        Current,
                        [El | Acc]
                    )
            end;

        [El@1 | Rest@1] ->
            do_insert_before_index(
                Rest@1,
                Segments,
                Index,
                Entry,
                Current,
                [El@1 | Acc]
            )
    end.

-file("src/molt/internal/cst/insert.gleam", 291).
?DOC(false).
-spec do_insert_after_path_scope(
    list(greenwood:element(molt@types:toml_kind())),
    greenwood:node_(molt@types:toml_kind()),
    list(binary()),
    boolean(),
    list(greenwood:element(molt@types:toml_kind()))
) -> list(greenwood:element(molt@types:toml_kind())).
do_insert_after_path_scope(Children, New_header, Prefix, Saw_in_scope, Acc) ->
    case Children of
        [] ->
            lists:reverse([{node_element, New_header} | Acc]);

        [{node_element, N} = El | Rest] when (erlang:element(2, N) =:= table) orelse (erlang:element(
            2,
            N
        ) =:= array_of_tables) ->
            Header_path = molt@internal@cst@elements:extract_key_segments(
                erlang:element(3, N)
            ),
            case gleam@list:take(Header_path, erlang:length(Prefix)) =:= Prefix of
                true ->
                    do_insert_after_path_scope(
                        Rest,
                        New_header,
                        Prefix,
                        true,
                        [El | Acc]
                    );

                false ->
                    case Saw_in_scope of
                        true ->
                            _pipe = lists:reverse(
                                [{node_element, New_header} | Acc]
                            ),
                            lists:append(_pipe, [El | Rest]);

                        false ->
                            do_insert_after_path_scope(
                                Rest,
                                New_header,
                                Prefix,
                                false,
                                [El | Acc]
                            )
                    end
            end;

        [El@1 | Rest@1] ->
            do_insert_after_path_scope(
                Rest@1,
                New_header,
                Prefix,
                Saw_in_scope,
                [El@1 | Acc]
            )
    end.

-file("src/molt/internal/cst/insert.gleam", 277).
?DOC(false).
-spec insert_after_path_scope(
    list(greenwood:element(molt@types:toml_kind())),
    greenwood:node_(molt@types:toml_kind()),
    list(binary())
) -> list(greenwood:element(molt@types:toml_kind())).
insert_after_path_scope(Children, New_header, Prefix) ->
    do_insert_after_path_scope(Children, New_header, Prefix, false, []).

-file("src/molt/internal/cst/insert.gleam", 128).
?DOC(false).
-spec do_insert_before_key(
    list(greenwood:element(molt@types:toml_kind())),
    binary(),
    greenwood:node_(molt@types:toml_kind()),
    list(greenwood:element(molt@types:toml_kind()))
) -> list(greenwood:element(molt@types:toml_kind())).
do_insert_before_key(Children, Before, Kv, Acc) ->
    case Children of
        [] ->
            lists:reverse([{node_element, Kv} | Acc]);

        [{node_element, N} = El | Rest] when erlang:element(2, N) =:= key_value ->
            case molt@internal@cst@elements:key_name(erlang:element(3, N)) =:= {some,
                Before} of
                true ->
                    lists:append(
                        lists:reverse(Acc),
                        [{node_element, Kv}, El | Rest]
                    );

                false ->
                    do_insert_before_key(Rest, Before, Kv, [El | Acc])
            end;

        [El@1 | Rest@1] ->
            do_insert_before_key(Rest@1, Before, Kv, [El@1 | Acc])
    end.

-file("src/molt/internal/cst/insert.gleam", 192).
?DOC(false).
-spec insert_after_last_kv(
    list(greenwood:element(molt@types:toml_kind())),
    greenwood:node_(molt@types:toml_kind()),
    list(greenwood:element(molt@types:toml_kind()))
) -> list(greenwood:element(molt@types:toml_kind())).
insert_after_last_kv(Children, Kv, Acc) ->
    case Children of
        [] ->
            lists:reverse([{node_element, Kv} | Acc]);

        [{node_element, N} = El | Rest] when (erlang:element(2, N) =:= table) orelse (erlang:element(
            2,
            N
        ) =:= array_of_tables) ->
            lists:append(lists:reverse([{node_element, Kv} | Acc]), [El | Rest]);

        [El@1 | Rest@1] ->
            insert_after_last_kv(Rest@1, Kv, [El@1 | Acc])
    end.

-file("src/molt/internal/cst/insert.gleam", 83).
?DOC(false).
-spec do_insert_kv_in_placement(
    greenwood:node_(molt@types:toml_kind()),
    greenwood:node_(molt@types:toml_kind()),
    placement()
) -> greenwood:node_(molt@types:toml_kind()).
do_insert_kv_in_placement(Container, New, At) ->
    case At of
        kv_region_end ->
            Children = insert_after_last_kv(
                erlang:element(3, Container),
                New,
                []
            ),
            {node,
                erlang:element(2, Container),
                Children,
                erlang:element(4, Container)};

        {before_kv_key, Key} ->
            Children@1 = do_insert_before_key(
                erlang:element(3, Container),
                Key,
                New,
                []
            ),
            {node,
                erlang:element(2, Container),
                Children@1,
                erlang:element(4, Container)};

        {family_scope_end, Family} ->
            New_children = insert_after_path_scope(
                erlang:element(3, Container),
                New,
                Family
            ),
            {node,
                erlang:element(2, Container),
                New_children,
                erlang:element(4, Container)};

        {before_family_index, Family@1, Index} ->
            Children@2 = do_insert_before_index(
                erlang:element(3, Container),
                Family@1,
                Index,
                New,
                0,
                []
            ),
            {node,
                erlang:element(2, Container),
                Children@2,
                erlang:element(4, Container)}
    end.

-file("src/molt/internal/cst/insert.gleam", 32).
?DOC(false).
-spec place(
    greenwood:node_(molt@types:toml_kind()),
    list(molt@types:path_segment()),
    greenwood:node_(molt@types:toml_kind()),
    placement()
) -> {ok, greenwood:node_(molt@types:toml_kind())} |
    {error, molt@error:molt_error()}.
place(Node, Container, New, At) ->
    case Container of
        [] ->
            {ok, do_insert_kv_in_placement(Node, New, At)};

        _ ->
            gleam@result:'try'(
                molt@internal@cst@query:get_cursor(Node, Container),
                fun(Cursor) ->
                    case erlang:element(2, erlang:element(2, Cursor)) of
                        root ->
                            _pipe = greenwood@zipper:map_focus(
                                Cursor,
                                fun(_capture) ->
                                    do_insert_kv_in_placement(_capture, New, At)
                                end
                            ),
                            _pipe@1 = greenwood@zipper:unzip(_pipe),
                            {ok, _pipe@1};

                        table ->
                            _pipe = greenwood@zipper:map_focus(
                                Cursor,
                                fun(_capture) ->
                                    do_insert_kv_in_placement(_capture, New, At)
                                end
                            ),
                            _pipe@1 = greenwood@zipper:unzip(_pipe),
                            {ok, _pipe@1};

                        array_of_tables ->
                            _pipe = greenwood@zipper:map_focus(
                                Cursor,
                                fun(_capture) ->
                                    do_insert_kv_in_placement(_capture, New, At)
                                end
                            ),
                            _pipe@1 = greenwood@zipper:unzip(_pipe),
                            {ok, _pipe@1};

                        _ ->
                            {error,
                                {type_mismatch,
                                    none,
                                    <<"table-node"/utf8>>,
                                    molt@internal@utils:toml_kind(
                                        erlang:element(
                                            2,
                                            erlang:element(2, Cursor)
                                        )
                                    )}}
                    end
                end
            )
    end.

-file("src/molt/internal/cst/insert.gleam", 363).
?DOC(false).
-spec is_child_path(list(binary()), list(binary())) -> boolean().
is_child_path(Parent, Child) ->
    case {Parent, Child} of
        {[], [_ | _]} ->
            true;

        {[P | Prest], [C | Crest]} when P =:= C ->
            is_child_path(Prest, Crest);

        {_, _} ->
            false
    end.

-file("src/molt/internal/cst/insert.gleam", 340).
?DOC(false).
-spec table_path(greenwood:node_(molt@types:toml_kind())) -> {ok,
        list(binary())} |
    {error, nil}.
table_path(Node) ->
    case erlang:element(2, Node) of
        table ->
            {ok,
                molt@internal@cst@elements:extract_key_segments(
                    erlang:element(3, Node)
                )};

        array_of_tables ->
            {ok,
                molt@internal@cst@elements:extract_key_segments(
                    erlang:element(3, Node)
                )};

        _ ->
            {error, nil}
    end.

-file("src/molt/internal/cst/insert.gleam", 355).
?DOC(false).
-spec in_parent_family(greenwood:node_(molt@types:toml_kind()), list(binary())) -> boolean().
in_parent_family(Node, Parent) ->
    case table_path(Node) of
        {ok, P} ->
            (P =:= Parent) orelse is_child_path(Parent, P);

        _ ->
            false
    end.

-file("src/molt/internal/cst/insert.gleam", 348).
?DOC(false).
-spec is_descendant_table(
    greenwood:node_(molt@types:toml_kind()),
    list(binary())
) -> boolean().
is_descendant_table(Node, Parent) ->
    case table_path(Node) of
        {ok, P} ->
            is_child_path(Parent, P);

        _ ->
            false
    end.

-file("src/molt/internal/cst/insert.gleam", 211).
?DOC(false).
-spec table_ordered(
    greenwood:node_(molt@types:toml_kind()),
    greenwood:node_(molt@types:toml_kind()),
    list(binary())
) -> greenwood:node_(molt@types:toml_kind()).
table_ordered(Node, New_table, Segments) ->
    Parent_path = gleam@list:take(Segments, erlang:length(Segments) - 1),
    Append = fun(Into) ->
        Table = case erlang:element(3, Into) of
            [] ->
                molt@internal@cst@builder:drop_leading_newlines(New_table);

            _ ->
                molt@internal@cst@builder:ensure_leading_newline(New_table)
        end,
        greenwood:append_child(Into, {node_element, Table})
    end,
    case greenwood@zipper:down_where(
        greenwood@zipper:zip(Node),
        fun(_capture) -> is_descendant_table(_capture, Segments) end
    ) of
        {some, Cursor} ->
            Table@1 = case greenwood@zipper:left(Cursor) of
                none ->
                    molt@internal@cst@builder:drop_leading_newlines(New_table);

                {some, _} ->
                    molt@internal@cst@builder:ensure_leading_newline(New_table)
            end,
            _pipe = greenwood@zipper:insert_left(
                Cursor,
                {node_element, Table@1}
            ),
            _pipe@1 = gleam@option:map(_pipe, fun greenwood@zipper:unzip/1),
            gleam@option:unwrap(_pipe@1, Node);

        none ->
            case Parent_path of
                [_ | _] ->
                    case greenwood@zipper:down_last_where(
                        greenwood@zipper:zip(Node),
                        fun(_capture@1) ->
                            in_parent_family(_capture@1, Parent_path)
                        end
                    ) of
                        {some, Cursor@1} ->
                            _pipe@2 = greenwood@zipper:insert_right(
                                Cursor@1,
                                {node_element,
                                    molt@internal@cst@builder:ensure_leading_newline(
                                        New_table
                                    )}
                            ),
                            _pipe@3 = gleam@option:map(
                                _pipe@2,
                                fun greenwood@zipper:unzip/1
                            ),
                            gleam@option:unwrap(_pipe@3, Node);

                        none ->
                            Append(Node)
                    end;

                [] ->
                    Append(Node)
            end
    end.

-file("src/molt/internal/cst/insert.gleam", 63).
?DOC(false).
-spec ensure_table(greenwood:node_(molt@types:toml_kind()), list(binary())) -> greenwood:node_(molt@types:toml_kind()).
ensure_table(Node, Path) ->
    New_table = molt@internal@cst@builder:build_empty_table(Path),
    table_ordered(Node, New_table, Path).

-file("src/molt/internal/cst/insert.gleam", 73).
?DOC(false).
-spec ensure_array_of_tables(
    greenwood:node_(molt@types:toml_kind()),
    list(binary())
) -> greenwood:node_(molt@types:toml_kind()).
ensure_array_of_tables(Node, Path) ->
    New_table = molt@internal@cst@builder:build_empty_array_of_tables(Path),
    table_ordered(Node, New_table, Path).