-module(aws@internal@ini).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/ini.gleam").
-export([parse/1, get_property/3]).
-export_type([parse_error/0, line_kind/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(
" Minimal INI parser, sized for the AWS shared credentials file and the\n"
" flat parts of `~/.aws/config`. Supports the subset:\n"
"\n"
" - `[section-name]` headers\n"
" - `key = value` properties (whitespace around `=` trimmed)\n"
" - `#` and `;` whole-line comments\n"
" - blank lines\n"
"\n"
" Out of scope for now (we'll grow into these when SSO and assume-role\n"
" providers need them):\n"
"\n"
" - line continuation for multi-line values\n"
" - sub-properties (nested key/value inside a property)\n"
" - inline `#`/`;` comments mid-value\n"
"\n"
" Errors carry the offending line number so the surfaced message can\n"
" point the user at the broken line of their credentials file.\n"
).
-type parse_error() :: {parse_error, integer(), binary()}.
-type line_kind() :: blank |
comment |
{section, binary()} |
{property, binary(), binary()} |
{malformed, binary()}.
-file("src/aws/internal/ini.gleam", 123).
-spec commit_section(
binary(),
gleam@dict:dict(binary(), binary()),
gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary()))
) -> gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary())).
commit_section(Name, Props, Ini) ->
case Name of
<<""/utf8>> ->
Ini;
_ ->
gleam@dict:insert(Ini, Name, Props)
end.
-file("src/aws/internal/ini.gleam", 109).
-spec classify_property(binary()) -> line_kind().
classify_property(Line) ->
case gleam@string:split_once(Line, <<"="/utf8>>) of
{error, _} ->
{malformed, <<"expected 'key = value'"/utf8>>};
{ok, {Key, Value}} ->
Key@1 = gleam@string:trim(Key),
Value@1 = gleam@string:trim(Value),
case Key@1 of
<<""/utf8>> ->
{malformed, <<"property name is empty"/utf8>>};
_ ->
{property, Key@1, Value@1}
end
end.
-file("src/aws/internal/ini.gleam", 97).
-spec classify_section(binary()) -> line_kind().
classify_section(Line) ->
case gleam_stdlib:string_ends_with(Line, <<"]"/utf8>>) of
false ->
{malformed, <<"section header missing closing ']'"/utf8>>};
true ->
Inner = gleam@string:slice(Line, 1, string:length(Line) - 2),
{section, gleam@string:trim(Inner)}
end.
-file("src/aws/internal/ini.gleam", 85).
-spec classify(binary()) -> line_kind().
classify(Line) ->
case Line of
<<""/utf8>> ->
blank;
_ ->
case gleam@string:slice(Line, 0, 1) of
<<"#"/utf8>> ->
comment;
<<";"/utf8>> ->
comment;
<<"["/utf8>> ->
classify_section(Line);
_ ->
classify_property(Line)
end
end.
-file("src/aws/internal/ini.gleam", 39).
-spec parse_lines(
list({integer(), binary()}),
binary(),
gleam@dict:dict(binary(), binary()),
gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary()))
) -> {ok, gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary()))} |
{error, parse_error()}.
parse_lines(Lines, Current_section, Current_props, Ini) ->
case Lines of
[] ->
{ok, commit_section(Current_section, Current_props, Ini)};
[{Line_no, Raw} | Rest] ->
Trimmed = gleam@string:trim(Raw),
case classify(Trimmed) of
blank ->
parse_lines(Rest, Current_section, Current_props, Ini);
comment ->
parse_lines(Rest, Current_section, Current_props, Ini);
{section, Name} ->
New_ini = commit_section(
Current_section,
Current_props,
Ini
),
parse_lines(Rest, Name, maps:new(), New_ini);
{property, Key, Value} ->
case Current_section of
<<""/utf8>> ->
{error,
{parse_error,
Line_no,
<<<<"property '"/utf8, Key/binary>>/binary,
"' outside any section"/utf8>>}};
_ ->
parse_lines(
Rest,
Current_section,
gleam@dict:insert(Current_props, Key, Value),
Ini
)
end;
{malformed, Message} ->
{error, {parse_error, Line_no, Message}}
end
end.
-file("src/aws/internal/ini.gleam", 32).
-spec parse(binary()) -> {ok,
gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary()))} |
{error, parse_error()}.
parse(Text) ->
_pipe = Text,
_pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
_pipe@2 = gleam@list:index_map(_pipe@1, fun(Line, I) -> {I + 1, Line} end),
parse_lines(_pipe@2, <<""/utf8>>, maps:new(), maps:new()).
-file("src/aws/internal/ini.gleam", 131).
?DOC(" Look up a single property value inside a section.\n").
-spec get_property(
gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary())),
binary(),
binary()
) -> {ok, binary()} | {error, nil}.
get_property(Ini, Section, Key) ->
gleam@result:'try'(
gleam_stdlib:map_get(Ini, Section),
fun(Props) -> gleam_stdlib:map_get(Props, Key) end
).