-module(molt). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/molt.gleam"). -export([new/0, parse/1, parse_bits/1, document_errors/1, has_errors/1, error_count/1, to_string/1, normalize/1, to_normalized_string/1, run/2, append/3, concat/3, ensure_exists/3, get/2, get_comments/2, get_document_comments/2, set_document_comments/3, has/2, insert/4, insert_key/5, keys/2, length/2, merge_values/4, move/3, move_comments/3, move_keys/5, place/3, remove/2, rename/3, representation/3, set/3, set_comments/3, set_version/2, transfer/4, update/3, update_error/1]). -export_type([document_comment_position/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( " molt: TOML manipulation kit\n" "\n" " Molt parses a TOML document into a concrete syntax representation that\n" " can be transformed and rewritten while preserving comments associated with\n" " nodes.\n" "\n" " ## Path Syntax\n" "\n" " Molt functions and operations accept a path string made up of key\n" " segments separated by `.` (like `a.b`) and array indexes in brackets\n" " (like `[0]` or `[-1]`).\n" "\n" " Bare key segments may contain ASCII letters, digits, underscores, and\n" " dashes. Segments requiring other characters (such as spaces or dots) must\n" " be quoted with single quotes (no escape processing) or double quotes (with\n" " escape processing, e.g. `\\\"`).\n" "\n" " ```\n" " a.b.c.d // [\"a\", \"b\", \"c\", \"d\"]\n" " a.b.-1.d // [\"a\", \"b\", \"-1\", \"d\"]\n" " a.b.\"with space\".'[3]' // [\"a\", \"b\", \"with space\", \"[3]\"]\n" " a.b.\"\\e\".'\\e' // [\"a\", \"b\", \"\\u{001b}\", \"\\\\e\"]\n" " ```\n" "\n" " Array indexes must be integer values within brackets and negative indexing\n" " is supported. `0` is the first value in an array or array of tables, `-1` is\n" " the last value in an array or array of tables.\n" "\n" " ```\n" " a.b[-1].c // [\"a\", \"b\", -1, \"c\"]\n" " a.b[3].c // [\"a\", \"b\", 3, \"c\"]\n" " ```\n" "\n" " Path resolution behaviour with key or index values that do not resolve to\n" " a logical entry in the document depends on the operation performed.\n" "\n" " When operations are required against the document root, use an empty path\n" " string (`\"\"`).\n" "\n" " The `InvalidPath` variant of `MoltError` will be returned from\n" " `molt` functions if the path syntax is invalid.\n" "\n" " In some contexts, _only_ key paths may be provided.\n" ). -type document_comment_position() :: header | trailer. -file("src/molt.gleam", 76). ?DOC(" Create an empty TOML document.\n"). -spec new() -> molt@types:document(). new() -> {document, {toml_version, <<"1.1"/utf8>>}, 0, greenwood:node(root, []), {some, maps:new()}}. -file("src/molt.gleam", 96). ?DOC( " Parse a TOML source string into a `Document`.\n" "\n" " Returns an error if the the document _cannot_ be parsed. Note that most\n" " documents can be parsed, but not all documents with errors produce usable or\n" " easily recoverable syntax trees.\n" "\n" " The document's `error_count` records how many validation errors were found;\n" " retrieve the full positioned list with `document_errors`.\n" "\n" " The parsed document defaults to TOML 1.1 format, even if the source file was\n" " TOML 1.0.\n" ). -spec parse(binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. parse(Source) -> gleam@result:'try'( molt@internal@parser:parse(Source), fun(Tree) -> Error_count = molt@internal@validate:count(Tree), {ok, {document, {toml_version, <<"1.1"/utf8>>}, Error_count, Tree, none}} end ). -file("src/molt.gleam", 742). -spec safe_bits_to_string(bitstring()) -> {ok, binary()} | {error, molt@error:molt_error()}. safe_bits_to_string(Source) -> {Bom_prefix, Source@1} = case Source of <<239, 187, 191, 239, 187, 191, Rest/binary>> -> {<<"\x{FEFF}\x{FEFF}"/utf8>>, Rest}; <<239, 187, 191, Rest@1/binary>> -> {<<"\x{FEFF}"/utf8>>, Rest@1}; _ -> {<<""/utf8>>, Source} end, case gleam@bit_array:to_string(Source@1) of {ok, Source@2} -> {ok, <>}; {error, nil} -> {error, invalid_source_encoding} end. -file("src/molt.gleam", 108). ?DOC( " Parses a TOML source `BitArray` into a `Document`.\n" "\n" " Returns an error if the source is not valid UTF-8 data or if the transformed\n" " UTF-8 data fails on `parse`.\n" ). -spec parse_bits(bitstring()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. parse_bits(Source) -> gleam@result:'try'( safe_bits_to_string(Source), fun(Source@1) -> parse(Source@1) end ). -file("src/molt.gleam", 118). ?DOC( " Every validation error in the document as a fully-positioned `SyntaxError`,\n" " computed on demand from the current tree. Returns `[]` for a valid document.\n" "\n" " This is the only path that builds error spans; parsing and construction only\n" " count, so positions are paid for solely when you ask for them here.\n" ). -spec document_errors(molt@types:document()) -> list(molt@types:syntax_error()). document_errors(Doc) -> molt@internal@validate:enrich(erlang:element(4, Doc)). -file("src/molt.gleam", 124). ?DOC( " Whether the document has any validation errors. Most document-level\n" " operations refuse to run while this is `True`.\n" ). -spec has_errors(molt@types:document()) -> boolean(). has_errors(Doc) -> erlang:element(3, Doc) > 0. -file("src/molt.gleam", 129). ?DOC(" The number of validation errors found in the document.\n"). -spec error_count(molt@types:document()) -> integer(). error_count(Doc) -> erlang:element(3, Doc). -file("src/molt.gleam", 137). ?DOC( " Generate a TOML string from the document.\n" "\n" " If the document version is the same as the original version and no changes\n" " have been made, the original document will be reproduced exactly.\n" ). -spec to_string(molt@types:document()) -> binary(). to_string(Doc) -> molt@internal@emitter:emit_versioned( erlang:element(4, Doc), erlang:element(2, Doc) ). -file("src/molt.gleam", 161). ?DOC( " Returns a copy of the document with its tree normalized:\n" "\n" " - Unix newlines (LF) throughout.\n" " - Table header declarations have excess leading and interior space removed.\n" " - Key/value pairs have excess leading space removed, a single space after\n" " the key and a single space before the value, resulting in `key = value`.\n" " - A single blank line separates table and array of tables headers.\n" " - Leading comments are preserved immediately before their node.\n" " - Trailing (inline) comments on key-value and header lines are preserved.\n" " - Inline arrays and tables without comments are collapsed to a single-line\n" " form.\n" " - The document ends with a single trailing newline.\n" "\n" " The returned document is valid for further operations or for piping into\n" " `to_string`.\n" ). -spec normalize(molt@types:document()) -> molt@types:document(). normalize(Doc) -> {document, erlang:element(2, Doc), erlang:element(3, Doc), molt@internal@emitter:normalize(erlang:element(4, Doc)), erlang:element(5, Doc)}. -file("src/molt.gleam", 142). ?DOC(" Outputs a `normalize`d version of the document as a string.\n"). -spec to_normalized_string(molt@types:document()) -> binary(). to_normalized_string(Doc) -> _pipe = Doc, _pipe@1 = normalize(_pipe), to_string(_pipe@1). -file("src/molt.gleam", 169). ?DOC( " Execute a batch of `Operation`s over a `Document` where all operations must\n" " succeed for an update.\n" "\n" " `Operation`s will not run over a `Document` with errors.\n" ). -spec run(molt@types:document(), list(molt@ops:operation())) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. run(Doc, Ops) -> gleam@list:try_fold(Ops, Doc, fun molt@internal@document:run/2). -file("src/molt.gleam", 181). ?DOC( " Appends to an array at `path`.\n" "\n" " `path` must resolve to either a value node containing an array or an array\n" " of tables. When `path` resolves to an array of tables, the `value`\n" " provided _must_ be table-like.\n" ). -spec append(molt@types:document(), binary(), molt@value:value()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. append(Doc, Path, Value) -> run(Doc, [{append, Path, Value}]). -file("src/molt.gleam", 194). ?DOC( " Concatenate multiple values to an array at `path`.\n" "\n" " `path` must resolve to either a value node containing an array or an array\n" " of tables. When `path` resolves to an array of tables, all entries in\n" " `values` _must_ be table-like.\n" ). -spec concat(molt@types:document(), binary(), list(molt@value:value())) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. concat(Doc, Path, Values) -> run(Doc, [{concat, Path, Values}]). -file("src/molt.gleam", 216). ?DOC( " Ensures that a table or array of tables exists at `path`.\n" "\n" " `kind` must be `types.Table` or `types.ArrayOfTables`; other kinds are\n" " rejected.\n" "\n" " If the matching structure already exists, nothing is done. If `path` does\n" " not resolve, an empty entry is created with implicit table ancestors as\n" " required. If `path` resolves to an implicit table and `kind` is\n" " `types.Table`, the implicit table is concretized into an emitted header\n" " using the key segments of `path`.\n" "\n" " A `TypeMismatch` error is returned if `path` resolves to any other value\n" " shape, or if any ancestor in the `path` is anything other than an implicit\n" " table, concrete table, or array of tables entry.\n" ). -spec ensure_exists(molt@types:document(), binary(), molt@types:toml_kind()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. ensure_exists(Doc, Path, Kind) -> run(Doc, [{ensure_exists, Path, Kind}]). -file("src/molt.gleam", 680). -spec get_array_of_tables_entry_value( molt@types:document(), list(molt@types:path_segment()), integer() ) -> {ok, molt@value:value()} | {error, molt@error:molt_error()}. get_array_of_tables_entry_value(Doc, Segments, Index) -> Entry_path = lists:append(Segments, [{index_segment, Index}]), case molt@cst:get(erlang:element(4, Doc), Entry_path) of {ok, Entry_node} -> Entries = begin _pipe = molt@cst:list_keys(Entry_node), gleam@list:filter_map( _pipe, fun(K) -> case molt@cst:get(Entry_node, [{key_segment, K}]) of {ok, Kv} -> {ok, {K, molt@value:from_cst(Kv)}}; {error, _} -> {error, nil} end end ) end, {ok, molt@value:from_table_entries(Entries)}; _ -> {error, molt@error:not_found_path(Segments)} end. -file("src/molt.gleam", 711). -spec get_array_of_table_entry( molt@types:document(), list(molt@types:path_segment()), integer(), integer(), list(molt@value:value()) ) -> list(molt@value:value()). get_array_of_table_entry(Doc, Segments, Index, Count, Acc) -> case Index >= Count of true -> lists:reverse(Acc); false -> case get_array_of_tables_entry_value(Doc, Segments, Index) of {ok, V} -> get_array_of_table_entry( Doc, Segments, Index + 1, Count, [V | Acc] ); _ -> get_array_of_table_entry( Doc, Segments, Index + 1, Count, Acc ) end end. -file("src/molt.gleam", 702). -spec get_array_of_tables_as_value( molt@types:document(), list(molt@types:path_segment()), integer() ) -> {ok, molt@value:value()} | {error, molt@error:molt_error()}. get_array_of_tables_as_value(Doc, Segments, Count) -> _pipe = get_array_of_table_entry(Doc, Segments, 0, Count, []), molt@value:from_array_of_tables(_pipe). -file("src/molt.gleam", 759). -spec get_value_by_index( molt@types:document(), gleam@dict:dict(molt@types:index_key(), molt@types:index_entry()), list(molt@types:path_segment()) ) -> {ok, molt@value:value()} | {error, molt@error:molt_error()}. get_value_by_index(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, {index_scalar_value, Container}} -> Kv_key = gleam@list:drop(Lookup, erlang:length(Container)), case Kv_key of [{key_segment, _} | _] -> molt@internal@document:get_value_at(Doc, Container, Kv_key); _ -> {error, molt@error:not_found_path(Segments)} end; {ok, {index_array_value, Container}} -> Kv_key = gleam@list:drop(Lookup, erlang:length(Container)), case Kv_key of [{key_segment, _} | _] -> molt@internal@document:get_value_at(Doc, Container, Kv_key); _ -> {error, molt@error:not_found_path(Segments)} end; {ok, {index_inline_table_value, Container}} -> Kv_key = gleam@list:drop(Lookup, erlang:length(Container)), case Kv_key of [{key_segment, _} | _] -> molt@internal@document:get_value_at(Doc, Container, Kv_key); _ -> {error, molt@error:not_found_path(Segments)} end; {ok, {index_implicit_table, Children}} -> Entries = gleam@list:filter_map( Children, fun(K) -> _pipe = get_value_by_index( Doc, Idx, lists:append(Lookup, [{key_segment, K}]) ), _pipe@1 = gleam@result:map(_pipe, fun(V) -> {K, V} end), gleam@result:replace_error(_pipe@1, nil) end ), {ok, molt@value:from_table_entries(Entries)}; {ok, {index_table, _}} -> gleam@result:'try'( begin _pipe@2 = molt@cst:get(erlang:element(4, Doc), Lookup), gleam@result:replace_error( _pipe@2, molt@error:not_found_path(Segments) ) end, fun(Node) -> Entries@1 = begin _pipe@3 = molt@cst:list_keys(Node), gleam@list:filter_map( _pipe@3, fun(K@1) -> _pipe@4 = molt@cst:get( Node, [{key_segment, K@1}] ), _pipe@5 = gleam@result:map( _pipe@4, fun(Kv) -> {K@1, molt@value:from_cst(Kv)} end ), gleam@result:replace_error(_pipe@5, nil) end ) end, {ok, molt@value:from_table_entries(Entries@1)} end ); {ok, {index_array_of_tables_entry, _, _, _}} -> gleam@result:'try'( begin _pipe@2 = molt@cst:get(erlang:element(4, Doc), Lookup), gleam@result:replace_error( _pipe@2, molt@error:not_found_path(Segments) ) end, fun(Node) -> Entries@1 = begin _pipe@3 = molt@cst:list_keys(Node), gleam@list:filter_map( _pipe@3, fun(K@1) -> _pipe@4 = molt@cst:get( Node, [{key_segment, K@1}] ), _pipe@5 = gleam@result:map( _pipe@4, fun(Kv) -> {K@1, molt@value:from_cst(Kv)} end ), gleam@result:replace_error(_pipe@5, nil) end ) end, {ok, molt@value:from_table_entries(Entries@1)} end ); {ok, {index_array_of_tables, Count, _}} -> get_array_of_tables_as_value(Doc, Lookup, Count); {error, nil} -> case molt@internal@document@index:find_deepest_ancestor_entry( Idx, Key ) of {ok, {index_scalar_value, _}} -> _pipe@6 = molt@cst:get(erlang:element(4, Doc), Lookup), _pipe@7 = gleam@result:map( _pipe@6, fun molt@value:from_cst/1 ), gleam@result:replace_error( _pipe@7, molt@error:not_found_path(Segments) ); {ok, {index_array_value, _}} -> _pipe@6 = molt@cst:get(erlang:element(4, Doc), Lookup), _pipe@7 = gleam@result:map( _pipe@6, fun molt@value:from_cst/1 ), gleam@result:replace_error( _pipe@7, molt@error:not_found_path(Segments) ); {ok, {index_inline_table_value, _}} -> _pipe@6 = molt@cst:get(erlang:element(4, Doc), Lookup), _pipe@7 = gleam@result:map( _pipe@6, fun molt@value:from_cst/1 ), gleam@result:replace_error( _pipe@7, molt@error:not_found_path(Segments) ); _ -> {error, molt@error:not_found_path(Segments)} end end. -file("src/molt.gleam", 666). -spec get_root_as_value(molt@types:document()) -> {ok, molt@value:value()} | {error, molt@error:molt_error()}. get_root_as_value(Doc) -> _pipe = molt@internal@document:list_keys(Doc, []), _pipe@3 = gleam@result:map( _pipe, fun(_capture) -> gleam@list:filter_map( _capture, fun(K) -> _pipe@1 = molt@internal@document:get_value(Doc, [], K), _pipe@2 = gleam@result:map(_pipe@1, fun(V) -> {K, V} end), gleam@result:replace_error(_pipe@2, {error, nil}) end ) end ), _pipe@4 = gleam@result:unwrap(_pipe@3, []), _pipe@5 = molt@value:from_table_entries(_pipe@4), {ok, _pipe@5}. -file("src/molt.gleam", 227). ?DOC( " Get the value or structure at `path` as a molt Value.\n" "\n" " Implicit tables are returned as table values.\n" ). -spec get(molt@types:document(), binary()) -> {ok, molt@value:value()} | {error, molt@error:molt_error()}. get(Doc, Path) -> gleam@result:'try'( molt@internal@path:parse(Path), fun(Path@1) -> case Path@1 of [] -> get_root_as_value(Doc); _ -> molt@internal@document@index:with_index( Doc, fun(Idx) -> get_value_by_index(Doc, Idx, Path@1) end ) end end ). -file("src/molt.gleam", 248). ?DOC( " Reads the comments attached to the node at `path`.\n" "\n" " `path` must resolve to a concrete node (not an implicit table) or the root\n" " of the document (`\"\"`); other paths return an error.\n" "\n" " The result mirrors `set_comments`: leading comment lines and an optional\n" " trailing (inline) comment. Comment text is returned verbatim, including the\n" " leading `#`, so a value read here round-trips back through `set_comments`\n" " unchanged.\n" ). -spec get_comments(molt@types:document(), binary()) -> {ok, molt@ops:comments()} | {error, molt@error:molt_error()}. get_comments(Doc, Path) -> molt@internal@document@comments:get_comments(Doc, Path). -file("src/molt.gleam", 270). ?DOC( " Reads `Header` or `Trailer` document comments.\n" "\n" " Returns the comment lines verbatim (including the leading `#` but without\n" " newlines) or `[]` when there are no comments.\n" ). -spec get_document_comments(molt@types:document(), document_comment_position()) -> list(binary()). get_document_comments(Doc, At) -> case At of header -> molt@cst:document_head_comments(erlang:element(4, Doc)); trailer -> molt@cst:document_tail_comments(erlang:element(4, Doc)) end. -file("src/molt.gleam", 286). ?DOC( " Replaces `Header` or `Trailer` document comments, returning the updated\n" " document. If the comment text does not include `#`, the comment text will\n" " have `# ` prepended. Comments must not include newlines; they will be added\n" " appropriately on emit.\n" "\n" " Passing `[]` clears the comments.\n" ). -spec set_document_comments( molt@types:document(), document_comment_position(), list(binary()) ) -> molt@types:document(). set_document_comments(Doc, At, Comments) -> Tree = case At of header -> molt@cst:set_document_head_comments( erlang:element(4, Doc), Comments ); trailer -> molt@cst:set_document_tail_comments( erlang:element(4, Doc), Comments ) end, {document, erlang:element(2, Doc), erlang:element(3, Doc), Tree, none}. -file("src/molt.gleam", 301). ?DOC(" Check if `path` exists in the document.\n"). -spec has(molt@types:document(), binary()) -> boolean(). has(Doc, Path) -> case {molt@internal@path:parse(Path), molt@internal@document@index:get_index(Doc)} of {{ok, []}, _} -> true; {{ok, Path@1}, {ok, Idx}} -> Lookup = molt@internal@document@index:resolve_negative_indices( Idx, Path@1 ), Key = molt@internal@document@index:path_to_index_key(Lookup), gleam@bool:guard( molt@internal@document@index:has_key(Idx, Key), true, fun() -> case molt@internal@document@index:find_deepest_ancestor_entry( Idx, Key ) of {ok, {index_scalar_value, _}} -> _pipe = molt@cst:get(erlang:element(4, Doc), Lookup), gleam@result:is_ok(_pipe); {ok, {index_array_value, _}} -> _pipe = molt@cst:get(erlang:element(4, Doc), Lookup), gleam@result:is_ok(_pipe); {ok, {index_inline_table_value, _}} -> _pipe = molt@cst:get(erlang:element(4, Doc), Lookup), gleam@result:is_ok(_pipe); _ -> false end end ); {_, _} -> false end. -file("src/molt.gleam", 327). ?DOC( " Inserts a value before index `before` in an array at `path`.\n" "\n" " `path` must resolve to a value node containing an array or an array of\n" " tables. When `path` resolves to an array of tables, `value` must be\n" " table-like.\n" ). -spec insert(molt@types:document(), binary(), integer(), molt@value:value()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. insert(Doc, Path, Before, Value) -> run(Doc, [{insert, Path, Before, Value}]). -file("src/molt.gleam", 344). ?DOC( " Inserts a key/value pair before an existing key in the table at `path`.\n" "\n" " `path` must resolve to a concrete table, array of tables entry, or\n" " implicit table. Both `before` and `key` are literal key names (immediate\n" " children of `path`). If `before` is not found, the entry is appended.\n" "\n" " When `path` is an implicit table, the new entry is emitted as a\n" " root-level dotted key.\n" ). -spec insert_key( molt@types:document(), binary(), binary(), binary(), molt@value:value() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. insert_key(Doc, Path, Before, Key, Value) -> run(Doc, [{insert_key, Path, Before, Key, Value}]). -file("src/molt.gleam", 355). ?DOC(" Return the list of keys in a table-like value at the provided `path`.\n"). -spec keys(molt@types:document(), binary()) -> {ok, list(binary())} | {error, molt@error:molt_error()}. keys(Doc, P) -> gleam@result:'try'( molt@internal@path:parse(P), fun(Segments) -> molt@internal@document@index:with_index( Doc, fun(Idx) -> case Segments of [] -> {ok, molt@internal@document@index:root_children(Idx)}; _ -> case molt@internal@document@index:get_path( Idx, Segments ) of {ok, {index_table, Children}} -> {ok, Children}; {ok, {index_implicit_table, Children}} -> {ok, Children}; {ok, {index_array_of_tables_entry, _, _, Children}} -> {ok, Children}; {ok, _} -> {error, {type_mismatch, {some, molt@internal@path:to_string( Segments )}, <<"table, implicit table, or array of tables entry"/utf8>>, <<"value"/utf8>>}}; {error, nil} -> {error, molt@error:not_found_path(Segments)} end end end ) end ). -file("src/molt.gleam", 383). ?DOC(" Return the number of entries in an array or array of tables at `path`.\n"). -spec length(molt@types:document(), binary()) -> {ok, integer()} | {error, molt@error:molt_error()}. length(Doc, P) -> gleam@result:'try'( molt@internal@path:parse(P), fun(Segments) -> gleam@bool:guard( Segments =:= [], {error, {invalid_operation, <<"length"/utf8>>, none}}, fun() -> molt@internal@document@index:with_index( Doc, fun(Idx) -> case molt@internal@document@index:get_path( Idx, Segments ) of {ok, {index_array_of_tables, Count, _}} -> {ok, Count}; {ok, {index_array_value, _}} -> {Table_segs, Key} = molt@internal@path:split_last_segment( Segments ), molt@internal@path:with_segment_key_name( Key, {error, molt@error:not_found_path(Segments)}, fun(Key_name) -> gleam@result:'try'( molt@internal@document:get_value( Doc, Table_segs, Key_name ), fun(Val) -> _pipe = molt@value:array_length( Val ), gleam@option:to_result( _pipe, {type_mismatch, {some, molt@internal@path:to_string( Segments )}, <<"array"/utf8>>, molt@value:type_of( Val )} ) end ) end ); {ok, Entry} -> {error, {type_mismatch, {some, molt@internal@path:to_string( Segments )}, <<"array"/utf8>>, molt@internal@utils:index_entry_to_string( Entry )}}; {error, nil} -> case molt@internal@path:split_last_segment( Segments ) of {Parent, {index_segment, I}} -> case molt@internal@document@index:get_path( Idx, Parent ) of {ok, {index_array_value, _}} -> {Table_segs@1, Key@1} = molt@internal@path:split_last_segment( Parent ), molt@internal@path:with_segment_key_name( Key@1, {error, molt@error:not_found_path( Segments )}, fun(Key_name@1) -> gleam@result:'try'( molt@internal@document:get_value( Doc, Table_segs@1, Key_name@1 ), fun(Arr) -> gleam@result:'try'( begin _pipe@1 = molt@value:array_to_list( Arr ), gleam@result:replace_error( _pipe@1, molt@error:not_found_path( Segments ) ) end, fun( Items ) -> Len = erlang:length( Items ), Resolved = molt@internal@utils:resolve_index( I, Len ), gleam@result:'try'( begin _pipe@2 = molt@internal@utils:list_at( Items, Resolved ), gleam@result:replace_error( _pipe@2, {index_out_of_range, molt@internal@path:to_string( Parent ), I, Len} ) end, fun( Entry@1 ) -> _pipe@3 = molt@value:array_length( Entry@1 ), gleam@option:to_result( _pipe@3, {type_mismatch, {some, molt@internal@path:to_string( Segments )}, <<"array"/utf8>>, molt@value:type_of( Entry@1 )} ) end ) end ) end ) end ); _ -> {error, molt@error:not_found_path( Segments )} end; _ -> {error, molt@error:not_found_path( Segments )} end end end ) end ) end ). -file("src/molt.gleam", 479). ?DOC( " Merge key/value entries into the resolved table at `path`.\n" "\n" " `path` must resolve to a concrete table or an array of tables entry;\n" " implicit tables and other shapes are rejected with a `TypeMismatch` error.\n" "\n" " Each key in `entries` is parsed as a path value relative to the table at\n" " `path`. Index segments in entry keys will be rejected with an\n" " `InvalidPath` error, and entry keys redefining any existing concrete table\n" " or value are rejected.\n" "\n" " The `on_conflict` parameter controls how collisions with existing leaf\n" " keys are resolved.\n" ). -spec merge_values( molt@types:document(), binary(), list({binary(), molt@value:value()}), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. merge_values(Doc, Path, Entries, On_conflict) -> run(Doc, [{merge_values, Path, Entries, On_conflict}]). -file("src/molt.gleam", 494). ?DOC( " Moves the node at `from` to `to`.\n" "\n" " `from` must resolve to an existing node of any kind except the root. `to`\n" " must not already exist, and its last segment must be a key (not an index).\n" " The node is removed from `from` and re-inserted at `to`, preserving its\n" " structure.\n" ). -spec move(molt@types:document(), binary(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move(Doc, From, To) -> run(Doc, [{move, From, To}]). -file("src/molt.gleam", 506). ?DOC( " Moves comments from the node at `from` to the node at `to`.\n" "\n" " Both `from` and `to` must resolve to concrete nodes or the root of the\n" " document (`\"\"`).\n" ). -spec move_comments(molt@types:document(), binary(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. move_comments(Doc, From, To) -> run(Doc, [{move_comments, From, To}]). -file("src/molt.gleam", 523). ?DOC( " Moves a subset of keys from the table at `from` into the table at `to`.\n" "\n" " `from` must resolve to a concrete table, implicit table, or array of\n" " tables entry. `keys` are literal key names naming the immediate children\n" " of the `from` table. Keys not present in the `from` table are ignored.\n" "\n" " If `to` does not exist or is an implicit table, a concrete table header\n" " will be created. The `on_conflict` parameter controls how collisions\n" " with existing keys in the `to` table are resolved.\n" ). -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, On_conflict) -> run(Doc, [{move_keys, From, To, Keys, On_conflict}]). -file("src/molt.gleam", 537). ?DOC( " Unconditionally places `value` at `path`.\n" "\n" " If `path` already exists, it is removed before writing the `value`.\n" " Structural `Value`s (table, array of tables, etc.) are permitted.\n" ). -spec place(molt@types:document(), binary(), molt@value:value()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. place(Doc, Path, Value) -> run(Doc, [{place, Path, Value}]). -file("src/molt.gleam", 549). ?DOC( " Removes the node at `path` from the document.\n" "\n" " If `path` resolves to an implicit table, the implicit table and _all_\n" " concrete nodes beneath it are removed.\n" ). -spec remove(molt@types:document(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. remove(Doc, Path) -> run(Doc, [{remove, Path}]). -file("src/molt.gleam", 563). ?DOC( " Renames the last segment of `path` to `to`.\n" "\n" " The last segment of `path` must be a key. `to` is a literal key name and\n" " must not already exist as a sibling.\n" "\n" " Renaming an implicit table renames all concrete descendants that reference\n" " it.\n" ). -spec rename(molt@types:document(), binary(), binary()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. rename(Doc, Path, To) -> run(Doc, [{rename, Path, To}]). -file("src/molt.gleam", 580). ?DOC( " Converts the structure at `path` between inline and block forms.\n" "\n" " `path` must resolve to a table or array of tables. The data is preserved;\n" " only the representation changes (inline table ↔ table section, array of\n" " inline tables ↔ array of tables entries).\n" "\n" " A `path` that does not reference a convertible structure is\n" " a `TypeMismatch`. Conversions that would produce invalid TOML (e.g.,\n" " inlining a table with sub-table descendants) are rejected.\n" ). -spec representation(molt@types:document(), binary(), molt@ops:form()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. representation(Doc, Path, Form) -> run(Doc, [{representation, Path, Form}]). -file("src/molt.gleam", 596). ?DOC( " Sets a value at `path` in the document.\n" "\n" " Creates or overwrites a key/value node. If `path` does not exist, it is\n" " created (with implicit ancestors as needed). If `path` resolves to an\n" " existing value node, the value is replaced.\n" "\n" " If `path` resolves to a structural node (table sections, array of tables,\n" " implicit tables), `Set` will return a `TypeMismatch` error.\n" ). -spec set(molt@types:document(), binary(), molt@value:value()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. set(Doc, Path, Value) -> run(Doc, [{set, Path, Value}]). -file("src/molt.gleam", 609). ?DOC( " Sets comments on the node at `path`.\n" "\n" " `path` must resolve to a concrete node (not an implicit table) or the root\n" " of the document (`\"\"`). Replaces any existing comments on the node with the\n" " provided `comments`.\n" ). -spec set_comments(molt@types:document(), binary(), molt@ops:comments()) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. set_comments(Doc, Path, Comments) -> run(Doc, [{set_comments, Path, Comments}]). -file("src/molt.gleam", 620). ?DOC( " Set the target TOML version for output.\n" "\n" " Parsed documents default to TOML 1.1 (`molt.v1_1`).\n" ). -spec set_version(molt@types:document(), molt@types:toml_version()) -> molt@types:document(). set_version(Doc, Version) -> {document, Version, erlang:element(3, Doc), erlang:element(4, Doc), erlang:element(5, Doc)}. -file("src/molt.gleam", 633). ?DOC( " Transfers all keys from `from` to `to`, then removes `from`.\n" "\n" " `from` must resolve to a concrete or implicit table. `to` will be\n" " created as a concrete table if it does not exist. The `on_conflict`\n" " parameter controls how collisions with existing keys in `to` are\n" " resolved.\n" ). -spec transfer( molt@types:document(), binary(), binary(), molt@ops:conflict_strategy() ) -> {ok, molt@types:document()} | {error, molt@error:molt_error()}. transfer(Doc, From, To, On_conflict) -> run(Doc, [{transfer, From, To, On_conflict}]). -file("src/molt.gleam", 650). ?DOC( " Transforms a value in place via callback.\n" "\n" " `path` must resolve to a scalar, array, or inline table value node.\n" " Structural types (concrete tables, implicit tables, array of tables) are\n" " rejected with `TypeMismatch`.\n" "\n" " Transforming inline tables or arrays round-trips through `Value`, which\n" " loses internal comments and multiline formatting.\n" ). -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, Path, With) -> run(Doc, [{update, Path, With}]). -file("src/molt.gleam", 662). ?DOC( " Creates a `MoltError` for returning from `Update` callbacks.\n" "\n" " `Update` callbacks must return `Result(Value, MoltError)`. Use this\n" " function to signal a failure with a descriptive message.\n" ). -spec update_error(binary()) -> molt@error:molt_error(). update_error(Message) -> {update_error, Message}.