-module(molt@internal@document@remove). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/molt/internal/document/remove.gleam"). -export([prune/3, delete/2]). -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/remove.gleam", 176). ?DOC(false). -spec remove_missing( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), molt@types:index_key(), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove_missing(Doc, Idx, Key, Segments) -> case molt@internal@document@index:find_deepest_ancestor_entry(Idx, Key) of {ok, {index_scalar_value, _}} -> 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) -> gleam@result:'try'( begin _pipe@1 = greenwood@zipper:delete(Cursor), gleam@option:to_result( _pipe@1, molt@error:not_found_path(Segments) ) end, fun(Cursor@1) -> _pipe@2 = greenwood@zipper:unzip(Cursor@1), _pipe@3 = molt@internal@document@primitives:patch( Doc, _pipe@2, Idx ), {ok, _pipe@3} end ) end ); {ok, {index_array_value, _}} -> 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) -> gleam@result:'try'( begin _pipe@1 = greenwood@zipper:delete(Cursor), gleam@option:to_result( _pipe@1, molt@error:not_found_path(Segments) ) end, fun(Cursor@1) -> _pipe@2 = greenwood@zipper:unzip(Cursor@1), _pipe@3 = molt@internal@document@primitives:patch( Doc, _pipe@2, Idx ), {ok, _pipe@3} end ) end ); {ok, {index_inline_table_value, _}} -> 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) -> gleam@result:'try'( begin _pipe@1 = greenwood@zipper:delete(Cursor), gleam@option:to_result( _pipe@1, molt@error:not_found_path(Segments) ) end, fun(Cursor@1) -> _pipe@2 = greenwood@zipper:unzip(Cursor@1), _pipe@3 = molt@internal@document@primitives:patch( Doc, _pipe@2, Idx ), {ok, _pipe@3} end ) end ); _ -> {error, molt@error:not_found_path(Segments)} end. -file("src/molt/internal/document/remove.gleam", 326). ?DOC(false). -spec counters_match(list({list(binary()), integer()}), list(integer())) -> boolean(). counters_match(Scopes, Counters) -> case {Scopes, Counters} of {[], []} -> true; {[{_, Idx} | S], [C | Cs]} -> (Idx =:= C) andalso counters_match(S, Cs); {_, _} -> false end. -file("src/molt/internal/document/remove.gleam", 311). ?DOC(false). -spec bump_counters( list({list(binary()), integer()}), list(integer()), list(binary()) ) -> list(integer()). bump_counters(Scopes, Counters, Kp) -> case {Scopes, Counters} of {[{Family, _} | S_rest], [C | C_rest]} -> case Family =:= Kp of true -> [C + 1 | gleam@list:map(C_rest, fun(_) -> -1 end)]; false -> [C | bump_counters(S_rest, C_rest, Kp)] end; {_, _} -> Counters end. -file("src/molt/internal/document/remove.gleam", 280). ?DOC(false). -spec remove_scoped_family( list(greenwood:element(molt@types:toml_kind())), list({list(binary()), integer()}), list(binary()), list(integer()), list(greenwood:element(molt@types:toml_kind())) ) -> list(greenwood:element(molt@types:toml_kind())). remove_scoped_family(Children, Scopes, Target, Counters, Acc) -> case Children of [] -> lists:reverse(Acc); [{node_element, N} | Rest] when (erlang:element(2, N) =:= array_of_tables) orelse (erlang:element( 2, N ) =:= table) -> Kp = molt@internal@cst@elements:extract_key_segments( erlang:element(3, N) ), Counters@1 = bump_counters(Scopes, Counters, Kp), Drop = (gleam@list:take(Kp, erlang:length(Target)) =:= Target) andalso counters_match(Scopes, Counters@1), Acc@1 = case Drop of true -> Acc; false -> [{node_element, N} | Acc] end, remove_scoped_family(Rest, Scopes, Target, Counters@1, Acc@1); [El | Rest@1] -> remove_scoped_family(Rest@1, Scopes, Target, Counters, [El | Acc]) end. -file("src/molt/internal/document/remove.gleam", 261). ?DOC(false). -spec do_index_scopes( list(molt@types:path_segment()), list(binary()), list({list(binary()), integer()}) ) -> list({list(binary()), integer()}). do_index_scopes(Remaining, Keys, Acc) -> case Remaining of [] -> lists:reverse(Acc); [{key_segment, K} | Rest] -> do_index_scopes(Rest, lists:append(Keys, [K]), Acc); [{index_segment, I} | Rest@1] -> do_index_scopes(Rest@1, Keys, [{Keys, I} | Acc]) end. -file("src/molt/internal/document/remove.gleam", 257). ?DOC(false). -spec index_scopes(list(molt@types:path_segment())) -> list({list(binary()), integer()}). index_scopes(Segments) -> do_index_scopes(Segments, [], []). -file("src/molt/internal/document/remove.gleam", 65). ?DOC(false). -spec remove_existing_array_of_tables( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove_existing_array_of_tables(Doc, Idx, Segments) -> case molt@internal@path:contains_index(Segments) of false -> Target_path = molt@internal@path:path_to_table_header(Segments), New_tree = greenwood:remove_children( erlang:element(4, Doc), fun(El) -> case El of {node_element, N} when erlang:element(2, N) =:= array_of_tables -> molt@internal@cst@elements:extract_key_segments( erlang:element(3, N) ) =:= Target_path; _ -> false end end ), {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx)}; true -> Norm = molt@internal@document@index:resolve_negative_indices( Idx, Segments ), Scopes = index_scopes(Norm), Target = molt@internal@path:path_to_table_header(Norm), New_children = remove_scoped_family( erlang:element(3, erlang:element(4, Doc)), Scopes, Target, gleam@list:map(Scopes, fun(_) -> -1 end), [] ), {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 )} end. -file("src/molt/internal/document/remove.gleam", 337). ?DOC(false). -spec is_kv_under_prefix( greenwood:node_(molt@types:toml_kind()), list(binary()) ) -> boolean(). is_kv_under_prefix(Kv, Prefix) -> case molt@internal@cst@elements:key_path(erlang:element(3, Kv)) of {some, Key_parts} -> gleam@list:take(Key_parts, erlang:length(Prefix)) =:= Prefix; none -> false end. -file("src/molt/internal/document/remove.gleam", 239). ?DOC(false). -spec under_prefix(list(binary())) -> fun((greenwood:element(molt@types:toml_kind())) -> boolean()). under_prefix(Prefix) -> fun(El) -> case El of {node_element, N} when (erlang:element(2, N) =:= table) orelse (erlang:element( 2, N ) =:= array_of_tables) -> gleam@list:take( molt@internal@cst@elements:extract_key_segments( erlang:element(3, N) ), erlang:length(Prefix) ) =:= Prefix; {node_element, N@1} when erlang:element(2, N@1) =:= key_value -> is_kv_under_prefix(N@1, Prefix); _ -> false end end. -file("src/molt/internal/document/remove.gleam", 204). ?DOC(false). -spec remove_implicit_table( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove_implicit_table(Doc, Idx, Segments) -> case molt@internal@path:split_at_last_index(Segments) of {error, nil} -> Prefix = molt@internal@path:path_to_table_header(Segments), New_tree = greenwood:remove_children( erlang:element(4, Doc), under_prefix(Prefix) ), Key = molt@internal@document@index:path_to_index_key(Segments), Idx@1 = molt@internal@document@index:prune_empty_implicit_ancestors( Idx, Key ), {ok, molt@internal@document@primitives:patch(Doc, New_tree, Idx@1)}; {ok, {Entry_path, Rel_segments}} -> Prefix@1 = molt@internal@path:path_to_table_header(Rel_segments), 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(Segments) ) end, fun(Cursor) -> New_entry = greenwood:remove_children( erlang:element(2, Cursor), under_prefix(Prefix@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/remove.gleam", 166). ?DOC(false). -spec drop_leading_newlines(greenwood:node_(molt@types:toml_kind())) -> greenwood:node_(molt@types:toml_kind()). drop_leading_newlines(Node) -> case erlang:element(4, Node) of bare -> Node; {trivia, Leading, Trailing} -> Stripped = gleam@list:drop_while( Leading, fun(T) -> erlang:element(2, T) =:= newline end ), {node, erlang:element(2, Node), erlang:element(3, Node), {trivia, Stripped, Trailing}} end. -file("src/molt/internal/document/remove.gleam", 109). ?DOC(false). -spec remove_value( molt@types:document(), list(molt@types:path_segment()), boolean() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove_value(Doc, Segments, Strip_first_blank) -> 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) -> Is_first_with_right_sibling = case {Strip_first_blank, erlang:element(3, Cursor)} of {true, [Crumb | _]} -> not gleam@list:any( erlang:element(4, Crumb), fun(El) -> case El of {node_element, _} -> true; _ -> false end end ) andalso gleam@list:any( erlang:element(5, Crumb), fun(El@1) -> case El@1 of {node_element, _} -> true; _ -> false end end ); {_, _} -> false end, case greenwood@zipper:delete(Cursor) of {some, Cursor@1} -> Cursor@2 = case Is_first_with_right_sibling of false -> Cursor@1; true -> greenwood@zipper:set_focus( Cursor@1, drop_leading_newlines( erlang:element(2, Cursor@1) ) ) end, {ok, molt@internal@document@primitives:rebuild( Doc, greenwood@zipper:unzip(Cursor@2) )}; none -> gleam@result:'try'( molt@cst:delete(erlang:element(4, Doc), Segments), fun(New_tree) -> {ok, molt@internal@document@primitives:rebuild( Doc, New_tree )} end ) end end ). -file("src/molt/internal/document/remove.gleam", 39). ?DOC(false). -spec remove_existing( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()), molt@types:index_entry() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove_existing(Doc, Idx, Segments, Entry) -> Key = molt@internal@document@index:path_to_index_key(Segments), Idx@1 = molt@internal@document@index:remove_subtree(Idx, Key), case Entry of {index_scalar_value, _} -> remove_value(Doc, Segments, false); {index_array_value, _} -> remove_value(Doc, Segments, false); {index_inline_table_value, _} -> remove_value(Doc, Segments, false); {index_table, _} -> remove_value(Doc, Segments, false); {index_array_of_tables_entry, _, _, _} -> remove_value(Doc, Segments, true); {index_implicit_table, _} -> remove_implicit_table(Doc, Idx@1, Segments); {index_array_of_tables, _, _} -> remove_existing_array_of_tables(Doc, Idx@1, Segments) end. -file("src/molt/internal/document/remove.gleam", 24). ?DOC(false). -spec prune( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()) ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. prune(Doc, Idx, Segments) -> Lookup = molt@internal@document@index:resolve_negative_indices( Idx, Segments ), Key = molt@internal@document@index:path_to_index_key(Lookup), case molt@internal@document@index:get(Idx, Key) of {ok, Entry} -> remove_existing(Doc, Idx, Segments, Entry); {error, nil} -> remove_missing(Doc, Idx, Key, Segments) end. -file("src/molt/internal/document/remove.gleam", 15). ?DOC(false). -spec delete(molt@types:document(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. delete(Doc, P) -> gleam@result:'try'( molt@internal@path:parse(P), fun(Segments) -> molt@internal@document@index:with_index( Doc, fun(Idx) -> prune(Doc, Idx, Segments) end ) end ).