-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 = <<Key/binary, " = "/utf8>>,
Source = <<<<Prefix/binary, Input/binary>>/binary, "\n"/utf8>>,
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} ->
<<Text/binary, "\n"/utf8>>;
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 => <<?FILEPATH/utf8>>,
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,
<<Escaped/binary, (escape_basic_string_codepoints(Rest))/binary>>
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.