Skip to main content

src/molt@internal@document@values.erl

-module(molt@internal@document@values).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/document/values.gleam").
-export([set/3, replace/3, insert_key/5, update/3]).

-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/values.gleam", 23).
?DOC(false).
-spec set(molt@types:document(), binary(), molt@value:value()) -> {ok,
        molt@types:document()} |
    {error, molt@error:molt_error()}.
set(Doc, P, Val) ->
    Type_of = molt@value:type_of(Val),
    gleam@bool:guard(
        Type_of =:= <<"invalid"/utf8>>,
        {error, {invalid_toml_value, P, molt@value:invalid_text(Val)}},
        fun() ->
            gleam@bool:guard(
                (Type_of =:= <<"table"/utf8>>) orelse (Type_of =:= <<"array_of_tables"/utf8>>),
                {error,
                    {type_mismatch,
                        {some, P},
                        <<"scalar, array, or inline-table value"/utf8>>,
                        Type_of}},
                fun() ->
                    gleam@result:'try'(
                        molt@internal@path:parse(P),
                        fun(Segments) ->
                            molt@internal@document@index:with_index(
                                Doc,
                                fun(Idx) ->
                                    case molt@internal@document@index:resolve(
                                        Idx,
                                        Segments
                                    ) of
                                        {hit, _, {index_scalar_value, _}} ->
                                            molt@internal@document@primitives:cursor_replace_value(
                                                Doc,
                                                Idx,
                                                Segments,
                                                molt@value:to_cst(Val)
                                            );

                                        {hit, _, {index_array_value, _}} ->
                                            molt@internal@document@primitives:cursor_replace_value(
                                                Doc,
                                                Idx,
                                                Segments,
                                                molt@value:to_cst(Val)
                                            );

                                        {hit, _, {index_inline_table_value, _}} ->
                                            molt@internal@document@primitives:cursor_replace_value(
                                                Doc,
                                                Idx,
                                                Segments,
                                                molt@value:to_cst(Val)
                                            );

                                        {hit, _, {index_implicit_table, _}} ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"scalar or array value"/utf8>>,
                                                    <<"implicit table"/utf8>>}};

                                        {hit, _, {index_table, _}} ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"scalar or array value"/utf8>>,
                                                    <<"table"/utf8>>}};

                                        {hit, _, {index_array_of_tables, _, _}} ->
                                            {error,
                                                {type_mismatch,
                                                    {some, P},
                                                    <<"scalar or array value (use an index to select an entry)"/utf8>>,
                                                    <<"array of tables"/utf8>>}};

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

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

                                        {fresh, _} ->
                                            molt@internal@document@primitives:write_at_site(
                                                Doc,
                                                Idx,
                                                {root_dotted_key, Segments},
                                                Segments,
                                                Val
                                            )
                                    end
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/values.gleam", 153).
?DOC(false).
-spec replace_array_of_tables(
    molt@types:document(),
    binary(),
    list(molt@value:value())
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
replace_array_of_tables(Doc, P, Items) ->
    case Items of
        [] ->
            {ok, Doc};

        [First | Rest] ->
            gleam@result:'try'(
                molt@value:table_to_list(First),
                fun(First_entries) ->
                    gleam@result:'try'(
                        molt@internal@document@structure:ensure_exists(
                            Doc,
                            P,
                            array_of_tables
                        ),
                        fun(D0) ->
                            gleam@result:'try'(
                                molt@internal@document@structure:merge_values(
                                    D0,
                                    <<P/binary, "[0]"/utf8>>,
                                    First_entries,
                                    on_conflict_error
                                ),
                                fun(D1) ->
                                    gleam@list:try_fold(
                                        Rest,
                                        D1,
                                        fun(D, Item) ->
                                            molt@internal@document@arrays:append(
                                                D,
                                                P,
                                                Item
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
    end.

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

                                    {miss, _, _, _} ->
                                        {ok, Doc};

                                    {fresh, _} ->
                                        {ok, Doc}
                                end,
                                fun(Doc2) -> case molt@value:type_of(Val) of
                                        <<"table"/utf8>> ->
                                            gleam@result:'try'(
                                                molt@value:table_to_list(Val),
                                                fun(Entries) ->
                                                    gleam@result:'try'(
                                                        molt@internal@document@structure:ensure_exists(
                                                            Doc2,
                                                            P,
                                                            table
                                                        ),
                                                        fun(Doc3) ->
                                                            molt@internal@document@structure:merge_values(
                                                                Doc3,
                                                                P,
                                                                Entries,
                                                                on_conflict_error
                                                            )
                                                        end
                                                    )
                                                end
                                            );

                                        <<"array_of_tables"/utf8>> ->
                                            gleam@result:'try'(
                                                molt@value:array_to_list(Val),
                                                fun(Items) ->
                                                    replace_array_of_tables(
                                                        Doc2,
                                                        P,
                                                        Items
                                                    )
                                                end
                                            );

                                        _ ->
                                            set(Doc2, P, Val)
                                    end end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/values.gleam", 395).
?DOC(false).
-spec insert_before_root_kv_with_path(
    list(greenwood:element(molt@types:toml_kind())),
    list(binary()),
    greenwood:element(molt@types:toml_kind())
) -> list(greenwood:element(molt@types:toml_kind())).
insert_before_root_kv_with_path(Children, Target_key_path, New_el) ->
    case Children of
        [] ->
            [New_el];

        [{node_element, N} | Rest] when erlang:element(2, N) =:= key_value ->
            case molt@internal@cst@elements:key_path(erlang:element(3, N)) of
                {some, Kp} when Kp =:= Target_key_path ->
                    [New_el, {node_element, N} | Rest];

                _ ->
                    [{node_element, N} |
                        insert_before_root_kv_with_path(
                            Rest,
                            Target_key_path,
                            New_el
                        )]
            end;

        [El | Rest@1] ->
            [El |
                insert_before_root_kv_with_path(Rest@1, Target_key_path, New_el)]
    end.

-file("src/molt/internal/document/values.gleam", 386).
?DOC(false).
-spec key_segment_names(list(molt@types:path_segment())) -> list(binary()).
key_segment_names(Path) ->
    gleam@list:filter_map(Path, fun(Seg) -> case Seg of
                {key_segment, Name} ->
                    {ok, molt@internal@utils:quote_key(Name)};

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

-file("src/molt/internal/document/values.gleam", 320).
?DOC(false).
-spec insert_key_in_implicit(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    binary(),
    binary(),
    molt@value:value()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
insert_key_in_implicit(Doc, Idx, Container, Before, Key, Val) ->
    case molt@internal@path:split_at_last_index(Container) of
        {error, nil} ->
            Before_keys = molt@internal@path:path_to_table_header(
                lists:append(Container, [{key_segment, Before}])
            ),
            New_key_path = lists:append(Container, [{key_segment, Key}]),
            Kv = molt@internal@cst@builder:build_kv_from_path(
                key_segment_names(New_key_path),
                molt@value:to_cst(Val)
            ),
            New_children = insert_before_root_kv_with_path(
                erlang:element(3, erlang:element(4, Doc)),
                Before_keys,
                {node_element, Kv}
            ),
            New_tree = begin
                _record = erlang:element(4, Doc),
                {node,
                    erlang:element(2, _record),
                    New_children,
                    erlang:element(4, _record)}
            end,
            Idx_key = molt@internal@document@index:path_to_index_key(
                New_key_path
            ),
            Entry = molt@internal@document@index:entry_for_value(Val, Container),
            Idx@1 = molt@internal@document@index:insert_entry(
                Idx,
                Idx_key,
                Entry
            ),
            {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx@1)};

        {ok, {Entry_path, Rel_container}} ->
            Before_keys@1 = molt@internal@path:path_to_table_header(
                lists:append(Rel_container, [{key_segment, Before}])
            ),
            Rel_key_path = lists:append(Rel_container, [{key_segment, Key}]),
            Kv@1 = molt@internal@cst@builder:build_kv_from_path(
                key_segment_names(Rel_key_path),
                molt@value:to_cst(Val)
            ),
            gleam@result:'try'(
                begin
                    _pipe = molt@internal@cst@query:get_cursor(
                        erlang:element(4, Doc),
                        Entry_path
                    ),
                    gleam@result:replace_error(
                        _pipe,
                        molt@error:not_found_path(Container)
                    )
                end,
                fun(Cursor) ->
                    Entry@1 = erlang:element(2, Cursor),
                    New_entry = {node,
                        erlang:element(2, Entry@1),
                        insert_before_root_kv_with_path(
                            erlang:element(3, Entry@1),
                            Before_keys@1,
                            {node_element, Kv@1}
                        ),
                        erlang:element(4, Entry@1)},
                    New_tree@1 = begin
                        _pipe@1 = greenwood@zipper:set_focus(Cursor, New_entry),
                        greenwood@zipper:unzip(_pipe@1)
                    end,
                    {ok,
                        molt@internal@document@primitives:rebuild(
                            Doc,
                            New_tree@1
                        )}
                end
            )
    end.

-file("src/molt/internal/document/values.gleam", 180).
?DOC(false).
-spec insert_key(
    molt@types:document(),
    binary(),
    binary(),
    binary(),
    molt@value:value()
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
insert_key(Doc, P, Before, Key, Val) ->
    gleam@result:'try'(
        molt@internal@path:parse(P),
        fun(Segments) ->
            molt@internal@document@index:with_index(
                Doc,
                fun(Idx) ->
                    case molt@internal@document@index:resolve(Idx, Segments) of
                        {hit, _, {index_table, _}} ->
                            Kv = molt@internal@cst@builder:build_kv_node(
                                molt@internal@utils:quote_key(Key),
                                molt@value:to_cst(Val)
                            ),
                            gleam@result:'try'(
                                case molt@cst:insert_kv(
                                    erlang:element(4, Doc),
                                    Segments,
                                    Kv,
                                    {before_key, Before}
                                ) of
                                    {error, {not_found, _, _}} ->
                                        molt@cst:insert_kv(
                                            erlang:element(4, Doc),
                                            Segments,
                                            Kv,
                                            kv_at_end
                                        );

                                    Other ->
                                        Other
                                end,
                                fun(New_tree) ->
                                    Full_path = lists:append(
                                        Segments,
                                        [{key_segment, Key}]
                                    ),
                                    Idx_key = molt@internal@document@index:path_to_index_key(
                                        Full_path
                                    ),
                                    Entry = molt@internal@document@index:entry_for_value(
                                        Val,
                                        Segments
                                    ),
                                    Idx@1 = molt@internal@document@index:insert_entry(
                                        Idx,
                                        Idx_key,
                                        Entry
                                    ),
                                    {ok,
                                        molt@internal@document@primitives:patch(
                                            Doc,
                                            New_tree,
                                            Idx@1
                                        )}
                                end
                            );

                        {hit, _, {index_array_of_tables_entry, _, _, _}} ->
                            Kv = molt@internal@cst@builder:build_kv_node(
                                molt@internal@utils:quote_key(Key),
                                molt@value:to_cst(Val)
                            ),
                            gleam@result:'try'(
                                case molt@cst:insert_kv(
                                    erlang:element(4, Doc),
                                    Segments,
                                    Kv,
                                    {before_key, Before}
                                ) of
                                    {error, {not_found, _, _}} ->
                                        molt@cst:insert_kv(
                                            erlang:element(4, Doc),
                                            Segments,
                                            Kv,
                                            kv_at_end
                                        );

                                    Other ->
                                        Other
                                end,
                                fun(New_tree) ->
                                    Full_path = lists:append(
                                        Segments,
                                        [{key_segment, Key}]
                                    ),
                                    Idx_key = molt@internal@document@index:path_to_index_key(
                                        Full_path
                                    ),
                                    Entry = molt@internal@document@index:entry_for_value(
                                        Val,
                                        Segments
                                    ),
                                    Idx@1 = molt@internal@document@index:insert_entry(
                                        Idx,
                                        Idx_key,
                                        Entry
                                    ),
                                    {ok,
                                        molt@internal@document@primitives:patch(
                                            Doc,
                                            New_tree,
                                            Idx@1
                                        )}
                                end
                            );

                        {hit, _, {index_implicit_table, _}} ->
                            insert_key_in_implicit(
                                Doc,
                                Idx,
                                Segments,
                                Before,
                                Key,
                                Val
                            );

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

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

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

-file("src/molt/internal/document/values.gleam", 280).
?DOC(false).
-spec cursor_transform_value(
    molt@types:document(),
    gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()),
    list(molt@types:path_segment()),
    fun((molt@value:value()) -> {ok, molt@value:value()} |
        {error, molt@error:molt_error()})
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
cursor_transform_value(Doc, Idx, Segments, Transform) ->
    gleam@result:'try'(
        begin
            _pipe = molt@internal@cst@query:get_cursor(
                erlang:element(4, Doc),
                Segments
            ),
            gleam@result:replace_error(
                _pipe,
                molt@error:not_found_path(Segments)
            )
        end,
        fun(Cursor) ->
            Current = molt@value:from_cst(erlang:element(2, Cursor)),
            gleam@result:'try'(
                Transform(Current),
                fun(New_val) ->
                    Path_str = molt@internal@path:to_string(Segments),
                    New_val_type = molt@value:type_of(New_val),
                    gleam@bool:guard(
                        (New_val_type =:= <<"table"/utf8>>) orelse (New_val_type
                        =:= <<"array_of_tables"/utf8>>),
                        {error,
                            {type_mismatch,
                                {some, Path_str},
                                <<"scalar or inline value"/utf8>>,
                                New_val_type}},
                        fun() ->
                            gleam@bool:guard(
                                New_val_type =:= <<"invalid"/utf8>>,
                                {error,
                                    {invalid_toml_value,
                                        Path_str,
                                        molt@value:invalid_text(New_val)}},
                                fun() ->
                                    New_node = case erlang:element(
                                        2,
                                        erlang:element(2, Cursor)
                                    ) of
                                        array_element ->
                                            molt@internal@document@primitives:rebuild_array_element(
                                                erlang:element(2, Cursor),
                                                molt@value:to_cst(New_val)
                                            );

                                        _ ->
                                            molt@internal@document@primitives:rebuild_kv_value(
                                                erlang:element(2, Cursor),
                                                molt@value:to_cst(New_val)
                                            )
                                    end,
                                    _pipe@1 = greenwood@zipper:set_focus(
                                        Cursor,
                                        New_node
                                    ),
                                    _pipe@2 = greenwood@zipper:unzip(_pipe@1),
                                    _pipe@3 = molt@internal@document@primitives:patch(
                                        Doc,
                                        _pipe@2,
                                        Idx
                                    ),
                                    {ok, _pipe@3}
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/document/values.gleam", 243).
?DOC(false).
-spec update(
    molt@types:document(),
    binary(),
    fun((molt@value:value()) -> {ok, molt@value:value()} |
        {error, molt@error:molt_error()})
) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}.
update(Doc, P, Transform) ->
    gleam@result:'try'(
        molt@internal@path:parse(P),
        fun(Segments) ->
            molt@internal@document@index:with_index(
                Doc,
                fun(Idx) ->
                    case molt@internal@document@index:resolve(Idx, Segments) of
                        {hit, _, {index_scalar_value, _}} ->
                            cursor_transform_value(
                                Doc,
                                Idx,
                                Segments,
                                Transform
                            );

                        {hit, _, {index_array_value, _}} ->
                            cursor_transform_value(
                                Doc,
                                Idx,
                                Segments,
                                Transform
                            );

                        {hit, _, {index_inline_table_value, _}} ->
                            cursor_transform_value(
                                Doc,
                                Idx,
                                Segments,
                                Transform
                            );

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

                        {miss, _, Ancestor, _} ->
                            case Ancestor of
                                {index_scalar_value, _} ->
                                    cursor_transform_value(
                                        Doc,
                                        Idx,
                                        Segments,
                                        Transform
                                    );

                                {index_array_value, _} ->
                                    cursor_transform_value(
                                        Doc,
                                        Idx,
                                        Segments,
                                        Transform
                                    );

                                {index_inline_table_value, _} ->
                                    cursor_transform_value(
                                        Doc,
                                        Idx,
                                        Segments,
                                        Transform
                                    );

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

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