-module(tomlet). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/tomlet.gleam"). -export([date_from_string/1, time_from_string/1, datetime_from_string/1, date_to_string/1, time_to_string/1, datetime_to_string/1, new/0, parse/1, parse_with/2, value_get/2, get/2, parse_value/1, parse_bytes/1, parse_bytes_with/2, line_column/2, position_line/1, position_column/1, to_string/1, get_string/2, get_int/2, get_bool/2, get_float/2, get_date/2, get_time/2, get_datetime/2, table_keys/2, as_string/1, as_int/1, as_bool/1, as_float/1, as_date/1, as_time/1, as_datetime/1, set_string/3, set_int/3, set_bool/3, set_float/3, set_date/3, set_time/3, set_datetime/3, set_array/3, set_inline_table/3, append_array_of_tables/3, remove/2, insert_comment_before/3]). -export_type([document/0, line_ending/0, parse_error/0, syntax_error_kind/0, get_error/0, expected_type/0, value/0, date/0, time/0, date_time/0, format_error/0, special_float/0, edit_error/0, toml_version/0, 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( " A round-tripping TOML parser and writer.\n" "\n" " Tomlet parses TOML into an opaque `Document`, preserves comments and\n" " formatting during round-trips, and provides checked helpers for common\n" " reads and edits.\n" ). -opaque document() :: {document, tomlet@ast:table(), binary(), line_ending(), gleam@option:option(binary())}. -type line_ending() :: lf | crlf. -type parse_error() :: invalid_encoding | {invalid_syntax, syntax_error_kind(), integer()} | {duplicate_key, list(binary()), integer()}. -type syntax_error_kind() :: expected_value | expected_key | expected_table_header | invalid_toml. -type get_error() :: {key_not_found, list(binary())} | {wrong_type, list(binary()), expected_type()}. -type expected_type() :: expected_string | expected_int | expected_bool | expected_float | expected_date | expected_time | expected_date_time. -type value() :: {string_value, binary()} | {int_value, integer()} | {float_value, float()} | {special_float_value, special_float()} | {bool_value, boolean()} | {date_value, date()} | {time_value, time()} | {date_time_value, date_time()} | {array_value, list(value())} | {inline_table_value, list({list(binary()), value()})} | {standard_table_value, list({list(binary()), value()})} | {array_of_tables_value, list(list({list(binary()), value()}))}. -opaque date() :: {date, binary()}. -opaque time() :: {time, binary()}. -opaque date_time() :: {date_time, binary()}. -type format_error() :: {invalid_date, binary()} | {invalid_time, binary()} | {invalid_date_time, binary()}. -type special_float() :: positive_infinity | negative_infinity | not_a_number. -type edit_error() :: empty_key_path | {invalid_key_segment, binary()} | invalid_comment_text | {missing_edit_key, list(binary())} | {key_conflict, list(binary())} | {inline_table_insert_unsupported, list(binary())} | invalid_value. -type toml_version() :: toml10 | toml11. -opaque position() :: {position, integer(), integer()}. -file("src/tomlet.gleam", 174). ?DOC( " Construct a `Date` from its TOML lexical form (e.g. `\"1979-05-27\"`).\n" "\n" " ```gleam\n" " let assert Ok(date) = tomlet.date_from_string(\"1979-05-27\")\n" " let assert Ok(doc) = tomlet.set_date(tomlet.new(), [\"released\"], date)\n" " tomlet.to_string(doc)\n" " // -> \"released = 1979-05-27\\n\"\n" " ```\n" ). -spec date_from_string(binary()) -> {ok, date()} | {error, format_error()}. date_from_string(Text) -> gleam@bool:guard( not tomlet@parser:date_repr_is_valid(Text), {error, {invalid_date, Text}}, fun() -> {ok, {date, Text}} end ). -file("src/tomlet.gleam", 190). ?DOC( " Construct a `Time` from its TOML lexical form (e.g. `\"07:32:00\"`).\n" "\n" " ```gleam\n" " let assert Ok(time) = tomlet.time_from_string(\"07:32:00\")\n" " let assert Ok(doc) = tomlet.set_time(tomlet.new(), [\"alarm\"], time)\n" " tomlet.to_string(doc)\n" " // -> \"alarm = 07:32:00\\n\"\n" " ```\n" ). -spec time_from_string(binary()) -> {ok, time()} | {error, format_error()}. time_from_string(Text) -> gleam@bool:guard( not tomlet@parser:time_repr_is_valid(Text), {error, {invalid_time, Text}}, fun() -> {ok, {time, Text}} end ). -file("src/tomlet.gleam", 209). ?DOC( " Construct a `DateTime` from its TOML lexical form\n" " (e.g. `\"1979-05-27T07:32:00Z\"`).\n" "\n" " ```gleam\n" " let assert Ok(datetime) =\n" " tomlet.datetime_from_string(\"1979-05-27T07:32:00Z\")\n" " let assert Ok(doc) =\n" " tomlet.set_datetime(tomlet.new(), [\"published\"], datetime)\n" " tomlet.to_string(doc)\n" " // -> \"published = 1979-05-27T07:32:00Z\\n\"\n" " ```\n" ). -spec datetime_from_string(binary()) -> {ok, date_time()} | {error, format_error()}. datetime_from_string(Text) -> gleam@bool:guard( not tomlet@parser:datetime_repr_is_valid(Text), {error, {invalid_date_time, Text}}, fun() -> {ok, {date_time, Text}} end ). -file("src/tomlet.gleam", 218). ?DOC(" Return the original lexical form of a TOML date value.\n"). -spec date_to_string(date()) -> binary(). date_to_string(Date) -> erlang:element(2, Date). -file("src/tomlet.gleam", 223). ?DOC(" Return the original lexical form of a TOML time value.\n"). -spec time_to_string(time()) -> binary(). time_to_string(Time) -> erlang:element(2, Time). -file("src/tomlet.gleam", 228). ?DOC(" Return the original lexical form of a TOML date-time value.\n"). -spec datetime_to_string(date_time()) -> binary(). datetime_to_string(Datetime) -> erlang:element(2, Datetime). -file("src/tomlet.gleam", 286). ?DOC( " Create an empty TOML document.\n" "\n" " Equivalent to `parse(\"\")` for downstream callers; the only observable\n" " difference is that `parse(\"\")` initially round-trips to `\"\"` even after the\n" " document is modified, while the document returned by `new` always emits its\n" " current content.\n" ). -spec new() -> document(). new() -> {document, {table, [], none}, <<""/utf8>>, lf, none}. -file("src/tomlet.gleam", 620). -spec normalized_offset_to_original_next( list(integer()), integer(), integer(), integer() ) -> integer(). normalized_offset_to_original_next(Codepoints, Target, Normalized, Original) -> case Codepoints of [] -> Original; [Codepoint | Rest] -> Width = erlang:byte_size( gleam_stdlib:utf_codepoint_list_to_string([Codepoint]) ), normalized_offset_to_original_loop( Rest, Target, Normalized + Width, Original + Width, false ) end. -file("src/tomlet.gleam", 569). -spec normalized_offset_to_original_loop( list(integer()), integer(), integer(), integer(), boolean() ) -> integer(). normalized_offset_to_original_loop( Codepoints, Target, Normalized, Original, At_start ) -> case {Normalized >= Target, Codepoints} of {true, _} -> Original; {false, []} -> Original; {false, [First, Second | Rest]} -> case {gleam_stdlib:identity(First), gleam_stdlib:identity(Second), At_start} of {65279, _, true} -> normalized_offset_to_original_loop( [Second | Rest], Target, Normalized, Original + 3, false ); {13, 10, _} -> normalized_offset_to_original_loop( Rest, Target, Normalized + 1, Original + 2, false ); {_, _, _} -> normalized_offset_to_original_next( [First, Second | Rest], Target, Normalized, Original ) end; {false, Remaining} -> normalized_offset_to_original_next( Remaining, Target, Normalized, Original ) end. -file("src/tomlet.gleam", 559). -spec normalized_offset_to_original(binary(), integer()) -> integer(). normalized_offset_to_original(Input, Target) -> normalized_offset_to_original_loop( gleam@string:to_utf_codepoints(Input), Target, 0, 0, true ). -file("src/tomlet.gleam", 641). -spec syntax_error_kind(tomlet@parser:expected_token_kind()) -> syntax_error_kind(). syntax_error_kind(Expected) -> case Expected of expected_value -> expected_value; expected_key -> expected_key; expected_table_header -> expected_table_header; expected_syntax -> invalid_toml end. -file("src/tomlet.gleam", 552). -spec to_parser_version(toml_version()) -> tomlet@parser:version(). to_parser_version(Version) -> case Version of toml10 -> toml10; toml11 -> toml11 end. -file("src/tomlet.gleam", 513). -spec parse_string_with(binary(), toml_version()) -> {ok, document()} | {error, parse_error()}. parse_string_with(Input, Version) -> Line_ending = case gleam_stdlib:contains_string(Input, <<"\r\n"/utf8>>) of true -> crlf; false -> lf end, Input_without_initial_bom = case gleam@string:to_graphemes(Input) of [<<"\x{FEFF}"/utf8>> | Rest] -> erlang:list_to_binary(Rest); _ -> Input end, case gleam_stdlib:contains_string( Input_without_initial_bom, <<"\x{FEFF}"/utf8>> ) of true -> {error, invalid_encoding}; false -> Normalized = gleam@string:replace( Input_without_initial_bom, <<"\r\n"/utf8>>, <<"\n"/utf8>> ), case tomlet@parser:parse(Normalized, to_parser_version(Version)) of {ok, Root} -> {ok, {document, Root, <<""/utf8>>, Line_ending, {some, Input}}}; {error, {unexpected, _, Expected, Offset}} -> {error, {invalid_syntax, syntax_error_kind(Expected), normalized_offset_to_original(Input, Offset)}}; {error, {key_already_in_use, Key, Offset@1}} -> {error, {duplicate_key, Key, normalized_offset_to_original(Input, Offset@1)}} end end. -file("src/tomlet.gleam", 308). ?DOC( " Parse TOML 1.1 text into a document. Use `parse_with(input, Toml10)` for\n" " strict TOML 1.0 parsing that rejects 1.1-only syntax.\n" "\n" " Successful parses return an opaque `Document` that preserves comments,\n" " formatting trivia, key order, and the original line ending style for\n" " round-tripping. Invalid text returns `ParseError`, including byte offsets for\n" " syntax and duplicate-key diagnostics.\n" ). -spec parse(binary()) -> {ok, document()} | {error, parse_error()}. parse(Input) -> parse_string_with(Input, toml11). -file("src/tomlet.gleam", 313). ?DOC(" Parse TOML text against the given language version.\n"). -spec parse_with(binary(), toml_version()) -> {ok, document()} | {error, parse_error()}. parse_with(Input, Version) -> parse_string_with(Input, Version). -file("src/tomlet.gleam", 356). -spec value_offset(integer(), integer()) -> integer(). value_offset(Offset, Prefix_size) -> case Offset < Prefix_size of true -> 0; false -> Offset - Prefix_size end. -file("src/tomlet.gleam", 346). -spec parse_value_error(parse_error(), integer()) -> parse_error(). parse_value_error(Error, Prefix_size) -> case Error of invalid_encoding -> invalid_encoding; {invalid_syntax, Kind, Offset} -> {invalid_syntax, Kind, value_offset(Offset, Prefix_size)}; {duplicate_key, _, Offset@1} -> {invalid_syntax, invalid_toml, value_offset(Offset@1, Prefix_size)} end. -file("src/tomlet.gleam", 961). -spec value_get_index(list(value()), binary(), list(binary()), list(binary())) -> {ok, value()} | {error, get_error()}. value_get_index(Items, Index_text, Rest, Key) -> case gleam_stdlib:parse_int(Index_text) of {ok, Index} when Index >= 0 -> case begin _pipe = Items, _pipe@1 = gleam@list:drop(_pipe, Index), gleam@list:first(_pipe@1) end of {ok, Item} -> value_get(Item, Rest); {error, nil} -> {error, {key_not_found, Key}} end; _ -> {error, {key_not_found, Key}} end. -file("src/tomlet.gleam", 938). -spec value_get_table( list({list(binary()), value()}), binary(), list(binary()), list(binary()) ) -> {ok, value()} | {error, get_error()}. value_get_table(Entries, Head, Rest, Key) -> Matched = gleam@list:filter_map( Entries, fun(Entry) -> {Entry_key, Entry_value} = Entry, case Entry_key of [First | Tail] when First =:= Head -> {ok, {Tail, Entry_value}}; _ -> {error, nil} end end ), case {Matched, Rest} of {[], _} -> {error, {key_not_found, Key}}; {[{[], Inner}], []} -> {ok, Inner}; {[{[], Inner@1}], _} -> value_get(Inner@1, Rest); {_, []} -> {ok, {standard_table_value, Matched}}; {_, _} -> value_get({standard_table_value, Matched}, Rest) end. -file("src/tomlet.gleam", 924). ?DOC( " Descend into a `Value` by key path without returning to the document root.\n" "\n" " Table-shaped values are descended by key; arrays and arrays of tables are\n" " descended by a non-negative decimal index (e.g. `\"0\"`). An empty path\n" " returns the value unchanged. A missing key, a non-numeric or out-of-range\n" " index, or descent into a scalar all yield `KeyNotFound`.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.parse(\"[[packages]]\\nname = \\\"gleam_stdlib\\\"\\n\")\n" " let assert Ok(packages) = tomlet.get(doc, [\"packages\"])\n" " tomlet.value_get(packages, [\"0\", \"name\"])\n" " // -> Ok(tomlet.StringValue(\"gleam_stdlib\"))\n" " ```\n" ). -spec value_get(value(), list(binary())) -> {ok, value()} | {error, get_error()}. value_get(Value, Key) -> case {Key, Value} of {[], _} -> {ok, Value}; {[Head | Rest], {inline_table_value, Entries}} -> value_get_table(Entries, Head, Rest, Key); {[Head@1 | Rest@1], {standard_table_value, Entries@1}} -> value_get_table(Entries@1, Head@1, Rest@1, Key); {[Head@2 | Rest@2], {array_value, Items}} -> value_get_index(Items, Head@2, Rest@2, Key); {[Head@3 | Rest@3], {array_of_tables_value, Tables}} -> value_get_index( gleam@list:map( Tables, fun(Field@0) -> {standard_table_value, Field@0} end ), Head@3, Rest@3, Key ); {_, _} -> {error, {key_not_found, Key}} end. -file("src/tomlet.gleam", 2259). -spec key_to_strings(tomlet@ast:key()) -> list(binary()). key_to_strings(Key) -> tomlet@key:to_strings(Key). -file("src/tomlet.gleam", 1020). -spec public_special_float(tomlet@ast:special_float()) -> special_float(). public_special_float(Value) -> case Value of positive_infinity -> positive_infinity; negative_infinity -> negative_infinity; not_a_number -> not_a_number end. -file("src/tomlet.gleam", 2249). -spec header_key(tomlet@ast:header()) -> list(binary()). header_key(Header) -> {header, Key, _, _} = Header, key_to_strings(Key). -file("src/tomlet.gleam", 2254). -spec header_is_standard_table(tomlet@ast:header()) -> boolean(). header_is_standard_table(Header) -> {header, _, Kind, _} = Header, Kind =:= standard_table. -file("src/tomlet.gleam", 1047). -spec entry_defines_target_table(tomlet@ast:entry(), list(binary())) -> boolean(). entry_defines_target_table(Entry, Target) -> case Entry of {table_header, Header} -> header_is_standard_table(Header) andalso (header_key(Header) =:= Target); _ -> false end. -file("src/tomlet.gleam", 1041). -spec public_table_entries(tomlet@ast:table()) -> list({list(binary()), value()}). public_table_entries(Table) -> {table, Entries, _} = Table, {Table_entries, _} = collect_table_entries(Entries, [], [], true, []), Table_entries. -file("src/tomlet.gleam", 1033). -spec public_inline_table_entry(tomlet@ast:inline_table_entry()) -> {list(binary()), value()}. public_inline_table_entry(Entry) -> {inline_table_entry, _, Key, Value, _} = Entry, {key_to_strings(Key), public_value(Value)}. -file("src/tomlet.gleam", 1028). -spec public_array_item(tomlet@ast:array_item()) -> value(). public_array_item(Item) -> {array_item, _, Value, _} = Item, public_value(Value). -file("src/tomlet.gleam", 1000). -spec public_value(tomlet@ast:value()) -> value(). public_value(Value) -> case Value of {int, Value@1, _} -> {int_value, Value@1}; {float, Value@2, _} -> {float_value, Value@2}; {special_float, Value@3, _} -> {special_float_value, public_special_float(Value@3)}; {bool, Value@4, _} -> {bool_value, Value@4}; {string, Value@5, _, _} -> {string_value, Value@5}; {date, Source_text} -> {date_value, {date, Source_text}}; {time, Source_text@1} -> {time_value, {time, Source_text@1}}; {date_time, Source_text@2} -> {date_time_value, {date_time, Source_text@2}}; {array, Items, _} -> {array_value, gleam@list:map(Items, fun public_array_item/1)}; {inline_table, Entries, _} -> {inline_table_value, gleam@list:map(Entries, fun public_inline_table_entry/1)}; {array_of_tables, Items@1} -> {array_of_tables_value, gleam@list:map(Items@1, fun public_table_entries/1)} end. -file("src/tomlet.gleam", 1055). -spec collect_key_value_entry( tomlet@ast:key(), tomlet@ast:value(), list(tomlet@ast:entry()), list(binary()), list(binary()), list(binary()), boolean(), list({list(binary()), value()}) ) -> {list({list(binary()), value()}), boolean()}. collect_key_value_entry( Key, Value, Rest, Active_table, Next_active_table, Target, Next_found, Collected ) -> Full_key = lists:append(Active_table, key_to_strings(Key)), case tomlet@key:starts_with(Full_key, Target) andalso (Full_key /= Target) of true -> collect_table_entries( Rest, Next_active_table, Target, true, [{gleam@list:drop(Full_key, erlang:length(Target)), public_value(Value)} | Collected] ); false -> collect_table_entries( Rest, Next_active_table, Target, Next_found, Collected ) end. -file("src/tomlet.gleam", 1083). -spec collect_table_entries( list(tomlet@ast:entry()), list(binary()), list(binary()), boolean(), list({list(binary()), value()}) ) -> {list({list(binary()), value()}), boolean()}. collect_table_entries(Entries, Active_table, Target, Found, Collected) -> case Entries of [] -> {lists:reverse(Collected), Found}; [Entry | Rest] -> Next_active_table = case Entry of {table_header, Header} -> header_key(Header); _ -> Active_table end, Next_found = Found orelse entry_defines_target_table(Entry, Target), case Entry of {key_value, _, Key, Value, _} -> collect_key_value_entry( Key, Value, Rest, Active_table, Next_active_table, Target, Next_found, Collected ); _ -> collect_table_entries( Rest, Next_active_table, Target, Next_found, Collected ) end end. -file("src/tomlet.gleam", 982). -spec get_table_value(document(), list(binary())) -> {ok, value()} | {error, get_error()}. get_table_value(Doc, Key) -> case Key of [] -> {error, {key_not_found, Key}}; _ -> {document, {table, Entries, _}, _, _, _} = Doc, {Table_entries, Found} = collect_table_entries( Entries, [], Key, false, [] ), case Found of true -> {ok, {standard_table_value, Table_entries}}; false -> {error, {key_not_found, Key}} end end. -file("src/tomlet.gleam", 977). -spec get_value(document(), list(binary())) -> {ok, tomlet@ast:value()} | {error, get_error()}. get_value(Doc, Key) -> _pipe = tomlet@path:get(erlang:element(2, Doc), Key), gleam@result:replace_error(_pipe, {key_not_found, Key}). -file("src/tomlet.gleam", 719). -spec get_indexed_loop(document(), list(binary()), integer()) -> {ok, value()} | {error, get_error()}. get_indexed_loop(Doc, Key, Split) -> gleam@bool:guard( Split < 1, {error, {key_not_found, Key}}, fun() -> _pipe = get(Doc, gleam@list:take(Key, Split)), _pipe@1 = gleam@result:'try'( _pipe, fun(_capture) -> value_get(_capture, gleam@list:drop(Key, Split)) end ), gleam@result:lazy_or( _pipe@1, fun() -> get_indexed_loop(Doc, Key, Split - 1) end ) end ). -file("src/tomlet.gleam", 715). -spec get_indexed(document(), list(binary())) -> {ok, value()} | {error, get_error()}. get_indexed(Doc, Key) -> get_indexed_loop(Doc, Key, erlang:length(Key) - 1). -file("src/tomlet.gleam", 694). ?DOC( " Read a TOML value at a key path.\n" "\n" " Use `get` instead of the typed `get_*` helpers when you need to inspect\n" " arrays, inline tables, standard tables, arrays of tables, or special floats.\n" "\n" " Path segments that name an array or array of tables can be followed by a\n" " non-negative decimal index to descend into it, e.g.\n" " `get(doc, [\"packages\", \"0\", \"name\"])`. For a typed read of an indexed path,\n" " compose with the `as_*` converters: `get(doc, path) |> result.try(as_string)`.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.parse(\"package = { name = \\\"tomato\\\", downloads = 42 }\\n\")\n" " let assert Ok(value) = tomlet.get(doc, [\"package\"])\n" " // -> tomlet.InlineTableValue([\n" " // #([\"name\"], tomlet.StringValue(\"tomato\")),\n" " // #([\"downloads\"], tomlet.IntValue(42)),\n" " // ])\n" " ```\n" ). -spec get(document(), list(binary())) -> {ok, value()} | {error, get_error()}. get(Doc, Key) -> case get_value(Doc, Key) of {ok, Value} -> {ok, public_value(Value)}; {error, {key_not_found, _}} -> case get_table_value(Doc, Key) of {ok, Value@1} -> {ok, Value@1}; {error, {key_not_found, _}} -> get_indexed(Doc, Key); {error, Error} -> {error, Error} end; {error, Error@1} -> {error, Error@1} end. -file("src/tomlet.gleam", 332). ?DOC( " Parse a standalone TOML value literal.\n" "\n" " The returned value uses Tomlet's stable public `Value` variants. Trailing\n" " non-comment syntax is rejected rather than ignored.\n" "\n" " ```gleam\n" " tomlet.parse_value(\"\\\"tomato\\\"\")\n" " // -> Ok(tomlet.StringValue(\"tomato\"))\n" "\n" " tomlet.parse_value(\"[8000, 8001]\")\n" " // -> Ok(tomlet.ArrayValue([tomlet.IntValue(8000), tomlet.IntValue(8001)]))\n" " ```\n" ). -spec parse_value(binary()) -> {ok, value()} | {error, parse_error()}. parse_value(Input) -> Key = <<"__tomlet_value__"/utf8>>, Prefix = <>, case parse_string_with(Source, toml11) of {ok, Doc} -> case get(Doc, [Key]) of {ok, Value} -> {ok, Value}; {error, _} -> {error, {invalid_syntax, invalid_toml, 0}} end; {error, Error} -> {error, parse_value_error(Error, erlang:byte_size(Prefix))} end. -file("src/tomlet.gleam", 504). -spec bit_array_contains_utf8_bom(bitstring()) -> boolean(). bit_array_contains_utf8_bom(Input) -> case Input of <<>> -> false; <<239, 187, 191, _/bitstring>> -> true; <<_, Rest/bitstring>> -> bit_array_contains_utf8_bom(Rest); _ -> false end. -file("src/tomlet.gleam", 387). -spec parse_bytes_versioned(bitstring(), toml_version()) -> {ok, document()} | {error, parse_error()}. parse_bytes_versioned(Input, Version) -> Input_without_initial_bom = case Input of <<239, 187, 191, Rest/bitstring>> -> Rest; _ -> Input end, case bit_array_contains_utf8_bom(Input_without_initial_bom) of true -> {error, invalid_encoding}; false -> case gleam@bit_array:to_string(Input_without_initial_bom) of {ok, Decoded} -> parse_string_with(Decoded, Version); {error, _} -> {error, invalid_encoding} end end. -file("src/tomlet.gleam", 375). ?DOC( " Parse TOML bytes into a document.\n" "\n" " This validates UTF-8 input and accepts a UTF-8 byte order mark only at the\n" " start of the input.\n" "\n" " ```gleam\n" " let assert Ok(doc) = tomlet.parse_bytes(<<\"answer = 42\\n\":utf8>>)\n" " let assert Ok(answer) = tomlet.get_int(doc, [\"answer\"])\n" "\n" " tomlet.parse_bytes(<<110, 97, 109, 101, 32, 61, 32, 255, 10>>)\n" " // -> Error(tomlet.InvalidEncoding)\n" " ```\n" ). -spec parse_bytes(bitstring()) -> {ok, document()} | {error, parse_error()}. parse_bytes(Input) -> parse_bytes_versioned(Input, toml11). -file("src/tomlet.gleam", 380). ?DOC(" Parse TOML bytes against the given language version.\n"). -spec parse_bytes_with(bitstring(), toml_version()) -> {ok, document()} | {error, parse_error()}. parse_bytes_with(Input, Version) -> parse_bytes_versioned(Input, Version). -file("src/tomlet.gleam", 482). -spec line_column_next( list(integer()), integer(), integer(), integer(), integer() ) -> {integer(), integer()}. line_column_next(Codepoints, Target, Current, Line, Column) -> case Codepoints of [] -> {Line, Column}; [Codepoint | Rest] -> case gleam_stdlib:identity(Codepoint) of 10 -> line_column_loop(Rest, Target, Current + 1, Line + 1, 1); 13 -> line_column_loop(Rest, Target, Current + 1, Line + 1, 1); _ -> Width = erlang:byte_size( gleam_stdlib:utf_codepoint_list_to_string([Codepoint]) ), line_column_loop( Rest, Target, Current + Width, Line, Column + 1 ) end end. -file("src/tomlet.gleam", 451). -spec line_column_loop( list(integer()), integer(), integer(), integer(), integer() ) -> {integer(), integer()}. line_column_loop(Codepoints, Target, Current, Line, Column) -> case {Current >= Target, Codepoints} of {true, _} -> {Line, Column}; {false, []} -> {Line, Column}; {false, [First, Second | Rest]} -> case {gleam_stdlib:identity(First), gleam_stdlib:identity(Second)} of {13, 10} -> line_column_loop(Rest, Target, Current + 2, Line + 1, 1); {_, _} -> line_column_next( [First, Second | Rest], Target, Current, Line, Column ) end; {false, Remaining} -> line_column_next(Remaining, Target, Current, Line, Column) end. -file("src/tomlet.gleam", 435). ?DOC( " Convert a byte offset into a one-based line and column.\n" "\n" " Offsets beyond the end of the input return the position just after the last\n" " character. CRLF is treated as a single line break.\n" "\n" " ```gleam\n" " let input = \"name = \\n\"\n" " case tomlet.parse(input) {\n" " Error(tomlet.InvalidSyntax(_, offset)) -> {\n" " let position = tomlet.line_column(input, offset)\n" " let line = tomlet.position_line(position)\n" " let column = tomlet.position_column(position)\n" " // Show line and column in your application's diagnostic.\n" " }\n" " _ -> Nil\n" " }\n" " ```\n" ). -spec line_column(binary(), integer()) -> position(). line_column(Input, Offset) -> {Line, Column} = line_column_loop( gleam@string:to_utf_codepoints(Input), Offset, 0, 1, 1 ), {position, Line, Column}. -file("src/tomlet.gleam", 442). ?DOC(" Return the one-based line number for a source position.\n"). -spec position_line(position()) -> integer(). position_line(Position) -> erlang:element(2, Position). -file("src/tomlet.gleam", 447). ?DOC(" Return the one-based column number for a source position.\n"). -spec position_column(position()) -> integer(). position_column(Position) -> erlang:element(3, Position). -file("src/tomlet.gleam", 1747). -spec emit_key_segment(tomlet@ast:key_segment()) -> binary(). emit_key_segment(Segment) -> case Segment of {bare_key_segment, Text} -> Text; {quoted_key_segment, _, Source_text} -> Source_text end. -file("src/tomlet.gleam", 1740). -spec emit_key(tomlet@ast:key()) -> binary(). emit_key(Key) -> {key, Segments} = Key, _pipe = Segments, _pipe@1 = gleam@list:map(_pipe, fun emit_key_segment/1), gleam@string:join(_pipe@1, <<"."/utf8>>). -file("src/tomlet.gleam", 1732). -spec emit_header(tomlet@ast:header()) -> binary(). emit_header(Header) -> {header, Key, Kind, _} = Header, case Kind of standard_table -> <<<<"["/utf8, (emit_key(Key))/binary>>/binary, "]"/utf8>>; array_of_tables_header -> <<<<"[["/utf8, (emit_key(Key))/binary>>/binary, "]]"/utf8>> end. -file("src/tomlet.gleam", 1796). -spec emit_trivia(tomlet@ast:trivia()) -> binary(). emit_trivia(Trivia) -> {trivia, Text} = Trivia, Text. -file("src/tomlet.gleam", 1754). -spec emit_value(tomlet@ast:value()) -> binary(). emit_value(Value) -> case Value of {int, _, Source_text} -> Source_text; {float, _, Source_text@1} -> Source_text@1; {special_float, _, Source_text@2} -> Source_text@2; {bool, _, Source_text@3} -> Source_text@3; {string, _, _, Source_text@4} -> Source_text@4; {date, Source_text@5} -> Source_text@5; {time, Source_text@6} -> Source_text@6; {date_time, Source_text@7} -> Source_text@7; {array, _, Source_text@8} -> Source_text@8; {inline_table, _, Source_text@9} -> Source_text@9; {array_of_tables, Items} -> _pipe = Items, _pipe@1 = gleam@list:map(_pipe, fun emit_table/1), gleam@string:join(_pipe@1, <<""/utf8>>) end. -file("src/tomlet.gleam", 1718). -spec emit_entry(tomlet@ast:entry()) -> binary(). emit_entry(Entry) -> case Entry of {key_value, Leading, Key, Value, Trailing} -> <<<<<<<<(emit_trivia(Leading))/binary, (emit_key(Key))/binary>>/binary, " = "/utf8>>/binary, (emit_value(Value))/binary>>/binary, (emit_trivia(Trailing))/binary>>; {table_header, Header} -> <<(emit_header(Header))/binary, "\n"/utf8>>; {comment, Text} -> <>; blank_line -> <<"\n"/utf8>> end. -file("src/tomlet.gleam", 1678). -spec emit_table(tomlet@ast:table()) -> binary(). emit_table(Table) -> case Table of {table, [], _} -> <<""/utf8>>; {table, Entries, _} -> _pipe = Entries, _pipe@1 = gleam@list:map(_pipe, fun emit_entry/1), gleam@string:join(_pipe@1, <<""/utf8>>) end. -file("src/tomlet.gleam", 653). ?DOC( " Emit a document as TOML text.\n" "\n" " Unedited parsed documents round-trip to their original source text.\n" ). -spec to_string(document()) -> binary(). to_string(Doc) -> case erlang:element(5, Doc) of {some, Source} -> Source; none -> Output = <<(emit_table(erlang:element(2, Doc)))/binary, (erlang:element(3, Doc))/binary>>, case erlang:element(4, Doc) of lf -> Output; crlf -> gleam@string:replace(Output, <<"\n"/utf8>>, <<"\r\n"/utf8>>) end end. -file("src/tomlet.gleam", 671). -spec with_root(document(), tomlet@ast:table()) -> document(). with_root(Doc, Root) -> {document, Root, erlang:element(3, Doc), erlang:element(4, Doc), none}. -file("src/tomlet.gleam", 731). ?DOC(" Read a TOML string value at a key path.\n"). -spec get_string(document(), list(binary())) -> {ok, binary()} | {error, get_error()}. get_string(Doc, Key) -> case get_value(Doc, Key) of {ok, {string, Value, _, _}} -> {ok, Value}; {ok, _} -> {error, {wrong_type, Key, expected_string}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 743). ?DOC(" Read a TOML integer value at a key path.\n"). -spec get_int(document(), list(binary())) -> {ok, integer()} | {error, get_error()}. get_int(Doc, Key) -> case get_value(Doc, Key) of {ok, {int, Value, _}} -> {ok, Value}; {ok, _} -> {error, {wrong_type, Key, expected_int}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 752). ?DOC(" Read a TOML boolean value at a key path.\n"). -spec get_bool(document(), list(binary())) -> {ok, boolean()} | {error, get_error()}. get_bool(Doc, Key) -> case get_value(Doc, Key) of {ok, {bool, Value, _}} -> {ok, Value}; {ok, _} -> {error, {wrong_type, Key, expected_bool}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 764). ?DOC( " Read a TOML float value at a key path.\n" "\n" " Special floats (`inf`, `-inf`, `nan`) are not returned here; reading one\n" " yields `WrongType`. Use `get` and match on `SpecialFloatValue` for those.\n" ). -spec get_float(document(), list(binary())) -> {ok, float()} | {error, get_error()}. get_float(Doc, Key) -> case get_value(Doc, Key) of {ok, {float, Value, _}} -> {ok, Value}; {ok, _} -> {error, {wrong_type, Key, expected_float}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 773). ?DOC(" Read a TOML local date value at a key path.\n"). -spec get_date(document(), list(binary())) -> {ok, date()} | {error, get_error()}. get_date(Doc, Key) -> case get_value(Doc, Key) of {ok, {date, Source_text}} -> {ok, {date, Source_text}}; {ok, _} -> {error, {wrong_type, Key, expected_date}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 782). ?DOC(" Read a TOML local time value at a key path.\n"). -spec get_time(document(), list(binary())) -> {ok, time()} | {error, get_error()}. get_time(Doc, Key) -> case get_value(Doc, Key) of {ok, {time, Source_text}} -> {ok, {time, Source_text}}; {ok, _} -> {error, {wrong_type, Key, expected_time}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 791). ?DOC(" Read a TOML date-time value at a key path.\n"). -spec get_datetime(document(), list(binary())) -> {ok, date_time()} | {error, get_error()}. get_datetime(Doc, Key) -> case get_value(Doc, Key) of {ok, {date_time, Source_text}} -> {ok, {date_time, Source_text}}; {ok, _} -> {error, {wrong_type, Key, expected_date_time}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 835). -spec top_level_keys(list({list(binary()), value()})) -> list(binary()). top_level_keys(Entries) -> _pipe = Entries, _pipe@1 = gleam@list:filter_map( _pipe, fun(Entry) -> case erlang:element(1, Entry) of [First | _] -> {ok, First}; [] -> {error, nil} end end ), gleam@list:unique(_pipe@1). -file("src/tomlet.gleam", 821). ?DOC( " Return the top-level keys of the table at a key path, in source order.\n" "\n" " Works on standard tables (`[table]`) and inline tables (`{ ... }`).\n" " Dotted keys and subtables collapse to their first segment, so\n" " `a.b` and `[t.sub]` both contribute a single `\"a\"` / `\"sub\"` key, and\n" " duplicates are removed while preserving first-occurrence order.\n" "\n" " A missing path yields `KeyNotFound`. A path that resolves to a non-table\n" " value (including an array of tables) yields `WrongType`. The\n" " `ExpectedType` reported for the non-table case is a placeholder until a\n" " dedicated `ExpectedTable` variant is introduced (see issue #28); match on\n" " the `WrongType` constructor rather than the specific `ExpectedType`.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.parse(\"[dependencies]\\ngleam_stdlib = \\\">= 0.40.0\\\"\\n\")\n" " tomlet.table_keys(doc, [\"dependencies\"])\n" " // -> Ok([\"gleam_stdlib\"])\n" " ```\n" ). -spec table_keys(document(), list(binary())) -> {ok, list(binary())} | {error, get_error()}. table_keys(Doc, Path) -> case get(Doc, Path) of {ok, {standard_table_value, Entries}} -> {ok, top_level_keys(Entries)}; {ok, {inline_table_value, Entries@1}} -> {ok, top_level_keys(Entries@1)}; {ok, _} -> {error, {wrong_type, Path, expected_string}}; {error, Error} -> {error, Error} end. -file("src/tomlet.gleam", 852). ?DOC( " Read a string from a `Value`.\n" "\n" " Mirrors `get_string`, but operates on a `Value` already obtained via `get`,\n" " so nested data can be decoded without re-walking from the document root. On\n" " a type mismatch the error carries an empty key path, since a bare `Value`\n" " has no path context.\n" ). -spec as_string(value()) -> {ok, binary()} | {error, get_error()}. as_string(Value) -> case Value of {string_value, Text} -> {ok, Text}; _ -> {error, {wrong_type, [], expected_string}} end. -file("src/tomlet.gleam", 860). ?DOC(" Read an integer from a `Value`. See `as_string` for the error convention.\n"). -spec as_int(value()) -> {ok, integer()} | {error, get_error()}. as_int(Value) -> case Value of {int_value, Number} -> {ok, Number}; _ -> {error, {wrong_type, [], expected_int}} end. -file("src/tomlet.gleam", 868). ?DOC(" Read a boolean from a `Value`. See `as_string` for the error convention.\n"). -spec as_bool(value()) -> {ok, boolean()} | {error, get_error()}. as_bool(Value) -> case Value of {bool_value, Boolean} -> {ok, Boolean}; _ -> {error, {wrong_type, [], expected_bool}} end. -file("src/tomlet.gleam", 879). ?DOC( " Read a float from a `Value`. See `as_string` for the error convention.\n" "\n" " Special floats (`inf`, `-inf`, `nan`) are not returned here; reading one\n" " yields `WrongType`. Match on `SpecialFloatValue` for those.\n" ). -spec as_float(value()) -> {ok, float()} | {error, get_error()}. as_float(Value) -> case Value of {float_value, Number} -> {ok, Number}; _ -> {error, {wrong_type, [], expected_float}} end. -file("src/tomlet.gleam", 887). ?DOC(" Read a local date from a `Value`. See `as_string` for the error convention.\n"). -spec as_date(value()) -> {ok, date()} | {error, get_error()}. as_date(Value) -> case Value of {date_value, Date} -> {ok, Date}; _ -> {error, {wrong_type, [], expected_date}} end. -file("src/tomlet.gleam", 895). ?DOC(" Read a local time from a `Value`. See `as_string` for the error convention.\n"). -spec as_time(value()) -> {ok, time()} | {error, get_error()}. as_time(Value) -> case Value of {time_value, Time} -> {ok, Time}; _ -> {error, {wrong_type, [], expected_time}} end. -file("src/tomlet.gleam", 903). ?DOC(" Read a date-time from a `Value`. See `as_string` for the error convention.\n"). -spec as_datetime(value()) -> {ok, date_time()} | {error, get_error()}. as_datetime(Value) -> case Value of {date_time_value, Datetime} -> {ok, Datetime}; _ -> {error, {wrong_type, [], expected_date_time}} end. -file("src/tomlet.gleam", 2344). -spec padded_hex(integer()) -> binary(). padded_hex(Value) -> Hex@1 = case gleam@int:to_base_string(Value, 16) of {ok, Hex} -> Hex; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"tomlet"/utf8>>, function => <<"padded_hex"/utf8>>, line => 2348, value => _assert_fail, start => 68936, 'end' => 68986, pattern_start => 68947, pattern_end => 68954}) end, case string:length(Hex@1) of 1 -> <<"000"/utf8, Hex@1/binary>>; 2 -> <<"00"/utf8, Hex@1/binary>>; 3 -> <<"0"/utf8, Hex@1/binary>>; _ -> Hex@1 end. -file("src/tomlet.gleam", 2323). -spec escape_basic_string_codepoints(list(integer())) -> binary(). escape_basic_string_codepoints(Codepoints) -> case Codepoints of [] -> <<""/utf8>>; [Codepoint | Rest] -> Codepoint_int = gleam_stdlib:identity(Codepoint), Escaped = case Codepoint_int of 8 -> <<"\\b"/utf8>>; 9 -> <<"\\t"/utf8>>; 10 -> <<"\\n"/utf8>>; 12 -> <<"\\f"/utf8>>; 13 -> <<"\\r"/utf8>>; 34 -> <<"\\\""/utf8>>; 92 -> <<"\\\\"/utf8>>; I when (I < 32) orelse (I =:= 127) -> <<"\\u"/utf8, (padded_hex(I))/binary>>; _ -> gleam_stdlib:utf_codepoint_list_to_string([Codepoint]) end, <> end. -file("src/tomlet.gleam", 2317). -spec escape_basic_string(binary()) -> binary(). escape_basic_string(Value) -> _pipe = Value, _pipe@1 = gleam@string:to_utf_codepoints(_pipe), escape_basic_string_codepoints(_pipe@1). -file("src/tomlet.gleam", 2313). -spec basic_string_repr(binary()) -> binary(). basic_string_repr(Value) -> <<<<"\""/utf8, (escape_basic_string(Value))/binary>>/binary, "\""/utf8>>. -file("src/tomlet.gleam", 2267). -spec key_segment_from_string(binary()) -> tomlet@ast:key_segment(). key_segment_from_string(Segment) -> case tomlet@key:is_bare_key(Segment) of true -> {bare_key_segment, Segment}; false -> {quoted_key_segment, Segment, basic_string_repr(Segment)} end. -file("src/tomlet.gleam", 2210). -spec new_key_value(binary(), tomlet@ast:value()) -> tomlet@ast:entry(). new_key_value(Key, Value) -> {key_value, {trivia, <<""/utf8>>}, {key, [key_segment_from_string(Key)]}, Value, {trivia, <<"\n"/utf8>>}}. -file("src/tomlet.gleam", 2263). -spec key_from_strings(list(binary())) -> tomlet@ast:key(). key_from_strings(Segments) -> {key, gleam@list:map(Segments, fun key_segment_from_string/1)}. -file("src/tomlet.gleam", 2228). -spec new_table_header(list(binary())) -> tomlet@ast:entry(). new_table_header(Key) -> {table_header, {header, key_from_strings(Key), standard_table, {trivia, <<""/utf8>>}}}. -file("src/tomlet.gleam", 2121). -spec append_inside_table(list(tomlet@ast:entry()), tomlet@ast:entry()) -> {list(tomlet@ast:entry()), boolean()}. append_inside_table(Entries, New_entry) -> case Entries of [] -> {[New_entry], true}; [Entry | Rest] -> case Entry of {table_header, _} -> {[New_entry, Entry | Rest], true}; _ -> {Updated_rest, Found} = append_inside_table(Rest, New_entry), {[Entry | Updated_rest], Found} end end. -file("src/tomlet.gleam", 2089). -spec append_table_entry( list(tomlet@ast:entry()), list(binary()), tomlet@ast:entry() ) -> {list(tomlet@ast:entry()), boolean()}. append_table_entry(Entries, Parent, New_entry) -> case Entries of [] -> {[], false}; [Entry | Rest] -> case Entry of {table_header, Header} -> case (header_key(Header) =:= Parent) andalso header_is_standard_table( Header ) of true -> {Updated_rest, Found} = append_inside_table( Rest, New_entry ), {[Entry | Updated_rest], Found}; false -> {Updated_rest@1, Found@1} = append_table_entry( Rest, Parent, New_entry ), {[Entry | Updated_rest@1], Found@1} end; _ -> {Updated_rest@2, Found@2} = append_table_entry( Rest, Parent, New_entry ), {[Entry | Updated_rest@2], Found@2} end end. -file("src/tomlet.gleam", 2075). -spec append_root_entry(list(tomlet@ast:entry()), tomlet@ast:entry()) -> list(tomlet@ast:entry()). append_root_entry(Entries, New_entry) -> case Entries of [] -> [New_entry]; [Entry | Rest] -> case Entry of {table_header, _} -> [New_entry, Entry | Rest]; _ -> [Entry | append_root_entry(Rest, New_entry)] end end. -file("src/tomlet.gleam", 2057). -spec append_new_entry( list(tomlet@ast:entry()), list(binary()), tomlet@ast:entry() ) -> list(tomlet@ast:entry()). append_new_entry(Entries, Parent, New_entry) -> case Parent of [] -> append_root_entry(Entries, New_entry); _ -> {Updated_entries, Found} = append_table_entry( Entries, Parent, New_entry ), case Found of true -> Updated_entries; false -> lists:append(Entries, [new_table_header(Parent), New_entry]) end end. -file("src/tomlet.gleam", 2219). -spec new_dotted_key_value(list(binary()), tomlet@ast:value()) -> tomlet@ast:entry(). new_dotted_key_value(Path, Value) -> {key_value, {trivia, <<""/utf8>>}, key_from_strings(Path), Value, {trivia, <<"\n"/utf8>>}}. -file("src/tomlet.gleam", 2030). -spec dotted_key_context( list(tomlet@ast:entry()), list(binary()), list(binary()) ) -> {ok, list(binary())} | {error, nil}. dotted_key_context(Entries, Active_table, Parent) -> case Entries of [] -> {error, nil}; [Entry | Rest] -> Next_active_table = case Entry of {table_header, Header} -> header_key(Header); _ -> Active_table end, case Entry of {key_value, _, Key, _, _} -> Full_key = lists:append(Active_table, key_to_strings(Key)), gleam@bool:guard( tomlet@key:starts_with(Full_key, Parent) andalso (Full_key /= Parent), {ok, Active_table}, fun() -> dotted_key_context(Rest, Next_active_table, Parent) end ); _ -> dotted_key_context(Rest, Next_active_table, Parent) end end. -file("src/tomlet.gleam", 2007). -spec insert_appended_entry( list(tomlet@ast:entry()), list(binary()), list(binary()), binary(), tomlet@ast:value() ) -> list(tomlet@ast:entry()). insert_appended_entry(Entries, Key, Parent, Leaf, Value) -> Dotted_anchor = case Parent of [] -> {error, nil}; _ -> dotted_key_context(Entries, [], Parent) end, case Dotted_anchor of {ok, Anchor} -> Relative = gleam@list:drop(Key, erlang:length(Anchor)), append_new_entry( Entries, Anchor, new_dotted_key_value(Relative, Value) ); {error, nil} -> append_new_entry(Entries, Parent, new_key_value(Leaf, Value)) end. -file("src/tomlet.gleam", 2236). -spec parent_and_leaf(list(binary())) -> {ok, {list(binary()), binary()}} | {error, nil}. parent_and_leaf(Key) -> case Key of [] -> {error, nil}; [Leaf] -> {ok, {[], Leaf}}; [Segment | Rest] -> case parent_and_leaf(Rest) of {ok, {Parent, Leaf@1}} -> {ok, {[Segment | Parent], Leaf@1}}; {error, nil} -> {error, nil} end end. -file("src/tomlet.gleam", 2206). -spec key_path_conflicts(list(binary()), list(binary())) -> boolean(). key_path_conflicts(Existing, Target) -> tomlet@key:conflicts(Existing, Target). -file("src/tomlet.gleam", 2194). -spec header_conflicts_with_new_key(tomlet@ast:header(), list(binary())) -> boolean(). header_conflicts_with_new_key(Header, Target) -> {header, _, Kind, _} = Header, Key = header_key(Header), case Kind of standard_table -> (Target =:= Key) orelse tomlet@key:starts_with(Key, Target); array_of_tables_header -> key_path_conflicts(Key, Target) end. -file("src/tomlet.gleam", 2166). -spec new_key_conflicts_with_table( list(tomlet@ast:entry()), list(binary()), list(binary()) ) -> boolean(). new_key_conflicts_with_table(Entries, Active_table, Target) -> case Entries of [] -> false; [Entry | Rest] -> Next_active_table = case Entry of {table_header, Header} -> header_key(Header); _ -> Active_table end, case Entry of {table_header, Header@1} -> header_conflicts_with_new_key(Header@1, Target) orelse new_key_conflicts_with_table( Rest, Next_active_table, Target ); {key_value, _, Key, _, _} -> Full_key = lists:append(Active_table, key_to_strings(Key)), key_path_conflicts(Full_key, Target) orelse new_key_conflicts_with_table( Rest, Next_active_table, Target ); _ -> new_key_conflicts_with_table( Rest, Next_active_table, Target ) end end. -file("src/tomlet.gleam", 2138). -spec new_key_conflicts(list(tomlet@ast:entry()), list(binary())) -> boolean(). new_key_conflicts(Entries, Target) -> new_key_conflicts_with_table(Entries, [], Target). -file("src/tomlet.gleam", 2142). -spec inline_table_blocks_key( list(tomlet@ast:entry()), list(binary()), list(binary()) ) -> boolean(). inline_table_blocks_key(Entries, Active_table, Target) -> case Entries of [] -> false; [Entry | Rest] -> Next_active_table = case Entry of {table_header, Header} -> header_key(Header); _ -> Active_table end, Blocks = case Entry of {key_value, _, Key, {inline_table, _, _}, _} -> Full_key = lists:append(Active_table, key_to_strings(Key)), tomlet@key:starts_with(Target, Full_key) andalso (Full_key /= Target); _ -> false end, Blocks orelse inline_table_blocks_key( Rest, Next_active_table, Target ) end. -file("src/tomlet.gleam", 1787). -spec emit_inline_table_entry(tomlet@ast:inline_table_entry()) -> binary(). emit_inline_table_entry(Entry) -> {inline_table_entry, Leading, Key, Value, Trailing} = Entry, <<<<<<<<(emit_trivia(Leading))/binary, (emit_key(Key))/binary>>/binary, " = "/utf8>>/binary, (emit_value(Value))/binary>>/binary, (emit_trivia(Trailing))/binary>>. -file("src/tomlet.gleam", 1773). -spec emit_inline_table(list(tomlet@ast:inline_table_entry())) -> binary(). emit_inline_table(Entries) -> case Entries of [] -> <<"{}"/utf8>>; _ -> <<<<"{ "/utf8, (begin _pipe = Entries, _pipe@1 = gleam@list:map( _pipe, fun emit_inline_table_entry/1 ), gleam@string:join(_pipe@1, <<", "/utf8>>) end)/binary>>/binary, " }"/utf8>> end. -file("src/tomlet.gleam", 1948). -spec update_inline_entry( tomlet@ast:trivia(), tomlet@ast:key(), tomlet@ast:value(), tomlet@ast:trivia(), list(tomlet@ast:inline_table_entry()), list(binary()), list(binary()), tomlet@ast:value() ) -> {list(tomlet@ast:inline_table_entry()), boolean()}. update_inline_entry( Leading, Key, Entry_value, Trailing, Rest, Active_path, Target, Value ) -> Full_key = lists:append(Active_path, key_to_strings(Key)), gleam@bool:guard( Full_key =:= Target, {[{inline_table_entry, Leading, Key, Value, Trailing} | Rest], true}, fun() -> case Entry_value of {inline_table, Nested_entries, _} -> {Updated_nested_entries, Nested_found} = update_inline_entries( Nested_entries, Full_key, Target, Value ), gleam@bool:lazy_guard( not Nested_found, fun() -> {Updated_rest, Found} = update_inline_entries( Rest, Active_path, Target, Value ), {[{inline_table_entry, Leading, Key, Entry_value, Trailing} | Updated_rest], Found} end, fun() -> Updated_value = {inline_table, Updated_nested_entries, emit_inline_table(Updated_nested_entries)}, {[{inline_table_entry, Leading, Key, Updated_value, Trailing} | Rest], true} end ); _ -> {Updated_rest@1, Found@1} = update_inline_entries( Rest, Active_path, Target, Value ), {[{inline_table_entry, Leading, Key, Entry_value, Trailing} | Updated_rest@1], Found@1} end end ). -file("src/tomlet.gleam", 1918). -spec update_inline_entries( list(tomlet@ast:inline_table_entry()), list(binary()), list(binary()), tomlet@ast:value() ) -> {list(tomlet@ast:inline_table_entry()), boolean()}. update_inline_entries(Entries, Active_path, Target, Value) -> case Entries of [] -> {[], false}; [{inline_table_entry, Leading, Key, Entry_value, Trailing} | Rest] -> update_inline_entry( Leading, Key, Entry_value, Trailing, Rest, Active_path, Target, Value ) end. -file("src/tomlet.gleam", 1834). -spec update_key_value_entry( tomlet@ast:entry(), tomlet@ast:trivia(), tomlet@ast:key(), tomlet@ast:value(), tomlet@ast:trivia(), list(tomlet@ast:entry()), list(binary()), list(binary()), list(binary()), tomlet@ast:value() ) -> {list(tomlet@ast:entry()), boolean()}. update_key_value_entry( Entry, Leading, Key, Entry_value, Trailing, Rest, Active_table, Next_active_table, Target, Value ) -> Full_key = lists:append(Active_table, key_to_strings(Key)), gleam@bool:guard( Full_key =:= Target, {[{key_value, Leading, Key, Value, Trailing} | Rest], true}, fun() -> case Entry_value of {inline_table, Inline_entries, _} -> {Updated_inline_entries, Inline_found} = update_inline_entries( Inline_entries, Full_key, Target, Value ), gleam@bool:lazy_guard( not Inline_found, fun() -> {Updated_rest, Found} = update_existing_entries( Rest, Next_active_table, Target, Value ), {[Entry | Updated_rest], Found} end, fun() -> Updated_value = {inline_table, Updated_inline_entries, emit_inline_table(Updated_inline_entries)}, {[{key_value, Leading, Key, Updated_value, Trailing} | Rest], true} end ); _ -> {Updated_rest@1, Found@1} = update_existing_entries( Rest, Next_active_table, Target, Value ), {[Entry | Updated_rest@1], Found@1} end end ). -file("src/tomlet.gleam", 1875). -spec update_existing_entries( list(tomlet@ast:entry()), list(binary()), list(binary()), tomlet@ast:value() ) -> {list(tomlet@ast:entry()), boolean()}. update_existing_entries(Entries, Active_table, Target, Value) -> case Entries of [] -> {[], false}; [Entry | Rest] -> Next_active_table = case Entry of {table_header, Header} -> header_key(Header); _ -> Active_table end, case Entry of {key_value, Leading, Key, Entry_value, Trailing} -> update_key_value_entry( Entry, Leading, Key, Entry_value, Trailing, Rest, Active_table, Next_active_table, Target, Value ); _ -> {Updated_rest, Found} = update_existing_entries( Rest, Next_active_table, Target, Value ), {[Entry | Updated_rest], Found} end end. -file("src/tomlet.gleam", 2281). -spec validate_key_segments(list(binary())) -> {ok, nil} | {error, edit_error()}. validate_key_segments(Key) -> case Key of [] -> {ok, nil}; [Segment | Rest] -> case gleam_stdlib:contains_string(Segment, <<"\n"/utf8>>) orelse gleam_stdlib:contains_string( Segment, <<"\r"/utf8>> ) of true -> {error, {invalid_key_segment, Segment}}; false -> validate_key_segments(Rest) end end. -file("src/tomlet.gleam", 2274). -spec validate_edit_key(list(binary())) -> {ok, nil} | {error, edit_error()}. validate_edit_key(Key) -> case Key of [] -> {error, empty_key_path}; _ -> validate_key_segments(Key) end. -file("src/tomlet.gleam", 1801). -spec set_value(document(), list(binary()), tomlet@ast:value()) -> {ok, document()} | {error, edit_error()}. set_value(Doc, Key, Value) -> gleam@result:'try'( validate_edit_key(Key), fun(_) -> {table, Entries, Header} = erlang:element(2, Doc), {Updated_entries, Found} = update_existing_entries( Entries, [], Key, Value ), gleam@bool:guard( Found, {ok, with_root(Doc, {table, Updated_entries, Header})}, fun() -> gleam@bool:guard( inline_table_blocks_key(Entries, [], Key), {error, {inline_table_insert_unsupported, Key}}, fun() -> gleam@bool:guard( new_key_conflicts(Entries, Key), {error, {key_conflict, Key}}, fun() -> gleam@result:'try'( gleam@result:replace_error( parent_and_leaf(Key), empty_key_path ), fun(_use0) -> {Parent, Leaf} = _use0, Appended_entries = insert_appended_entry( Updated_entries, Key, Parent, Leaf, Value ), {ok, with_root( Doc, {table, Appended_entries, Header} )} end ) end ) end ) end ) end ). -file("src/tomlet.gleam", 1137). ?DOC( " Set a TOML string value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.set_string(tomlet.new(), [\"package\", \"name\"], \"tomlet\")\n" " tomlet.to_string(doc)\n" " // -> \"\n" " // [package]\n" " // name = \\\"tomlet\\\"\n" " // \"\n" " ```\n" ). -spec set_string(document(), list(binary()), binary()) -> {ok, document()} | {error, edit_error()}. set_string(Doc, Key, Value) -> set_value(Doc, Key, {string, Value, basic_string, basic_string_repr(Value)}). -file("src/tomlet.gleam", 1153). ?DOC( " Set a TOML integer value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_int(document(), list(binary()), integer()) -> {ok, document()} | {error, edit_error()}. set_int(Doc, Key, Value) -> set_value(Doc, Key, {int, Value, erlang:integer_to_binary(Value)}). -file("src/tomlet.gleam", 1165). ?DOC( " Set a TOML boolean value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_bool(document(), list(binary()), boolean()) -> {ok, document()} | {error, edit_error()}. set_bool(Doc, Key, Value) -> Repr = case Value of true -> <<"true"/utf8>>; false -> <<"false"/utf8>> end, set_value(Doc, Key, {bool, Value, Repr}). -file("src/tomlet.gleam", 1181). ?DOC( " Set a TOML float value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_float(document(), list(binary()), float()) -> {ok, document()} | {error, edit_error()}. set_float(Doc, Key, Value) -> set_value(Doc, Key, {float, Value, gleam_stdlib:float_to_string(Value)}). -file("src/tomlet.gleam", 1193). ?DOC( " Set a TOML local date value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_date(document(), list(binary()), date()) -> {ok, document()} | {error, edit_error()}. set_date(Doc, Key, Value) -> set_value(Doc, Key, {date, erlang:element(2, Value)}). -file("src/tomlet.gleam", 1205). ?DOC( " Set a TOML local time value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_time(document(), list(binary()), time()) -> {ok, document()} | {error, edit_error()}. set_time(Doc, Key, Value) -> set_value(Doc, Key, {time, erlang:element(2, Value)}). -file("src/tomlet.gleam", 1217). ?DOC( " Set a TOML date-time value at a key path.\n" "\n" " Existing values are replaced in place. Missing keys are inserted, creating a\n" " table header when needed.\n" ). -spec set_datetime(document(), list(binary()), date_time()) -> {ok, document()} | {error, edit_error()}. set_datetime(Doc, Key, Value) -> set_value(Doc, Key, {date_time, erlang:element(2, Value)}). -file("src/tomlet.gleam", 1443). -spec emit_array_items(list(tomlet@ast:array_item())) -> binary(). emit_array_items(Items) -> case Items of [] -> <<"[]"/utf8>>; _ -> <<<<"["/utf8, (begin _pipe = Items, _pipe@1 = gleam@list:map( _pipe, fun(Item) -> {array_item, _, Value, _} = Item, emit_value(Value) end ), gleam@string:join(_pipe@1, <<", "/utf8>>) end)/binary>>/binary, "]"/utf8>> end. -file("src/tomlet.gleam", 1430). -spec value_to_inline_entry({list(binary()), value()}) -> {ok, tomlet@ast:inline_table_entry()} | {error, edit_error()}. value_to_inline_entry(Entry) -> {Path, Value} = Entry, gleam@result:'try'( value_to_ast(Value), fun(Ast_value) -> {ok, {inline_table_entry, {trivia, <<""/utf8>>}, key_from_strings(Path), Ast_value, {trivia, <<""/utf8>>}}} end ). -file("src/tomlet.gleam", 1359). -spec value_to_ast(value()) -> {ok, tomlet@ast:value()} | {error, edit_error()}. value_to_ast(Value) -> case Value of {string_value, S} -> {ok, {string, S, basic_string, basic_string_repr(S)}}; {int_value, I} -> {ok, {int, I, erlang:integer_to_binary(I)}}; {float_value, F} -> {ok, {float, F, gleam_stdlib:float_to_string(F)}}; {special_float_value, S@1} -> {Internal, Source_text} = case S@1 of positive_infinity -> {positive_infinity, <<"inf"/utf8>>}; negative_infinity -> {negative_infinity, <<"-inf"/utf8>>}; not_a_number -> {not_a_number, <<"nan"/utf8>>} end, {ok, {special_float, Internal, Source_text}}; {bool_value, B} -> Repr = case B of true -> <<"true"/utf8>>; false -> <<"false"/utf8>> end, {ok, {bool, B, Repr}}; {date_value, D} -> {ok, {date, erlang:element(2, D)}}; {time_value, T} -> {ok, {time, erlang:element(2, T)}}; {date_time_value, D@1} -> {ok, {date_time, erlang:element(2, D@1)}}; {array_value, Items} -> gleam@result:'try'( gleam@list:try_map(Items, fun value_to_array_item/1), fun(Ast_items) -> {ok, {array, Ast_items, emit_array_items(Ast_items)}} end ); {inline_table_value, Entries} -> gleam@result:'try'( gleam@list:try_map(Entries, fun value_to_inline_entry/1), fun(Ast_entries) -> {ok, {inline_table, Ast_entries, emit_inline_table(Ast_entries)}} end ); {standard_table_value, _} -> {error, invalid_value}; {array_of_tables_value, _} -> {error, invalid_value} end. -file("src/tomlet.gleam", 1394). -spec value_to_array_item(value()) -> {ok, tomlet@ast:array_item()} | {error, edit_error()}. value_to_array_item(Value) -> gleam@result:'try'( value_to_ast(Value), fun(Ast_value) -> {ok, {array_item, {trivia, <<""/utf8>>}, Ast_value, {trivia, <<""/utf8>>}}} end ). -file("src/tomlet.gleam", 1488). -spec table_entry_conflicts(list(list(binary())), list(binary())) -> boolean(). table_entry_conflicts(Seen, Path) -> case Seen of [] -> false; [Existing | Rest] -> key_path_conflicts(Existing, Path) orelse table_entry_conflicts( Rest, Path ) end. -file("src/tomlet.gleam", 1466). -spec validate_table_entries_loop( list({list(binary()), value()}), list(list(binary())) ) -> {ok, nil} | {error, edit_error()}. validate_table_entries_loop(Entries, Seen) -> case Entries of [] -> {ok, nil}; [{Path, Value} | Rest] -> case validate_edit_key(Path) of {error, Error} -> {error, Error}; {ok, nil} -> case table_entry_conflicts(Seen, Path) of true -> {error, {key_conflict, Path}}; false -> case validate_value(Value) of {error, Error@1} -> {error, Error@1}; {ok, nil} -> validate_table_entries_loop( Rest, [Path | Seen] ) end end end end. -file("src/tomlet.gleam", 1460). -spec validate_table_entries(list({list(binary()), value()})) -> {ok, nil} | {error, edit_error()}. validate_table_entries(Entries) -> validate_table_entries_loop(Entries, []). -file("src/tomlet.gleam", 1414). -spec validate_value(value()) -> {ok, nil} | {error, edit_error()}. validate_value(Value) -> case Value of {string_value, _} -> {ok, nil}; {int_value, _} -> {ok, nil}; {float_value, _} -> {ok, nil}; {special_float_value, _} -> {ok, nil}; {bool_value, _} -> {ok, nil}; {date_value, _} -> {ok, nil}; {time_value, _} -> {ok, nil}; {date_time_value, _} -> {ok, nil}; {array_value, Items} -> validate_values(Items); {inline_table_value, Entries} -> validate_table_entries(Entries); {standard_table_value, _} -> {error, invalid_value}; {array_of_tables_value, _} -> {error, invalid_value} end. -file("src/tomlet.gleam", 1403). -spec validate_values(list(value())) -> {ok, nil} | {error, edit_error()}. validate_values(Values) -> case Values of [] -> {ok, nil}; [Value | Rest] -> case validate_value(Value) of {error, Error} -> {error, Error}; {ok, nil} -> validate_values(Rest) end end. -file("src/tomlet.gleam", 1244). ?DOC( " Set a TOML array value at a key path.\n" "\n" " Items are emitted in order using a default flow-style representation\n" " (`[a, b, c]`). Existing values are replaced in place. Missing keys are\n" " inserted, creating a table header when needed.\n" "\n" " `StandardTableValue` and `ArrayOfTablesValue` items are rejected with\n" " `InvalidValue`; use `set_inline_table` or `append_array_of_tables` for\n" " table-shaped values.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.set_array(tomlet.new(), [\"ports\"], [\n" " tomlet.IntValue(8000),\n" " tomlet.IntValue(8001),\n" " ])\n" " tomlet.to_string(doc)\n" " // -> \"ports = [8000, 8001]\\n\"\n" " ```\n" ). -spec set_array(document(), list(binary()), list(value())) -> {ok, document()} | {error, edit_error()}. set_array(Doc, Key, Items) -> case validate_values(Items) of {error, Error} -> {error, Error}; {ok, nil} -> gleam@result:'try'( gleam@list:try_map(Items, fun value_to_array_item/1), fun(Ast_items) -> set_value( Doc, Key, {array, Ast_items, emit_array_items(Ast_items)} ) end ) end. -file("src/tomlet.gleam", 1277). ?DOC( " Set a TOML inline table value at a key path.\n" "\n" " Entries are emitted in order using a default flow-style representation\n" " (`{ a = 1, b = 2 }`). Each entry's key path is rendered as a dotted key\n" " when it contains more than one segment. Existing values are replaced in\n" " place. Missing keys are inserted, creating a table header when needed.\n" "\n" " Entry values that are `StandardTableValue` or `ArrayOfTablesValue` are\n" " rejected with `InvalidValue`; nest an `InlineTableValue` instead.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.set_inline_table(tomlet.new(), [\"pkg\"], [\n" " #([\"name\"], tomlet.StringValue(\"tomato\")),\n" " #([\"meta\", \"downloads\"], tomlet.IntValue(42)),\n" " ])\n" " tomlet.to_string(doc)\n" " // -> \"pkg = { name = \\\"tomato\\\", meta.downloads = 42 }\\n\"\n" " ```\n" ). -spec set_inline_table( document(), list(binary()), list({list(binary()), value()}) ) -> {ok, document()} | {error, edit_error()}. set_inline_table(Doc, Key, Entries) -> case validate_table_entries(Entries) of {error, Error} -> {error, Error}; {ok, nil} -> gleam@result:'try'( gleam@list:try_map(Entries, fun value_to_inline_entry/1), fun(Ast_entries) -> set_value( Doc, Key, {inline_table, Ast_entries, emit_inline_table(Ast_entries)} ) end ) end. -file("src/tomlet.gleam", 1496). -spec entry_conflicts_with_target( tomlet@ast:entry(), list(binary()), boolean(), list(binary()) ) -> boolean(). entry_conflicts_with_target(Entry, Active_table, In_array_of_tables, Target) -> case Entry of {table_header, {header, Key, standard_table, _}} -> key_to_strings(Key) =:= Target; {table_header, {header, Key@1, array_of_tables_header, _}} -> Header_key = key_to_strings(Key@1), (Header_key /= Target) andalso key_path_conflicts( Header_key, Target ); {key_value, _, Key@2, _, _} -> case In_array_of_tables of true -> false; false -> Full_key = lists:append(Active_table, key_to_strings(Key@2)), key_path_conflicts(Full_key, Target) end; _ -> false end. -file("src/tomlet.gleam", 1530). -spec array_of_tables_key_conflicts( list(tomlet@ast:entry()), list(binary()), boolean(), list(binary()) ) -> boolean(). array_of_tables_key_conflicts(Entries, Active_table, In_array_of_tables, Target) -> case Entries of [] -> false; [Entry | Rest] -> {Next_active_table, Next_in_aot} = case Entry of {table_header, {header, Key, Kind, _}} -> {key_to_strings(Key), Kind =:= array_of_tables_header}; _ -> {Active_table, In_array_of_tables} end, Conflicts = entry_conflicts_with_target( Entry, Active_table, In_array_of_tables, Target ), Conflicts orelse array_of_tables_key_conflicts( Rest, Next_active_table, Next_in_aot, Target ) end. -file("src/tomlet.gleam", 1312). ?DOC( " Append a new table to an array of tables at a key path.\n" "\n" " A `[[key]]` header is appended to the document followed by the supplied\n" " entries. Works whether or not an array of tables already exists at the key\n" " path; if no array of tables exists yet, a new one is created.\n" "\n" " ```gleam\n" " let assert Ok(doc) =\n" " tomlet.append_array_of_tables(tomlet.new(), [\"packages\"], [\n" " #([\"name\"], tomlet.StringValue(\"tomato\")),\n" " ])\n" " tomlet.to_string(doc)\n" " // -> \"\n" " // [[packages]]\n" " // name = \\\"tomato\\\"\n" " // \"\n" " ```\n" ). -spec append_array_of_tables( document(), list(binary()), list({list(binary()), value()}) ) -> {ok, document()} | {error, edit_error()}. append_array_of_tables(Doc, Key, Entries) -> gleam@result:'try'( validate_edit_key(Key), fun(_) -> gleam@result:'try'( validate_table_entries(Entries), fun(_) -> {document, {table, Doc_entries, Header}, _, _, _} = Doc, gleam@bool:guard( array_of_tables_key_conflicts( Doc_entries, [], false, Key ), {error, {key_conflict, Key}}, fun() -> gleam@result:'try'( gleam@list:try_map( Entries, fun(Entry) -> {Path, Value} = Entry, gleam@result:'try'( value_to_ast(Value), fun(Ast_value) -> {ok, {key_value, {trivia, <<""/utf8>>}, key_from_strings(Path), Ast_value, {trivia, <<"\n"/utf8>>}}} end ) end ), fun(Table_entries) -> New_entries = lists:append( [{table_header, {header, key_from_strings(Key), array_of_tables_header, {trivia, <<""/utf8>>}}}], Table_entries ), {ok, with_root( Doc, {table, lists:append( Doc_entries, New_entries ), Header} )} end ) end ) end ) end ). -file("src/tomlet.gleam", 1688). -spec remove_entries(list(tomlet@ast:entry()), list(binary()), list(binary())) -> {list(tomlet@ast:entry()), boolean()}. remove_entries(Entries, Active_table, Target) -> case Entries of [] -> {[], false}; [Entry | Rest] -> case Entry of {table_header, {header, Key, _, _}} -> Table_key = key_to_strings(Key), {Next_rest, Removed} = remove_entries( Rest, Table_key, Target ), {[Entry | Next_rest], Removed}; {key_value, _, Key@1, _, _} -> Full_key = lists:append(Active_table, key_to_strings(Key@1)), {Next_rest@1, Removed@1} = remove_entries( Rest, Active_table, Target ), case Full_key =:= Target of true -> {Next_rest@1, true}; false -> {[Entry | Next_rest@1], Removed@1} end; _ -> {Next_rest@2, Removed@2} = remove_entries( Rest, Active_table, Target ), {[Entry | Next_rest@2], Removed@2} end end. -file("src/tomlet.gleam", 1568). ?DOC( " Remove an existing value from a document.\n" "\n" " Returns `MissingEditKey` when the key path does not exist, and\n" " `EmptyKeyPath` when the key path is empty.\n" ). -spec remove(document(), list(binary())) -> {ok, document()} | {error, edit_error()}. remove(Doc, Key) -> case validate_edit_key(Key) of {error, Error} -> {error, Error}; {ok, nil} -> {document, {table, Entries, Header}, _, _, _} = Doc, {Next_entries, Removed} = remove_entries(Entries, [], Key), case Removed of true -> {ok, with_root(Doc, {table, Next_entries, Header})}; false -> {error, {missing_edit_key, Key}} end end. -file("src/tomlet.gleam", 1666). -spec normalize_comment_text(binary()) -> binary(). normalize_comment_text(Text) -> Trimmed = gleam@string:trim(Text), case Trimmed of <<""/utf8>> -> <<"#"/utf8>>; _ -> case gleam_stdlib:string_starts_with(Trimmed, <<"#"/utf8>>) of true -> Trimmed; false -> <<"# "/utf8, Trimmed/binary>> end end. -file("src/tomlet.gleam", 1627). -spec insert_comment_before_entries( list(tomlet@ast:entry()), list(binary()), list(binary()), tomlet@ast:entry() ) -> {list(tomlet@ast:entry()), boolean()}. insert_comment_before_entries(Entries, Target, Active_table, Comment) -> case Entries of [] -> {[], false}; [Entry | Rest] -> case Entry of {table_header, Header} -> Table_key = header_key(Header), gleam@bool:guard( Table_key =:= Target, {[Comment, Entry | Rest], true}, fun() -> {Updated_rest, Inserted} = insert_comment_before_entries( Rest, Target, Table_key, Comment ), {[Entry | Updated_rest], Inserted} end ); {key_value, _, Entry_key, _, _} -> Full_key = lists:append( Active_table, key_to_strings(Entry_key) ), gleam@bool:guard( Full_key =:= Target, {[Comment, Entry | Rest], true}, fun() -> {Updated_rest@1, Inserted@1} = insert_comment_before_entries( Rest, Target, Active_table, Comment ), {[Entry | Updated_rest@1], Inserted@1} end ); _ -> {Updated_rest@2, Inserted@2} = insert_comment_before_entries( Rest, Target, Active_table, Comment ), {[Entry | Updated_rest@2], Inserted@2} end end. -file("src/tomlet.gleam", 2298). -spec validate_comment_codepoints(list(integer())) -> {ok, nil} | {error, edit_error()}. validate_comment_codepoints(Codepoints) -> case Codepoints of [] -> {ok, nil}; [Codepoint | Rest] -> Value = gleam_stdlib:identity(Codepoint), case ((Value =< 8) orelse ((Value >= 10) andalso (Value =< 31))) orelse (Value =:= 127) of true -> {error, invalid_comment_text}; false -> validate_comment_codepoints(Rest) end end. -file("src/tomlet.gleam", 2292). -spec validate_comment_text(binary()) -> {ok, nil} | {error, edit_error()}. validate_comment_text(Text) -> _pipe = Text, _pipe@1 = gleam@string:to_utf_codepoints(_pipe), validate_comment_codepoints(_pipe@1). -file("src/tomlet.gleam", 1600). ?DOC( " Insert a standalone comment before an existing key.\n" "\n" " The comment text may include a leading `#`, but must not contain TOML\n" " comment control characters. Returns `MissingEditKey` when the target key\n" " does not exist, `InvalidCommentText` when the comment is unsafe to emit, and\n" " `EmptyKeyPath` when the key path is empty.\n" "\n" " ```gleam\n" " let assert Ok(doc) = tomlet.parse(\"released = 1979-05-27\\n\")\n" " let assert Ok(doc) =\n" " tomlet.insert_comment_before(doc, [\"released\"], \"release date\")\n" " tomlet.to_string(doc)\n" " // -> \"\n" " // # release date\n" " // released = 1979-05-27\n" " // \"\n" " ```\n" ). -spec insert_comment_before(document(), list(binary()), binary()) -> {ok, document()} | {error, edit_error()}. insert_comment_before(Doc, Key, Text) -> case {validate_edit_key(Key), validate_comment_text(Text)} of {{error, Error}, _} -> {error, Error}; {_, {error, Error@1}} -> {error, Error@1}; {{ok, nil}, {ok, nil}} -> {document, {table, Entries, Header}, _, _, _} = Doc, {Updated_entries, Inserted} = insert_comment_before_entries( Entries, Key, [], {comment, normalize_comment_text(Text)} ), case Inserted of true -> {ok, with_root(Doc, {table, Updated_entries, Header})}; false -> {error, {missing_edit_key, Key}} end end.