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
).