-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, <<Bom_prefix/binary, Source@2/binary>>};
{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}.