Skip to main content

src/aws@internal@providers@process.erl

-module(aws@internal@providers@process).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/providers/process.gleam").
-export([fetch/2]).
-export_type([process_credentials/0, error/0, raw_output/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(
    " Credential-process provider. Runs an external command and reads JSON\n"
    " credentials from its standard output, per the AWS CLI's\n"
    " `credential_process` spec.\n"
    "\n"
    " Expected output:\n"
    "   {\n"
    "     \"Version\": 1,\n"
    "     \"AccessKeyId\": \"...\",\n"
    "     \"SecretAccessKey\": \"...\",\n"
    "     \"SessionToken\": \"...\",        // optional\n"
    "     \"Expiration\": \"ISO 8601 Z\"    // optional; absent = non-expiring\n"
    "   }\n"
).

-type process_credentials() :: {process_credentials,
        binary(),
        binary(),
        gleam@option:option(binary()),
        gleam@option:option(integer())}.

-type error() :: {launch_failed, binary()} | {bad_output, binary()}.

-type raw_output() :: {raw_output,
        integer(),
        binary(),
        binary(),
        gleam@option:option(binary()),
        gleam@option:option(binary())}.

-file("src/aws/internal/providers/process.gleam", 119).
-spec raw_decoder() -> gleam@dynamic@decode:decoder(raw_output()).
raw_decoder() ->
    gleam@dynamic@decode:field(
        <<"Version"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_int/1},
        fun(Version) ->
            gleam@dynamic@decode:field(
                <<"AccessKeyId"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_string/1},
                fun(Access_key_id) ->
                    gleam@dynamic@decode:field(
                        <<"SecretAccessKey"/utf8>>,
                        {decoder, fun gleam@dynamic@decode:decode_string/1},
                        fun(Secret_access_key) ->
                            gleam@dynamic@decode:then(
                                gleam@dynamic@decode:optionally_at(
                                    [<<"SessionToken"/utf8>>],
                                    none,
                                    gleam@dynamic@decode:map(
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_string/1},
                                        fun(Field@0) -> {some, Field@0} end
                                    )
                                ),
                                fun(Session_token) ->
                                    gleam@dynamic@decode:then(
                                        gleam@dynamic@decode:optionally_at(
                                            [<<"Expiration"/utf8>>],
                                            none,
                                            gleam@dynamic@decode:map(
                                                {decoder,
                                                    fun gleam@dynamic@decode:decode_string/1},
                                                fun(Field@0) -> {some, Field@0} end
                                            )
                                        ),
                                        fun(Expiration) ->
                                            gleam@dynamic@decode:success(
                                                {raw_output,
                                                    Version,
                                                    Access_key_id,
                                                    Secret_access_key,
                                                    Session_token,
                                                    Expiration}
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/aws/internal/providers/process.gleam", 142).
-spec decode_credentials(bitstring()) -> {ok, process_credentials()} |
    {error, error()}.
decode_credentials(Stdout) ->
    gleam@result:'try'(
        begin
            _pipe = gleam@bit_array:to_string(Stdout),
            gleam@result:replace_error(
                _pipe,
                {bad_output, <<"non-utf8 stdout"/utf8>>}
            )
        end,
        fun(Text) ->
            gleam@result:'try'(
                begin
                    _pipe@1 = gleam@json:parse(Text, raw_decoder()),
                    gleam@result:map_error(
                        _pipe@1,
                        fun(_) ->
                            {bad_output,
                                <<"stdout is not the expected credential_process JSON"/utf8>>}
                        end
                    )
                end,
                fun(Raw) -> case erlang:element(2, Raw) of
                        1 ->
                            Expires_at = case erlang:element(6, Raw) of
                                {some, Ts} ->
                                    case aws_ffi:parse_iso8601(Ts) of
                                        {ok, T} ->
                                            {some, T};

                                        {error, _} ->
                                            none
                                    end;

                                none ->
                                    none
                            end,
                            {ok,
                                {process_credentials,
                                    erlang:element(3, Raw),
                                    erlang:element(4, Raw),
                                    erlang:element(5, Raw),
                                    Expires_at}};

                        Other ->
                            {error,
                                {bad_output,
                                    <<"unsupported credential_process Version "/utf8,
                                        (erlang:integer_to_binary(Other))/binary>>}}
                    end end
            )
        end
    ).

-file("src/aws/internal/providers/process.gleam", 100).
-spec do_reverse(list(HLW), list(HLW)) -> list(HLW).
do_reverse(Xs, Acc) ->
    case Xs of
        [] ->
            Acc;

        [H | T] ->
            do_reverse(T, [H | Acc])
    end.

-file("src/aws/internal/providers/process.gleam", 96).
-spec list_reverse(list(HLT)) -> list(HLT).
list_reverse(Xs) ->
    do_reverse(Xs, []).

-file("src/aws/internal/providers/process.gleam", 85).
-spec do_filter(list(HLP), fun((HLP) -> boolean()), list(HLP)) -> list(HLP).
do_filter(Xs, Keep, Acc) ->
    case Xs of
        [] ->
            list_reverse(Acc);

        [H | T] ->
            case Keep(H) of
                true ->
                    do_filter(T, Keep, [H | Acc]);

                false ->
                    do_filter(T, Keep, Acc)
            end
    end.

-file("src/aws/internal/providers/process.gleam", 81).
-spec list_filter(list(HLM), fun((HLM) -> boolean())) -> list(HLM).
list_filter(Xs, Keep) ->
    do_filter(Xs, Keep, []).

-file("src/aws/internal/providers/process.gleam", 70).
-spec split_command(binary()) -> {ok, {binary(), list(binary())}} | {error, nil}.
split_command(Command) ->
    Parts = begin
        _pipe = Command,
        _pipe@1 = gleam@string:split(_pipe, <<" "/utf8>>),
        list_filter(_pipe@1, fun(S) -> S /= <<""/utf8>> end)
    end,
    case Parts of
        [] ->
            {error, nil};

        [Program | Args] ->
            {ok, {Program, Args}}
    end.

-file("src/aws/internal/providers/process.gleam", 45).
?DOC(
    " Run the given command line and decode its stdout as credential-process\n"
    " JSON. `command` is the verbatim string from `credential_process`; we\n"
    " split it on whitespace into program + arguments before launch.\n"
).
-spec fetch(
    fun((binary(), list(binary())) -> {ok, {integer(), bitstring()}} |
        {error, nil}),
    binary()
) -> {ok, process_credentials()} | {error, error()}.
fetch(Runner, Command) ->
    gleam@result:'try'(
        begin
            _pipe = split_command(Command),
            gleam@result:replace_error(
                _pipe,
                {launch_failed, <<"empty credential_process command"/utf8>>}
            )
        end,
        fun(_use0) ->
            {Program, Args} = _use0,
            gleam@result:'try'(
                begin
                    _pipe@1 = Runner(Program, Args),
                    gleam@result:replace_error(
                        _pipe@1,
                        {launch_failed,
                            <<<<"could not run '"/utf8, Program/binary>>/binary,
                                "'"/utf8>>}
                    )
                end,
                fun(_use0@1) ->
                    {Exit, Stdout} = _use0@1,
                    case Exit of
                        0 ->
                            decode_credentials(Stdout);

                        Code ->
                            {error,
                                {bad_output,
                                    <<"credential_process exited with status "/utf8,
                                        (erlang:integer_to_binary(Code))/binary>>}}
                    end
                end
            )
        end
    ).