-module(molt@internal@document@reshape). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/molt/internal/document/reshape.gleam"). -export([rename/3, move/3, move_keys/5, merge/4]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -file("src/molt/internal/document/reshape.gleam", 516). ?DOC(false). -spec rewrite_kv_key( greenwood:node_(molt@types:toml_kind()), list(molt@types:path_segment()) ) -> greenwood:node_(molt@types:toml_kind()). rewrite_kv_key(Kv, New_key) -> Key_names = gleam@list:map(New_key, fun(Seg) -> case Seg of {key_segment, Name} -> Name; _ -> molt@internal@path:to_string([Seg]) end end), Value_and_after = molt@internal@cst@elements:value_tokens( erlang:element(3, Kv) ), New_key_elements = case Key_names of [Single] -> [{token_element, molt@internal@utils:make_key_token(Single)}]; _ -> [{node_element, greenwood:node( key, molt@internal@cst@builder:build_key_tokens(Key_names) )}] end, Children = lists:append( [New_key_elements, [{token_element, {token, whitespace, <<" "/utf8>>}}, {token_element, {token, equals, <<""/utf8>>}}], Value_and_after] ), {node, erlang:element(2, Kv), Children, erlang:element(4, Kv)}. -file("src/molt/internal/document/reshape.gleam", 487). ?DOC(false). -spec rewrite_header_prefix( list(greenwood:element(molt@types:toml_kind())), list(binary()), list(binary()) ) -> list(greenwood:element(molt@types:toml_kind())). rewrite_header_prefix(Children, From_keys, To_keys) -> From_len = erlang:length(From_keys), gleam@list:map(Children, fun(El) -> case El of {node_element, N} when (erlang:element(2, N) =:= table) orelse (erlang:element( 2, N ) =:= array_of_tables) -> Tp = molt@internal@cst@elements:extract_key_segments( erlang:element(3, N) ), gleam@bool:guard( gleam@list:take(Tp, From_len) /= From_keys, El, fun() -> New_path = lists:append( To_keys, gleam@list:drop(Tp, From_len) ), {node_element, molt@internal@cst@builder:rewrite_header_path( N, New_path )} end ); {node_element, N@1} when erlang:element(2, N@1) =:= key_value -> case molt@internal@cst@elements:key_path( erlang:element(3, N@1) ) of {some, Kp} -> gleam@bool:guard( gleam@list:take(Kp, From_len) /= From_keys, El, fun() -> New_kp = lists:append( To_keys, gleam@list:drop(Kp, From_len) ), New_key = gleam@list:map( New_kp, fun(Field@0) -> {key_segment, Field@0} end ), {node_element, rewrite_kv_key(N@1, New_key)} end ); none -> El end; _ -> El end end). -file("src/molt/internal/document/reshape.gleam", 422). ?DOC(false). -spec move_implicit_table( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move_implicit_table(Doc, Idx, From, To) -> case {molt@internal@path:split_at_last_index(From), molt@internal@path:split_at_last_index(To)} of {{error, nil}, {error, nil}} -> From_keys = molt@internal@path:path_to_table_header(From), To_keys = molt@internal@path:path_to_table_header(To), New_children = rewrite_header_prefix( erlang:element(3, erlang:element(4, Doc)), From_keys, To_keys ), New_tree = begin _record = erlang:element(4, Doc), {node, erlang:element(2, _record), New_children, erlang:element(4, _record)} end, From_key = molt@internal@document@index:path_to_index_key(From), To_key = molt@internal@document@index:path_to_index_key(To), Idx@1 = molt@internal@document@index:relocate_subtree( Idx, From_key, To_key ), {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx@1)}; {{ok, {From_entry, From_rel}}, {ok, {To_entry, To_rel}}} when From_entry =:= To_entry -> From_keys@1 = molt@internal@path:path_to_table_header(From_rel), To_keys@1 = molt@internal@path:path_to_table_header(To_rel), gleam@result:'try'( begin _pipe = molt@internal@cst@query:get_cursor( erlang:element(4, Doc), From_entry ), gleam@result:replace_error( _pipe, molt@error:not_found_path(From) ) end, fun(Cursor) -> Entry = erlang:element(2, Cursor), New_entry = {node, erlang:element(2, Entry), rewrite_header_prefix( erlang:element(3, Entry), From_keys@1, To_keys@1 ), erlang:element(4, Entry)}, New_tree@1 = begin _pipe@1 = greenwood@zipper:set_focus(Cursor, New_entry), greenwood@zipper:unzip(_pipe@1) end, {ok, molt@internal@document@primitives:rebuild( Doc, New_tree@1 )} end ); {_, _} -> {error, {invalid_operation, <<"move"/utf8>>, {some, <<<<<<"cannot move an implicit table across array of tables entries: "/utf8, (molt@internal@path:to_string(From))/binary>>/binary, " -> "/utf8>>/binary, (molt@internal@path:to_string(To))/binary>>}}} end. -file("src/molt/internal/document/reshape.gleam", 26). ?DOC(false). -spec rename(molt@types:document(), binary(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. rename(Doc, P, To) -> gleam@bool:guard( P =:= <<""/utf8>>, {error, {invalid_operation, <<"rename"/utf8>>, none}}, fun() -> gleam@result:'try'( molt@internal@path:parse(P), fun(Segments) -> gleam@bool:guard( case molt@internal@path:split_last_segment(Segments) of {_, {key_segment, _}} -> false; _ -> true end, {error, {type_mismatch, {some, P}, <<"a key"/utf8>>, <<"an index segment"/utf8>>}}, fun() -> molt@internal@document@index:with_index( Doc, fun(Idx) -> case molt@internal@document@index:resolve( Idx, Segments ) of {miss, _, _, _} -> {error, molt@error:not_found(P)}; {fresh, _} -> {error, molt@error:not_found(P)}; {hit, _, {index_array_of_tables_entry, _, _, _}} -> {error, {type_mismatch, {some, P}, <<"a named key"/utf8>>, <<"array of tables entry"/utf8>>}}; {hit, _, Entry} -> case molt@internal@document@index:get( Idx, molt@internal@document@index:rename_key( Segments, To ) ) of {ok, Current} -> {error, {already_exists, molt@internal@path:to_string( lists:append( molt@internal@path:drop_last_segment( Segments ), [{key_segment, To}] ) ), Current}}; _ -> case Entry of {index_implicit_table, _} -> {Parent, _} = molt@internal@path:split_last_segment( Segments ), To_segments = lists:append( Parent, [{key_segment, To}] ), move_implicit_table( Doc, Idx, Segments, To_segments ); {index_array_of_tables, _, _} -> From_keys = molt@internal@path:path_to_table_header( Segments ), {Parent@1, _} = molt@internal@path:split_last_segment( Segments ), To_keys = molt@internal@path:path_to_table_header( lists:append( Parent@1, [{key_segment, To}] ) ), New_children = rewrite_header_prefix( erlang:element( 3, erlang:element( 4, Doc ) ), From_keys, To_keys ), {ok, molt@internal@document@primitives:rebuild( Doc, begin _record = erlang:element( 4, Doc ), {node, erlang:element( 2, _record ), New_children, erlang:element( 4, _record )} end )}; _ -> gleam@result:'try'( molt@cst:rename( erlang:element( 4, Doc ), Segments, To ), fun(New_tree) -> From_key = molt@internal@document@index:path_to_index_key( Segments ), To_key = molt@internal@document@index:rename_key( Segments, To ), Idx@1 = molt@internal@document@index:relocate_subtree( Idx, From_key, To_key ), {ok, molt@internal@document@primitives:patch( Doc, New_tree, Idx@1 )} end ) end end end end ) end ) end ) end ). -file("src/molt/internal/document/reshape.gleam", 292). ?DOC(false). -spec move_kv( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move_kv(Doc, Idx, From, To) -> gleam@result:'try'( begin _pipe = molt@internal@cst@query:get_cursor( erlang:element(4, Doc), From ), gleam@result:replace_error(_pipe, molt@error:not_found_path(From)) end, fun(Cursor) -> {To_container, _} = molt@internal@path:split_last_segment(To), To_kv_key = gleam@list:drop(To, erlang:length(To_container)), Kv_node = rewrite_kv_key(erlang:element(2, Cursor), To_kv_key), gleam@result:'try'( molt@cst:delete(erlang:element(4, Doc), From), fun(Tree_after_delete) -> From_key = molt@internal@document@index:path_to_index_key( From ), To_key = molt@internal@document@index:path_to_index_key(To), Idx@1 = molt@internal@document@index:relocate_subtree( Idx, From_key, To_key ), Doc@1 = molt@internal@document@primitives:patch( Doc, Tree_after_delete, Idx@1 ), gleam@result:'try'( molt@internal@document@index:get_index(Doc@1), fun(Idx2) -> case molt@internal@document@index:resolve( Idx2, To_container ) of {hit, _, {index_table, _}} -> _pipe@1 = molt@cst:insert_kv( erlang:element(4, Doc@1), To_container, Kv_node, kv_at_end ), gleam@result:map( _pipe@1, fun(Tree) -> molt@internal@document@primitives:patch( Doc@1, Tree, Idx2 ) end ); {hit, _, {index_array_of_tables_entry, _, _, _}} -> _pipe@1 = molt@cst:insert_kv( erlang:element(4, Doc@1), To_container, Kv_node, kv_at_end ), gleam@result:map( _pipe@1, fun(Tree) -> molt@internal@document@primitives:patch( Doc@1, Tree, Idx2 ) end ); {hit, _, {index_implicit_table, _}} -> Full_kv = rewrite_kv_key(Kv_node, To), New_tree = molt@internal@document@primitives:insert_kv_before_first_header( erlang:element(4, Doc@1), Full_kv ), Idx3 = molt@internal@document@index:ensure_implicit_tables( Idx2, To ), {ok, molt@internal@document@primitives:patch( Doc@1, New_tree, Idx3 )}; {miss, _, _, _} -> Full_kv = rewrite_kv_key(Kv_node, To), New_tree = molt@internal@document@primitives:insert_kv_before_first_header( erlang:element(4, Doc@1), Full_kv ), Idx3 = molt@internal@document@index:ensure_implicit_tables( Idx2, To ), {ok, molt@internal@document@primitives:patch( Doc@1, New_tree, Idx3 )}; {fresh, _} -> Full_kv = rewrite_kv_key(Kv_node, To), New_tree = molt@internal@document@primitives:insert_kv_before_first_header( erlang:element(4, Doc@1), Full_kv ), Idx3 = molt@internal@document@index:ensure_implicit_tables( Idx2, To ), {ok, molt@internal@document@primitives:patch( Doc@1, New_tree, Idx3 )}; _ -> {error, {invalid_operation, <<"move into inline value container"/utf8>>, none}} end end ) end ) end ). -file("src/molt/internal/document/reshape.gleam", 118). ?DOC(false). -spec move(molt@types:document(), binary(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move(Doc, From, To) -> gleam@result:'try'( molt@internal@path:parse(From), fun(From_segments) -> gleam@result:'try'( molt@internal@path:parse(To), fun(To_segments) -> molt@internal@document@index:with_index( Doc, fun(Idx) -> Resolution = molt@internal@document@index:resolve( Idx, From_segments ), gleam@bool:guard(case Resolution of {miss, _, _, _} -> true; {fresh, _} -> true; _ -> false end, {error, molt@error:not_found(From)}, fun() -> Maybe_existing_at_to = molt@internal@document@index:get_path( Idx, To_segments ), gleam@bool:guard( gleam@result:is_ok(Maybe_existing_at_to), case molt@internal@path:split_last_segment( To_segments ) of {_, {key_segment, _}} -> {error, {already_exists, To, gleam@result:unwrap( Maybe_existing_at_to, {index_scalar_value, []} )}}; _ -> {error, {type_mismatch, {some, To}, <<"a key"/utf8>>, <<"an index segment"/utf8>>}} end, fun() -> case Resolution of {hit, _, {index_implicit_table, _}} -> move_implicit_table( Doc, Idx, From_segments, To_segments ); {hit, _, {index_table, _}} -> gleam@result:'try'( molt@cst:move( erlang:element( 4, Doc ), From_segments, To_segments ), fun(New_tree) -> From_key = molt@internal@document@index:path_to_index_key( From_segments ), To_key = molt@internal@document@index:path_to_index_key( To_segments ), Idx@1 = molt@internal@document@index:relocate_subtree( Idx, From_key, To_key ), {ok, molt@internal@document@primitives:patch( Doc, New_tree, Idx@1 )} end ); {hit, _, {index_array_of_tables, _, _}} -> gleam@result:'try'( molt@cst:move( erlang:element( 4, Doc ), From_segments, To_segments ), fun(New_tree) -> From_key = molt@internal@document@index:path_to_index_key( From_segments ), To_key = molt@internal@document@index:path_to_index_key( To_segments ), Idx@1 = molt@internal@document@index:relocate_subtree( Idx, From_key, To_key ), {ok, molt@internal@document@primitives:patch( Doc, New_tree, Idx@1 )} end ); {hit, _, _} -> move_kv( Doc, Idx, From_segments, To_segments ); {miss, _, _, _} -> {error, molt@error:not_found( From )}; {fresh, _} -> {error, molt@error:not_found( From )} end end ) end) end ) end ) end ). -file("src/molt/internal/document/reshape.gleam", 338). ?DOC(false). -spec move_single_key( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()), list(molt@types:path_segment()), binary(), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move_single_key(Doc, Idx, From, To, Key, On_conflict) -> From_key_path = lists:append(From, [{key_segment, Key}]), To_key_path = lists:append(To, [{key_segment, Key}]), Maybe_existing = molt@internal@document@index:get_path(Idx, To_key_path), Exists = gleam@result:is_ok(Maybe_existing), gleam@bool:guard( Exists andalso (On_conflict =:= on_conflict_error), {error, {already_exists, molt@internal@path:to_string(To_key_path), gleam@result:unwrap(Maybe_existing, {index_scalar_value, []})}}, fun() -> gleam@bool:guard( Exists andalso (On_conflict =:= on_conflict_skip), {ok, Doc}, fun() -> gleam@result:'try'( case Exists andalso (On_conflict =:= on_conflict_overwrite) of true -> molt@internal@document@index:with_index( Doc, fun(Idx2) -> molt@internal@document@remove:prune( Doc, Idx2, To_key_path ) end ); false -> {ok, Doc} end, fun(Doc@1) -> molt@internal@document@index:with_index( Doc@1, fun(Idx_curr) -> case molt@cst:move( erlang:element(4, Doc@1), From_key_path, To_key_path ) of {ok, New_tree} -> From_ikey = molt@internal@document@index:path_to_index_key( From_key_path ), To_ikey = molt@internal@document@index:path_to_index_key( To_key_path ), Idx2@1 = molt@internal@document@index:relocate_subtree( Idx_curr, From_ikey, To_ikey ), {ok, molt@internal@document@primitives:patch( Doc@1, New_tree, Idx2@1 )}; _ -> gleam@result:'try'( begin _pipe = molt@internal@cst@query:get_cursor( erlang:element(4, Doc@1), From_key_path ), gleam@result:replace_error( _pipe, molt@error:not_found_path( From_key_path ) ) end, fun(Src_cursor) -> Val = molt@value:from_cst( erlang:element( 2, Src_cursor ) ), gleam@result:'try'( molt@internal@document@remove:prune( Doc@1, Idx_curr, From_key_path ), fun(Doc2) -> molt@internal@document@index:with_index( Doc2, fun(Idx3) -> case molt@internal@document@index:resolve( Idx3, To_key_path ) of {hit, _, Current} -> {error, {already_exists, molt@internal@path:to_string( To_key_path ), Current}}; {miss, Ancestor_path, Ancestor, _} -> gleam@result:'try'( molt@internal@document@index:insertion_site( Idx3, Ancestor_path, Ancestor, To_key_path ), fun( Site ) -> molt@internal@document@primitives:write_at_site( Doc2, Idx3, Site, To_key_path, Val ) end ); {fresh, _} -> molt@internal@document@primitives:write_at_site( Doc2, Idx3, {root_dotted_key, To_key_path}, To_key_path, Val ) end end ) end ) end ) end end ) end ) end ) end ). -file("src/molt/internal/document/reshape.gleam", 198). ?DOC(false). -spec do_move_keys( molt@types:document(), list(molt@types:path_segment()), list(molt@types:path_segment()), list(binary()), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. do_move_keys(Doc, From_segments, To_segments, Keys_list, On_conflict) -> molt@internal@document@index:with_index( Doc, fun(Idx) -> case molt@internal@document@index:resolve(Idx, From_segments) of {hit, _, {index_table, _}} -> Existing_keys = gleam@list:filter( Keys_list, fun(Key) -> molt@internal@document@index:has_path( Idx, lists:append( From_segments, [{key_segment, Key}] ) ) end ), gleam@bool:guard( Existing_keys =:= [], {ok, Doc}, fun() -> gleam@result:'try'( molt@internal@document@structure:ensure_exists( Doc, molt@internal@path:to_string(To_segments), table ), fun(Doc@1) -> gleam@list:try_fold( Existing_keys, Doc@1, fun(D, Key@1) -> molt@internal@document@index:with_index( D, fun(Idx2) -> move_single_key( D, Idx2, From_segments, To_segments, Key@1, On_conflict ) end ) end ) end ) end ); {hit, _, {index_implicit_table, _}} -> Existing_keys = gleam@list:filter( Keys_list, fun(Key) -> molt@internal@document@index:has_path( Idx, lists:append( From_segments, [{key_segment, Key}] ) ) end ), gleam@bool:guard( Existing_keys =:= [], {ok, Doc}, fun() -> gleam@result:'try'( molt@internal@document@structure:ensure_exists( Doc, molt@internal@path:to_string(To_segments), table ), fun(Doc@1) -> gleam@list:try_fold( Existing_keys, Doc@1, fun(D, Key@1) -> molt@internal@document@index:with_index( D, fun(Idx2) -> move_single_key( D, Idx2, From_segments, To_segments, Key@1, On_conflict ) end ) end ) end ) end ); {hit, _, {index_array_of_tables_entry, _, _, _}} -> Existing_keys = gleam@list:filter( Keys_list, fun(Key) -> molt@internal@document@index:has_path( Idx, lists:append( From_segments, [{key_segment, Key}] ) ) end ), gleam@bool:guard( Existing_keys =:= [], {ok, Doc}, fun() -> gleam@result:'try'( molt@internal@document@structure:ensure_exists( Doc, molt@internal@path:to_string(To_segments), table ), fun(Doc@1) -> gleam@list:try_fold( Existing_keys, Doc@1, fun(D, Key@1) -> molt@internal@document@index:with_index( D, fun(Idx2) -> move_single_key( D, Idx2, From_segments, To_segments, Key@1, On_conflict ) end ) end ) end ) end ); {hit, _, Entry} -> {error, {type_mismatch, {some, molt@internal@path:to_string(From_segments)}, <<"table"/utf8>>, molt@internal@utils:index_entry_to_string(Entry)}}; {miss, _, _, _} -> {error, molt@error:not_found_path(From_segments)}; {fresh, _} -> {error, molt@error:not_found_path(From_segments)} end end ). -file("src/molt/internal/document/reshape.gleam", 180). ?DOC(false). -spec move_keys( molt@types:document(), binary(), binary(), list(binary()), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move_keys(Doc, From, To, Keys_list, On_conflict) -> gleam@result:'try'( molt@internal@path:parse(From), fun(From_segments) -> gleam@result:'try'( molt@internal@path:parse(To), fun(To_segments) -> do_move_keys( Doc, From_segments, To_segments, Keys_list, On_conflict ) end ) end ). -file("src/molt/internal/document/reshape.gleam", 248). ?DOC(false). -spec merge( molt@types:document(), binary(), binary(), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. merge(Doc, From, Into, On_conflict) -> gleam@result:'try'( molt@internal@path:parse(From), fun(From_segments) -> gleam@result:'try'( molt@internal@path:parse(Into), fun(To_segments) -> molt@internal@document@index:with_index( Doc, fun(Idx) -> case molt@internal@document@index:resolve( Idx, From_segments ) of {hit, _, {index_table, Children}} -> gleam@result:'try'( do_move_keys( Doc, From_segments, To_segments, Children, On_conflict ), fun(Doc2) -> molt@internal@document@index:with_index( Doc2, fun(Idx2) -> molt@internal@document@remove:prune( Doc2, Idx2, From_segments ) end ) end ); {hit, _, {index_implicit_table, Children}} -> gleam@result:'try'( do_move_keys( Doc, From_segments, To_segments, Children, On_conflict ), fun(Doc2) -> molt@internal@document@index:with_index( Doc2, fun(Idx2) -> molt@internal@document@remove:prune( Doc2, Idx2, From_segments ) end ) end ); {hit, _, {index_array_of_tables_entry, _, _, Children}} -> gleam@result:'try'( do_move_keys( Doc, From_segments, To_segments, Children, On_conflict ), fun(Doc2) -> molt@internal@document@index:with_index( Doc2, fun(Idx2) -> molt@internal@document@remove:prune( Doc2, Idx2, From_segments ) end ) end ); {hit, _, Entry} -> {error, {type_mismatch, {some, From}, <<"table"/utf8>>, molt@internal@utils:index_entry_to_string( Entry )}}; {miss, _, _, _} -> {error, molt@error:not_found(From)}; {fresh, _} -> {error, molt@error:not_found(From)} end end ) end ) end ).