Skip to main content

src/aws@endpoints.erl

-module(aws@endpoints).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/endpoints.gleam").
-export([params_from/2, parse_rule_set/1, resolve/2]).
-export_type([endpoint/0, resolve_error/0, value/0, rule_set/0, parameter/0, param_type/0, rule/0, endpoint_spec/0, condition/0, expr/0, template_part/0, partition/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(
    " Smithy endpoint rule set evaluator.\n"
    "\n"
    " Implements the runtime side of the Smithy rules engine: parses a\n"
    " `endpoint-rule-set.json` document, then walks it against a set of\n"
    " parameter values and produces a concrete endpoint URL.\n"
    "\n"
    " Supported features:\n"
    "\n"
    "   - Rule types: `endpoint`, `error`, `tree`\n"
    "   - Parameters: `String`, `Boolean`, with `default` and `required`\n"
    "   - Templates: `{Name}` and `{Var#field}` (and nested `{Var#a#b}`)\n"
    "   - Built-in functions: `isSet`, `not`, `booleanEquals`, `stringEquals`,\n"
    "     `getAttr`, `substring`, `uriEncode`, `parseURL`, `isValidHostLabel`,\n"
    "     `aws.partition`, `aws.parseArn`, `aws.isVirtualHostableS3Bucket`\n"
    "   - Common partition data hardcoded (`aws`, `aws-cn`, `aws-us-gov`).\n"
    "     For a richer table, vendor `partitions.json` and replace\n"
    "     `partition_for/1`.\n"
    "\n"
    " Not implemented (rare; flagged at evaluation time as `Unsupported`):\n"
    "\n"
    "   - `StringArray` parameter type (used by a few new services)\n"
    "\n"
    " Codegen at milestone 7 will use this evaluator at compile time to emit\n"
    " per-service endpoint resolvers; the runtime fallback path here keeps\n"
    " hand-written services working before any code is generated.\n"
).

-type endpoint() :: {endpoint,
        binary(),
        gleam@dict:dict(binary(), list(binary()))}.

-type resolve_error() :: {rule_error, binary()} |
    no_match |
    {invalid_rule_set, binary()} |
    {unsupported, binary()} |
    {missing_parameter, binary()} |
    {required_parameter_missing, binary()}.

-type value() :: {string_val, binary()} |
    {bool_val, boolean()} |
    {record_val, gleam@dict:dict(binary(), value())} |
    {list_val, list(value())} |
    empty_val.

-type rule_set() :: {rule_set,
        gleam@dict:dict(binary(), parameter()),
        list(rule())}.

-type parameter() :: {parameter,
        param_type(),
        boolean(),
        gleam@option:option(value()),
        gleam@option:option(binary())}.

-type param_type() :: string_type | boolean_type.

-type rule() :: {endpoint_rule, list(condition()), endpoint_spec()} |
    {error_rule, list(condition()), expr()} |
    {tree_rule, list(condition()), list(rule())}.

-type endpoint_spec() :: {endpoint_spec,
        expr(),
        gleam@dict:dict(binary(), list(expr()))}.

-type condition() :: {condition, expr(), gleam@option:option(binary())}.

-type expr() :: {ref, binary()} |
    {template_expr, list(template_part())} |
    {bool_lit, boolean()} |
    {int_lit, integer()} |
    {fn_call, binary(), list(expr())}.

-type template_part() :: {static, binary()} | {interp, list(binary())}.

-type partition() :: aws_commercial |
    aws_cn |
    aws_us_gov |
    aws_eusc |
    aws_iso |
    aws_iso_b |
    aws_iso_e |
    aws_iso_f.

-file("src/aws/endpoints.gleam", 77).
?DOC(
    " Convenience constructor: build a `Params` map from string and boolean\n"
    " pairs. The most common shape (Region + UseFips + UseDualStack).\n"
).
-spec params_from(list({binary(), binary()}), list({binary(), boolean()})) -> gleam@dict:dict(binary(), value()).
params_from(Strings, Bools) ->
    S_dict = gleam@list:fold(
        Strings,
        maps:new(),
        fun(Acc, Pair) ->
            gleam@dict:insert(
                Acc,
                erlang:element(1, Pair),
                {string_val, erlang:element(2, Pair)}
            )
        end
    ),
    gleam@list:fold(
        Bools,
        S_dict,
        fun(Acc@1, Pair@1) ->
            gleam@dict:insert(
                Acc@1,
                erlang:element(1, Pair@1),
                {bool_val, erlang:element(2, Pair@1)}
            )
        end
    ).

-file("src/aws/endpoints.gleam", 302).
-spec describe_decode_error(gleam@json:decode_error()) -> binary().
describe_decode_error(Err) ->
    case Err of
        {unexpected_byte, B} ->
            <<"unexpected byte: "/utf8, B/binary>>;

        unexpected_end_of_input ->
            <<"unexpected end of input"/utf8>>;

        {unexpected_sequence, _} ->
            <<"unexpected sequence in JSON"/utf8>>;

        {unable_to_decode, _} ->
            <<"JSON did not match rule set schema"/utf8>>
    end.

-file("src/aws/endpoints.gleam", 215).
-spec error_rule_failsafe() -> rule().
error_rule_failsafe() ->
    {error_rule, [], {template_expr, [{static, <<"unknown rule"/utf8>>}]}}.

-file("src/aws/endpoints.gleam", 321).
-spec do_parse_template(binary(), binary(), list(template_part())) -> list(template_part()).
do_parse_template(Remaining, Static_buf, Acc) ->
    case gleam@string:split_once(Remaining, <<"{"/utf8>>) of
        {error, _} ->
            lists:reverse(
                [{static, <<Static_buf/binary, Remaining/binary>>} | Acc]
            );

        {ok, {Before_brace, Rest_with_brace}} ->
            case gleam@string:split_once(Rest_with_brace, <<"}"/utf8>>) of
                {error, _} ->
                    lists:reverse(
                        [{static, <<Static_buf/binary, Remaining/binary>>} |
                            Acc]
                    );

                {ok, {Interp_body, After_close}} ->
                    New_static = <<Static_buf/binary, Before_brace/binary>>,
                    Acc@1 = case New_static of
                        <<""/utf8>> ->
                            Acc;

                        _ ->
                            [{static, New_static} | Acc]
                    end,
                    Path = gleam@string:split(Interp_body, <<"#"/utf8>>),
                    Acc@2 = [{interp, Path} | Acc@1],
                    do_parse_template(After_close, <<""/utf8>>, Acc@2)
            end
    end.

-file("src/aws/endpoints.gleam", 317).
?DOC(
    " Split a raw template string into static + interpolation segments.\n"
    " `{Region}` becomes `Interp([\"Region\"])`. `{var#field#sub}` becomes\n"
    " `Interp([\"var\", \"field\", \"sub\"])`. Literal `{{` and `}}` are NOT supported\n"
    " — none of the AWS rule sets in scope use escaped braces.\n"
).
-spec parse_template(binary()) -> list(template_part()).
parse_template(Text) ->
    do_parse_template(Text, <<""/utf8>>, []).

-file("src/aws/endpoints.gleam", 271).
-spec string_expr_decoder() -> gleam@dynamic@decode:decoder(expr()).
string_expr_decoder() ->
    gleam@dynamic@decode:map(
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(S) -> {template_expr, parse_template(S)} end
    ).

-file("src/aws/endpoints.gleam", 252).
-spec int_decoder() -> gleam@dynamic@decode:decoder(expr()).
int_decoder() ->
    gleam@dynamic@decode:map(
        {decoder, fun gleam@dynamic@decode:decode_int/1},
        fun(Field@0) -> {int_lit, Field@0} end
    ).

-file("src/aws/endpoints.gleam", 261).
-spec bool_decoder() -> gleam@dynamic@decode:decoder(expr()).
bool_decoder() ->
    gleam@dynamic@decode:map(
        {decoder, fun gleam@dynamic@decode:decode_bool/1},
        fun(Field@0) -> {bool_lit, Field@0} end
    ).

-file("src/aws/endpoints.gleam", 256).
-spec ref_decoder() -> gleam@dynamic@decode:decoder(expr()).
ref_decoder() ->
    gleam@dynamic@decode:field(
        <<"ref"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Name) -> gleam@dynamic@decode:success({ref, Name}) end
    ).

-file("src/aws/endpoints.gleam", 265).
-spec fn_call_decoder() -> gleam@dynamic@decode:decoder(expr()).
fn_call_decoder() ->
    gleam@dynamic@decode:field(
        <<"fn"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Fn_name) ->
            gleam@dynamic@decode:field(
                <<"argv"/utf8>>,
                gleam@dynamic@decode:list(expr_decoder()),
                fun(Argv) ->
                    gleam@dynamic@decode:success({fn_call, Fn_name, Argv})
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 243).
?DOC(
    " Decoder for an `Expr` — i.e. anything that can appear in an `argv` slot\n"
    " of a function call or as the value of an endpoint property. Smithy rules\n"
    " allow several shapes here, so we use `one_of` to try each in turn.\n"
).
-spec expr_decoder() -> gleam@dynamic@decode:decoder(expr()).
expr_decoder() ->
    gleam@dynamic@decode:one_of(
        ref_decoder(),
        [bool_decoder(),
            int_decoder(),
            fn_call_decoder(),
            string_expr_decoder()]
    ).

-file("src/aws/endpoints.gleam", 226).
-spec condition_decoder() -> gleam@dynamic@decode:decoder(condition()).
condition_decoder() ->
    gleam@dynamic@decode:field(
        <<"fn"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Fn_name) ->
            gleam@dynamic@decode:field(
                <<"argv"/utf8>>,
                gleam@dynamic@decode:list(expr_decoder()),
                fun(Argv) ->
                    gleam@dynamic@decode:then(
                        gleam@dynamic@decode:optionally_at(
                            [<<"assign"/utf8>>],
                            none,
                            gleam@dynamic@decode:map(
                                {decoder,
                                    fun gleam@dynamic@decode:decode_string/1},
                                fun(Field@0) -> {some, Field@0} end
                            )
                        ),
                        fun(Assign) ->
                            gleam@dynamic@decode:success(
                                {condition, {fn_call, Fn_name, Argv}, Assign}
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 222).
-spec conditions_decoder() -> gleam@dynamic@decode:decoder(list(condition())).
conditions_decoder() ->
    gleam@dynamic@decode:list(condition_decoder()).

-file("src/aws/endpoints.gleam", 290).
-spec error_rule_decoder() -> gleam@dynamic@decode:decoder(rule()).
error_rule_decoder() ->
    gleam@dynamic@decode:field(
        <<"conditions"/utf8>>,
        conditions_decoder(),
        fun(Conditions) ->
            gleam@dynamic@decode:field(
                <<"error"/utf8>>,
                expr_decoder(),
                fun(Message) ->
                    gleam@dynamic@decode:success(
                        {error_rule, Conditions, Message}
                    )
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 276).
?DOC(" Endpoint rule fields: `endpoint = { url, properties, headers }`.\n").
-spec endpoint_rule_decoder() -> gleam@dynamic@decode:decoder(rule()).
endpoint_rule_decoder() ->
    gleam@dynamic@decode:field(
        <<"conditions"/utf8>>,
        conditions_decoder(),
        fun(Conditions) ->
            gleam@dynamic@decode:subfield(
                [<<"endpoint"/utf8>>, <<"url"/utf8>>],
                expr_decoder(),
                fun(Url) ->
                    gleam@dynamic@decode:then(
                        gleam@dynamic@decode:optionally_at(
                            [<<"endpoint"/utf8>>, <<"headers"/utf8>>],
                            maps:new(),
                            gleam@dynamic@decode:dict(
                                {decoder,
                                    fun gleam@dynamic@decode:decode_string/1},
                                gleam@dynamic@decode:list(expr_decoder())
                            )
                        ),
                        fun(Headers) ->
                            gleam@dynamic@decode:success(
                                {endpoint_rule,
                                    Conditions,
                                    {endpoint_spec, Url, Headers}}
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 296).
-spec tree_rule_decoder() -> gleam@dynamic@decode:decoder(rule()).
tree_rule_decoder() ->
    gleam@dynamic@decode:field(
        <<"conditions"/utf8>>,
        conditions_decoder(),
        fun(Conditions) ->
            gleam@dynamic@decode:field(
                <<"rules"/utf8>>,
                gleam@dynamic@decode:list(rule_decoder()),
                fun(Rules) ->
                    gleam@dynamic@decode:success({tree_rule, Conditions, Rules})
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 205).
-spec rule_decoder() -> gleam@dynamic@decode:decoder(rule()).
rule_decoder() ->
    gleam@dynamic@decode:field(
        <<"type"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Type_str) -> case Type_str of
                <<"endpoint"/utf8>> ->
                    endpoint_rule_decoder();

                <<"error"/utf8>> ->
                    error_rule_decoder();

                <<"tree"/utf8>> ->
                    tree_rule_decoder();

                _ ->
                    gleam@dynamic@decode:failure(
                        error_rule_failsafe(),
                        <<"unknown rule type"/utf8>>
                    )
            end end
    ).

-file("src/aws/endpoints.gleam", 199).
-spec value_decoder() -> gleam@dynamic@decode:decoder(value()).
value_decoder() ->
    gleam@dynamic@decode:one_of(
        gleam@dynamic@decode:map(
            {decoder, fun gleam@dynamic@decode:decode_string/1},
            fun(Field@0) -> {string_val, Field@0} end
        ),
        [gleam@dynamic@decode:map(
                {decoder, fun gleam@dynamic@decode:decode_bool/1},
                fun(Field@0) -> {bool_val, Field@0} end
            )]
    ).

-file("src/aws/endpoints.gleam", 174).
-spec parameter_decoder() -> gleam@dynamic@decode:decoder(parameter()).
parameter_decoder() ->
    gleam@dynamic@decode:field(
        <<"type"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Type_str) ->
            gleam@dynamic@decode:optional_field(
                <<"required"/utf8>>,
                false,
                {decoder, fun gleam@dynamic@decode:decode_bool/1},
                fun(Required) ->
                    gleam@dynamic@decode:then(
                        gleam@dynamic@decode:optionally_at(
                            [<<"default"/utf8>>],
                            none,
                            gleam@dynamic@decode:map(
                                value_decoder(),
                                fun(Field@0) -> {some, Field@0} end
                            )
                        ),
                        fun(Default) ->
                            gleam@dynamic@decode:then(
                                gleam@dynamic@decode:optionally_at(
                                    [<<"builtIn"/utf8>>],
                                    none,
                                    gleam@dynamic@decode:map(
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_string/1},
                                        fun(Field@0) -> {some, Field@0} end
                                    )
                                ),
                                fun(Builtin) ->
                                    Type_ = case string:lowercase(Type_str) of
                                        <<"string"/utf8>> ->
                                            string_type;

                                        _ ->
                                            boolean_type
                                    end,
                                    gleam@dynamic@decode:success(
                                        {parameter,
                                            Type_,
                                            Required,
                                            Default,
                                            Builtin}
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 170).
-spec parameters_decoder() -> gleam@dynamic@decode:decoder(gleam@dict:dict(binary(), parameter())).
parameters_decoder() ->
    gleam@dynamic@decode:dict(
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        parameter_decoder()
    ).

-file("src/aws/endpoints.gleam", 164).
-spec rule_set_decoder() -> gleam@dynamic@decode:decoder(rule_set()).
rule_set_decoder() ->
    gleam@dynamic@decode:field(
        <<"parameters"/utf8>>,
        parameters_decoder(),
        fun(Parameters) ->
            gleam@dynamic@decode:field(
                <<"rules"/utf8>>,
                gleam@dynamic@decode:list(rule_decoder()),
                fun(Rules) ->
                    gleam@dynamic@decode:success({rule_set, Parameters, Rules})
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 92).
?DOC(
    " Parse a rule set JSON document (as a Gleam string) into the internal AST.\n"
    " The result is reusable across many `resolve` calls with different params.\n"
).
-spec parse_rule_set(binary()) -> {ok, rule_set()} | {error, resolve_error()}.
parse_rule_set(Json_text) ->
    _pipe = gleam@json:parse(Json_text, rule_set_decoder()),
    gleam@result:map_error(
        _pipe,
        fun(Err) -> {invalid_rule_set, describe_decode_error(Err)} end
    ).

-file("src/aws/endpoints.gleam", 458).
-spec is_truthy(value()) -> boolean().
is_truthy(Value) ->
    case Value of
        empty_val ->
            false;

        {bool_val, B} ->
            B;

        {list_val, []} ->
            false;

        {record_val, Fields} ->
            case maps:size(Fields) of
                0 ->
                    false;

                _ ->
                    true
            end;

        _ ->
            true
    end.

-file("src/aws/endpoints.gleam", 900).
-spec is_int_string(binary()) -> boolean().
is_int_string(S) ->
    case gleam_stdlib:parse_int(S) of
        {ok, _} ->
            true;

        {error, _} ->
            false
    end.

-file("src/aws/endpoints.gleam", 893).
-spec all_ip4_segments(list(binary())) -> boolean().
all_ip4_segments(Parts) ->
    case erlang:length(Parts) of
        4 ->
            gleam@list:all(Parts, fun(P) -> is_int_string(P) end);

        _ ->
            false
    end.

-file("src/aws/endpoints.gleam", 880).
-spec looks_like_ip(binary()) -> boolean().
looks_like_ip(Authority) ->
    Host = case gleam@string:split_once(Authority, <<":"/utf8>>) of
        {ok, {H, _}} ->
            H;

        {error, _} ->
            Authority
    end,
    case gleam_stdlib:string_starts_with(Host, <<"["/utf8>>) andalso gleam_stdlib:string_ends_with(
        Host,
        <<"]"/utf8>>
    ) of
        true ->
            true;

        false ->
            all_ip4_segments(gleam@string:split(Host, <<"."/utf8>>))
    end.

-file("src/aws/endpoints.gleam", 1125).
-spec is_lower_alnum(binary()) -> boolean().
is_lower_alnum(C) ->
    case C of
        <<"0"/utf8>> ->
            true;

        <<"1"/utf8>> ->
            true;

        <<"2"/utf8>> ->
            true;

        <<"3"/utf8>> ->
            true;

        <<"4"/utf8>> ->
            true;

        <<"5"/utf8>> ->
            true;

        <<"6"/utf8>> ->
            true;

        <<"7"/utf8>> ->
            true;

        <<"8"/utf8>> ->
            true;

        <<"9"/utf8>> ->
            true;

        _ ->
            case gleam@string:to_utf_codepoints(C) of
                [Cp] ->
                    N = gleam_stdlib:identity(Cp),
                    (N >= 97) andalso (N =< 122);

                _ ->
                    false
            end
    end.

-file("src/aws/endpoints.gleam", 1116).
-spec all_bucket_chars(list(binary())) -> boolean().
all_bucket_chars(Chars) ->
    gleam@list:all(Chars, fun(C) -> case C of
                <<"-"/utf8>> ->
                    true;

                _ ->
                    is_lower_alnum(C)
            end end).

-file("src/aws/endpoints.gleam", 1099).
-spec is_valid_bucket_label(binary()) -> boolean().
is_valid_bucket_label(Label) ->
    Length = string:length(Label),
    Bytes = erlang:byte_size(gleam_stdlib:identity(Label)),
    case ((Length =:= Bytes) andalso (Length >= 3)) andalso (Length =< 63) of
        false ->
            false;

        true ->
            case Label of
                <<""/utf8>> ->
                    false;

                _ ->
                    ((not gleam_stdlib:string_starts_with(Label, <<"-"/utf8>>)
                    andalso not gleam_stdlib:string_ends_with(
                        Label,
                        <<"-"/utf8>>
                    ))
                    andalso all_bucket_chars(gleam@string:to_graphemes(Label)))
                    andalso not looks_like_ip(Label)
            end
    end.

-file("src/aws/endpoints.gleam", 1089).
-spec is_virtual_hostable(binary(), boolean()) -> boolean().
is_virtual_hostable(Name, Allow_subdomains) ->
    case Allow_subdomains of
        false ->
            is_valid_bucket_label(Name);

        true ->
            _pipe = Name,
            _pipe@1 = gleam@string:split(_pipe, <<"."/utf8>>),
            gleam@list:all(_pipe@1, fun is_valid_bucket_label/1)
    end.

-file("src/aws/endpoints.gleam", 986).
-spec do_split_resource(binary(), binary(), list(binary())) -> list(binary()).
do_split_resource(Input, Buf, Acc) ->
    case gleam_stdlib:string_pop_grapheme(Input) of
        {error, _} ->
            lists:reverse([Buf | Acc]);

        {ok, {C, Rest}} ->
            case C of
                <<"/"/utf8>> ->
                    do_split_resource(Rest, <<""/utf8>>, [Buf | Acc]);

                <<":"/utf8>> ->
                    do_split_resource(Rest, <<""/utf8>>, [Buf | Acc]);

                _ ->
                    do_split_resource(Rest, <<Buf/binary, C/binary>>, Acc)
            end
    end.

-file("src/aws/endpoints.gleam", 982).
-spec split_resource_chars(binary()) -> list(binary()).
split_resource_chars(Input) ->
    do_split_resource(Input, <<""/utf8>>, []).

-file("src/aws/endpoints.gleam", 974).
?DOC(
    " Resource-section splitter: break on either `/` or `:`, like the Go SDK's\n"
    " `splitResource`.\n"
).
-spec split_resource_to_value(binary()) -> value().
split_resource_to_value(Resource) ->
    _pipe = Resource,
    _pipe@1 = split_resource_chars(_pipe),
    _pipe@2 = gleam@list:index_map(
        _pipe@1,
        fun(Part, Idx) ->
            {erlang:integer_to_binary(Idx), {string_val, Part}}
        end
    ),
    _pipe@3 = maps:from_list(_pipe@2),
    {record_val, _pipe@3}.

-file("src/aws/endpoints.gleam", 956).
-spec do_split_n(binary(), binary(), integer(), list(binary())) -> list(binary()).
do_split_n(Input, Sep, N, Acc) ->
    case N =< 1 of
        true ->
            lists:reverse([Input | Acc]);

        false ->
            case gleam@string:split_once(Input, Sep) of
                {ok, {Head, Rest}} ->
                    do_split_n(Rest, Sep, N - 1, [Head | Acc]);

                {error, _} ->
                    lists:reverse([Input | Acc])
            end
    end.

-file("src/aws/endpoints.gleam", 952).
?DOC(
    " `strings.SplitN(input, sep, n)` — split into at most `n` parts; the last\n"
    " part keeps any remaining separators as literal characters.\n"
).
-spec split_n(binary(), binary(), integer()) -> list(binary()).
split_n(Input, Sep, N) ->
    do_split_n(Input, Sep, N, []).

-file("src/aws/endpoints.gleam", 928).
?DOC(
    " Port of the Go SDK's `awsrulesfn.ParseARN`\n"
    " (aws-sdk-go-v2/internal/endpoints/awsrulesfn/arn.go). Requires the\n"
    " literal `arn:` prefix, splits into exactly six sections (so colons in\n"
    " the resource section stay intact), requires non-empty partition /\n"
    " service / resource, and splits the resource on either `:` or `/`.\n"
).
-spec parse_arn(binary()) -> value().
parse_arn(Arn) ->
    case gleam_stdlib:string_starts_with(Arn, <<"arn:"/utf8>>) of
        false ->
            empty_val;

        true ->
            case split_n(Arn, <<":"/utf8>>, 6) of
                [_, Partition, Service, Region, Account_id, Resource] when ((Partition =/= <<""/utf8>>) andalso (Service =/= <<""/utf8>>)) andalso (Resource =/= <<""/utf8>>) ->
                    {record_val,
                        maps:from_list(
                            [{<<"partition"/utf8>>, {string_val, Partition}},
                                {<<"service"/utf8>>, {string_val, Service}},
                                {<<"region"/utf8>>, {string_val, Region}},
                                {<<"accountId"/utf8>>, {string_val, Account_id}},
                                {<<"resourceId"/utf8>>,
                                    split_resource_to_value(Resource)}]
                        )};

                _ ->
                    empty_val
            end
    end.

-file("src/aws/endpoints.gleam", 1320).
?DOC(
    " Classify a region into one of the AWS partitions. Mirrors the\n"
    " `regionRegex` patterns from upstream `partitions.json`. Order matters:\n"
    " the longest-prefix matchers (us-isob-, us-isof-) come first, then the\n"
    " generic `us-iso-`.\n"
).
-spec classify_partition(binary()) -> partition().
classify_partition(Region) ->
    case Region of
        <<"aws-cn-global"/utf8>> ->
            aws_cn;

        <<"aws-us-gov-global"/utf8>> ->
            aws_us_gov;

        <<"aws-iso-global"/utf8>> ->
            aws_iso;

        <<"aws-iso-b-global"/utf8>> ->
            aws_iso_b;

        <<"aws-iso-e-global"/utf8>> ->
            aws_iso_e;

        <<"aws-iso-f-global"/utf8>> ->
            aws_iso_f;

        _ ->
            case gleam_stdlib:string_starts_with(Region, <<"cn-"/utf8>>) orelse gleam_stdlib:string_starts_with(
                Region,
                <<"aws-cn-"/utf8>>
            ) of
                true ->
                    aws_cn;

                false ->
                    case gleam_stdlib:string_starts_with(
                        Region,
                        <<"us-gov-"/utf8>>
                    ) of
                        true ->
                            aws_us_gov;

                        false ->
                            case gleam_stdlib:string_starts_with(
                                Region,
                                <<"eusc-"/utf8>>
                            ) of
                                true ->
                                    aws_eusc;

                                false ->
                                    case gleam_stdlib:string_starts_with(
                                        Region,
                                        <<"us-isob-"/utf8>>
                                    ) of
                                        true ->
                                            aws_iso_b;

                                        false ->
                                            case gleam_stdlib:string_starts_with(
                                                Region,
                                                <<"us-isof-"/utf8>>
                                            ) of
                                                true ->
                                                    aws_iso_f;

                                                false ->
                                                    case gleam_stdlib:string_starts_with(
                                                        Region,
                                                        <<"us-iso-"/utf8>>
                                                    ) of
                                                        true ->
                                                            aws_iso;

                                                        false ->
                                                            case gleam_stdlib:string_starts_with(
                                                                Region,
                                                                <<"eu-isoe-"/utf8>>
                                                            ) of
                                                                true ->
                                                                    aws_iso_e;

                                                                false ->
                                                                    aws_commercial
                                                            end
                                                    end
                                            end
                                    end
                            end
                    end
            end
    end.

-file("src/aws/endpoints.gleam", 1227).
?DOC(
    " Hardcoded partition data sourced from smithy-rs's `partitions.json`.\n"
    " Refresh when AWS announces a new partition or DNS suffix.\n"
).
-spec partition_for(binary()) -> gleam@dict:dict(binary(), value()).
partition_for(Region) ->
    Trimmed = gleam@string:trim(Region),
    case classify_partition(Trimmed) of
        aws_commercial ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"amazonaws.com"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.aws"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"us-east-1"/utf8>>}}]
            );

        aws_cn ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-cn"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"amazonaws.com.cn"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.amazonwebservices.com.cn"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"cn-northwest-1"/utf8>>}}]
            );

        aws_us_gov ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-us-gov"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"amazonaws.com"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.aws"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"us-gov-west-1"/utf8>>}}]
            );

        aws_eusc ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-eusc"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"amazonaws.eu"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.amazonwebservices.eu"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"eusc-de-east-1"/utf8>>}}]
            );

        aws_iso ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-iso"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>, {string_val, <<"c2s.ic.gov"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.aws.ic.gov"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"us-iso-east-1"/utf8>>}}]
            );

        aws_iso_b ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-iso-b"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"sc2s.sgov.gov"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.aws.scloud"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"us-isob-east-1"/utf8>>}}]
            );

        aws_iso_e ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-iso-e"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"cloud.adc-e.uk"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.cloud-aws.adc-e.uk"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"eu-isoe-west-1"/utf8>>}}]
            );

        aws_iso_f ->
            maps:from_list(
                [{<<"name"/utf8>>, {string_val, <<"aws-iso-f"/utf8>>}},
                    {<<"dnsSuffix"/utf8>>,
                        {string_val, <<"csp.hci.ic.gov"/utf8>>}},
                    {<<"dualStackDnsSuffix"/utf8>>,
                        {string_val, <<"api.aws.hci.ic.gov"/utf8>>}},
                    {<<"supportsFIPS"/utf8>>, {bool_val, true}},
                    {<<"supportsDualStack"/utf8>>, {bool_val, true}},
                    {<<"implicitGlobalRegion"/utf8>>,
                        {string_val, <<"us-isof-south-1"/utf8>>}}]
            )
    end.

-file("src/aws/endpoints.gleam", 1062).
-spec is_digit(binary()) -> boolean().
is_digit(C) ->
    case C of
        <<"0"/utf8>> ->
            true;

        <<"1"/utf8>> ->
            true;

        <<"2"/utf8>> ->
            true;

        <<"3"/utf8>> ->
            true;

        <<"4"/utf8>> ->
            true;

        <<"5"/utf8>> ->
            true;

        <<"6"/utf8>> ->
            true;

        <<"7"/utf8>> ->
            true;

        <<"8"/utf8>> ->
            true;

        <<"9"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/aws/endpoints.gleam", 1052).
-spec is_upper_alpha(binary()) -> boolean().
is_upper_alpha(C) ->
    case gleam@string:to_utf_codepoints(C) of
        [Cp] ->
            N = gleam_stdlib:identity(Cp),
            (N >= 65) andalso (N =< 90);

        _ ->
            false
    end.

-file("src/aws/endpoints.gleam", 1043).
-spec all_host_label_chars(list(binary())) -> boolean().
all_host_label_chars(Chars) ->
    gleam@list:all(Chars, fun(C) -> case C of
                <<"-"/utf8>> ->
                    true;

                _ ->
                    (is_lower_alnum(C) orelse is_upper_alpha(C)) orelse is_digit(
                        C
                    )
            end end).

-file("src/aws/endpoints.gleam", 1031).
-spec is_one_host_label(binary()) -> boolean().
is_one_host_label(Label) ->
    Length = string:length(Label),
    Bytes = erlang:byte_size(gleam_stdlib:identity(Label)),
    case ((Length =:= Bytes) andalso (Length >= 1)) andalso (Length =< 63) of
        false ->
            false;

        true ->
            (not gleam_stdlib:string_starts_with(Label, <<"-"/utf8>>) andalso not gleam_stdlib:string_ends_with(
                Label,
                <<"-"/utf8>>
            ))
            andalso all_host_label_chars(gleam@string:to_graphemes(Label))
    end.

-file("src/aws/endpoints.gleam", 1021).
-spec is_valid_host_label(binary(), boolean()) -> boolean().
is_valid_host_label(Name, Allow_subdomains) ->
    case Allow_subdomains of
        false ->
            is_one_host_label(Name);

        true ->
            _pipe = Name,
            _pipe@1 = gleam@string:split(_pipe, <<"."/utf8>>),
            gleam@list:all(_pipe@1, fun is_one_host_label/1)
    end.

-file("src/aws/endpoints.gleam", 838).
-spec do_parse_url_parts(binary(), binary()) -> value().
do_parse_url_parts(Scheme, After_scheme) ->
    case gleam_stdlib:contains_string(After_scheme, <<"@"/utf8>>) orelse gleam_stdlib:contains_string(
        After_scheme,
        <<"#"/utf8>>
    ) of
        true ->
            empty_val;

        false ->
            {Authority, Path_and_query} = case gleam@string:split_once(
                After_scheme,
                <<"/"/utf8>>
            ) of
                {ok, {A, Rest}} ->
                    {A, <<"/"/utf8, Rest/binary>>};

                {error, _} ->
                    {After_scheme, <<""/utf8>>}
            end,
            Path = case gleam@string:split_once(Path_and_query, <<"?"/utf8>>) of
                {ok, {P, _}} ->
                    P;

                {error, _} ->
                    Path_and_query
            end,
            Normalized = case Path of
                <<""/utf8>> ->
                    <<"/"/utf8>>;

                _ ->
                    case gleam_stdlib:string_ends_with(Path, <<"/"/utf8>>) of
                        true ->
                            Path;

                        false ->
                            <<Path/binary, "/"/utf8>>
                    end
            end,
            Is_ip = looks_like_ip(Authority),
            {record_val,
                maps:from_list(
                    [{<<"scheme"/utf8>>, {string_val, Scheme}},
                        {<<"authority"/utf8>>, {string_val, Authority}},
                        {<<"path"/utf8>>, {string_val, Path}},
                        {<<"normalizedPath"/utf8>>, {string_val, Normalized}},
                        {<<"isIp"/utf8>>, {bool_val, Is_ip}}]
                )}
    end.

-file("src/aws/endpoints.gleam", 826).
-spec parse_url(binary()) -> value().
parse_url(Url) ->
    case gleam@string:split_once(Url, <<"://"/utf8>>) of
        {error, _} ->
            empty_val;

        {ok, {Scheme, Rest}} ->
            case Scheme of
                <<"http"/utf8>> ->
                    do_parse_url_parts(Scheme, Rest);

                <<"https"/utf8>> ->
                    do_parse_url_parts(Scheme, Rest);

                _ ->
                    empty_val
            end
    end.

-file("src/aws/endpoints.gleam", 1204).
-spec hex_digit(integer()) -> binary().
hex_digit(N) ->
    case N of
        0 ->
            <<"0"/utf8>>;

        1 ->
            <<"1"/utf8>>;

        2 ->
            <<"2"/utf8>>;

        3 ->
            <<"3"/utf8>>;

        4 ->
            <<"4"/utf8>>;

        5 ->
            <<"5"/utf8>>;

        6 ->
            <<"6"/utf8>>;

        7 ->
            <<"7"/utf8>>;

        8 ->
            <<"8"/utf8>>;

        9 ->
            <<"9"/utf8>>;

        10 ->
            <<"A"/utf8>>;

        11 ->
            <<"B"/utf8>>;

        12 ->
            <<"C"/utf8>>;

        13 ->
            <<"D"/utf8>>;

        14 ->
            <<"E"/utf8>>;

        _ ->
            <<"F"/utf8>>
    end.

-file("src/aws/endpoints.gleam", 1198).
-spec hex_byte(integer()) -> binary().
hex_byte(B) ->
    High = B div 16,
    Low = B rem 16,
    <<(hex_digit(High))/binary, (hex_digit(Low))/binary>>.

-file("src/aws/endpoints.gleam", 1188).
-spec is_unreserved_byte(integer()) -> boolean().
is_unreserved_byte(B) ->
    (((((((B >= 16#41) andalso (B =< 16#5A)) orelse ((B >= 16#61) andalso (B =< 16#7A)))
    orelse ((B >= 16#30) andalso (B =< 16#39)))
    orelse (B =:= 16#2D))
    orelse (B =:= 16#5F))
    orelse (B =:= 16#2E))
    orelse (B =:= 16#7E).

-file("src/aws/endpoints.gleam", 1172).
-spec do_uri_encode(bitstring(), binary()) -> binary().
do_uri_encode(Bits, Acc) ->
    case Bits of
        <<>> ->
            Acc;

        <<B, Rest/bitstring>> ->
            case is_unreserved_byte(B) of
                true ->
                    case gleam@bit_array:to_string(<<B>>) of
                        {ok, S} ->
                            do_uri_encode(Rest, <<Acc/binary, S/binary>>);

                        {error, _} ->
                            do_uri_encode(
                                Rest,
                                <<<<Acc/binary, "%"/utf8>>/binary,
                                    (hex_byte(B))/binary>>
                            )
                    end;

                false ->
                    do_uri_encode(
                        Rest,
                        <<<<Acc/binary, "%"/utf8>>/binary,
                            (hex_byte(B))/binary>>
                    )
            end;

        _ ->
            Acc
    end.

-file("src/aws/endpoints.gleam", 1168).
?DOC(
    " Single-component percent-encoding matching `aws/internal/uri.encode_component`.\n"
    " Inlined to avoid a cross-module dependency.\n"
).
-spec aws_uri_encode(binary()) -> binary().
aws_uri_encode(Input) ->
    do_uri_encode(gleam_stdlib:identity(Input), <<""/utf8>>).

-file("src/aws/endpoints.gleam", 801).
-spec percent_encode_uri(binary()) -> binary().
percent_encode_uri(Input) ->
    case Input of
        <<""/utf8>> ->
            <<""/utf8>>;

        _ ->
            aws_uri_encode(Input)
    end.

-file("src/aws/endpoints.gleam", 766).
-spec do_substring(binary(), integer(), integer(), boolean()) -> value().
do_substring(Input, Start, Stop, Reverse) ->
    Length = string:length(Input),
    Bytes = erlang:byte_size(gleam_stdlib:identity(Input)),
    case Bytes /= Length of
        true ->
            empty_val;

        false ->
            case ((Start < 0) orelse (Stop > Length)) orelse (Start >= Stop) of
                true ->
                    empty_val;

                false ->
                    {Real_start, Real_stop} = case Reverse of
                        false ->
                            {Start, Stop};

                        true ->
                            {Length - Stop, Length - Start}
                    end,
                    {string_val,
                        gleam@string:slice(
                            Input,
                            Real_start,
                            Real_stop - Real_start
                        )}
            end
    end.

-file("src/aws/endpoints.gleam", 724).
-spec list_index(list(JOT), integer()) -> {ok, JOT} | {error, nil}.
list_index(Items, Idx) ->
    case {Items, Idx} of
        {[], _} ->
            {error, nil};

        {[Head | _], 0} ->
            {ok, Head};

        {[_ | Rest], N} when N > 0 ->
            list_index(Rest, N - 1);

        {_, _} ->
            {error, nil}
    end.

-file("src/aws/endpoints.gleam", 700).
-spec traverse_value(value(), list(binary())) -> value().
traverse_value(Value, Path) ->
    case Path of
        [] ->
            Value;

        [First | Rest] ->
            case Value of
                {record_val, Fields} ->
                    case gleam_stdlib:map_get(Fields, First) of
                        {ok, Inner} ->
                            traverse_value(Inner, Rest);

                        {error, _} ->
                            empty_val
                    end;

                {list_val, Items} ->
                    case gleam_stdlib:parse_int(First) of
                        {ok, Idx} ->
                            case list_index(Items, Idx) of
                                {ok, Inner@1} ->
                                    traverse_value(Inner@1, Rest);

                                {error, _} ->
                                    empty_val
                            end;

                        {error, _} ->
                            empty_val
                    end;

                _ ->
                    empty_val
            end
    end.

-file("src/aws/endpoints.gleam", 693).
?DOC(
    " Parse a getAttr path into a list of segments. `\"service\"` → `[\"service\"]`,\n"
    " `\"resourceId[0]\"` → `[\"resourceId\", \"0\"]`, `\"a[1][2]\"` → `[\"a\",\"1\",\"2\"]`.\n"
).
-spec parse_attr_path(binary()) -> list(binary()).
parse_attr_path(Path) ->
    _pipe = Path,
    _pipe@1 = gleam@string:split(_pipe, <<"["/utf8>>),
    _pipe@2 = gleam@list:map(
        _pipe@1,
        fun(Segment) ->
            gleam@string:replace(Segment, <<"]"/utf8>>, <<""/utf8>>)
        end
    ),
    gleam@list:filter(_pipe@2, fun(S) -> S /= <<""/utf8>> end).

-file("src/aws/endpoints.gleam", 582).
-spec value_to_string(value()) -> {ok, binary()} | {error, resolve_error()}.
value_to_string(Value) ->
    case Value of
        {string_val, S} ->
            {ok, S};

        {bool_val, true} ->
            {ok, <<"true"/utf8>>};

        {bool_val, false} ->
            {ok, <<"false"/utf8>>};

        empty_val ->
            {ok, <<""/utf8>>};

        {record_val, _} ->
            {error, {unsupported, <<"cannot stringify record"/utf8>>}};

        {list_val, _} ->
            {error, {unsupported, <<"cannot stringify list"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 564).
-spec traverse_record(value(), list(binary())) -> {ok, value()} |
    {error, resolve_error()}.
traverse_record(Value, Path) ->
    case Path of
        [] ->
            {ok, Value};

        [Field | Rest] ->
            case Value of
                {record_val, Fields} ->
                    case gleam_stdlib:map_get(Fields, Field) of
                        {ok, Inner} ->
                            traverse_record(Inner, Rest);

                        {error, _} ->
                            {ok, empty_val}
                    end;

                _ ->
                    {ok, empty_val}
            end
    end.

-file("src/aws/endpoints.gleam", 550).
-spec lookup_path(list(binary()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
lookup_path(Path, Scope) ->
    case Path of
        [] ->
            {ok, empty_val};

        [First | Rest] ->
            case gleam_stdlib:map_get(Scope, First) of
                {error, _} ->
                    {ok, empty_val};

                {ok, Value} ->
                    traverse_record(Value, Rest)
            end
    end.

-file("src/aws/endpoints.gleam", 534).
-spec do_eval_template(
    list(template_part()),
    gleam@dict:dict(binary(), value()),
    binary()
) -> {ok, binary()} | {error, resolve_error()}.
do_eval_template(Parts, Scope, Acc) ->
    case Parts of
        [] ->
            {ok, Acc};

        [{static, Text} | Rest] ->
            do_eval_template(Rest, Scope, <<Acc/binary, Text/binary>>);

        [{interp, Path} | Rest@1] ->
            gleam@result:'try'(
                lookup_path(Path, Scope),
                fun(Value) ->
                    gleam@result:'try'(
                        value_to_string(Value),
                        fun(Text@1) ->
                            do_eval_template(
                                Rest@1,
                                Scope,
                                <<Acc/binary, Text@1/binary>>
                            )
                        end
                    )
                end
            )
    end.

-file("src/aws/endpoints.gleam", 527).
-spec eval_template(list(template_part()), gleam@dict:dict(binary(), value())) -> {ok,
        binary()} |
    {error, resolve_error()}.
eval_template(Parts, Scope) ->
    do_eval_template(Parts, Scope, <<""/utf8>>).

-file("src/aws/endpoints.gleam", 1158).
-spec eval_to_bool(expr(), gleam@dict:dict(binary(), value())) -> {ok,
        boolean()} |
    {error, resolve_error()}.
eval_to_bool(Expr, Scope) ->
    gleam@result:'try'(eval(Expr, Scope), fun(Value) -> case Value of
                {bool_val, B} ->
                    {ok, B};

                _ ->
                    {error, {unsupported, <<"expected a boolean"/utf8>>}}
            end end).

-file("src/aws/endpoints.gleam", 515).
-spec eval_to_string(expr(), gleam@dict:dict(binary(), value())) -> {ok,
        binary()} |
    {error, resolve_error()}.
eval_to_string(Expr, Scope) ->
    gleam@result:'try'(eval(Expr, Scope), fun(Value) -> case Value of
                {string_val, S} ->
                    {ok, S};

                {bool_val, true} ->
                    {ok, <<"true"/utf8>>};

                {bool_val, false} ->
                    {ok, <<"false"/utf8>>};

                empty_val ->
                    {ok, <<""/utf8>>};

                {record_val, _} ->
                    {error,
                        {unsupported, <<"cannot stringify record value"/utf8>>}};

                {list_val, _} ->
                    {error,
                        {unsupported, <<"cannot stringify list value"/utf8>>}}
            end end).

-file("src/aws/endpoints.gleam", 1071).
?DOC(
    " DNS-3-to-63-char rules + S3 bucket constraints: lowercase letters, digits,\n"
    " hyphens; not IP-shaped; doesn't begin or end with a hyphen.\n"
).
-spec bi_is_virtual_hostable_s3_bucket(
    list(expr()),
    gleam@dict:dict(binary(), value())
) -> {ok, value()} | {error, resolve_error()}.
bi_is_virtual_hostable_s3_bucket(Args, Scope) ->
    case Args of
        [Name_expr, Allow_subdomains_expr] ->
            gleam@result:'try'(
                eval_to_string(Name_expr, Scope),
                fun(Name) ->
                    gleam@result:'try'(
                        eval_to_bool(Allow_subdomains_expr, Scope),
                        fun(Allow_subdomains) ->
                            {ok,
                                {bool_val,
                                    is_virtual_hostable(Name, Allow_subdomains)}}
                        end
                    )
                end
            );

        _ ->
            {error,
                {unsupported,
                    <<"aws.isVirtualHostableS3Bucket expects 2 args"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 910).
?DOC(
    " Parse an AWS ARN: `arn:partition:service:region:accountId:resource`. The\n"
    " resource is whatever comes after the fifth colon. Returns `EmptyVal` if\n"
    " the input doesn't have at least 6 colon-separated parts.\n"
).
-spec bi_aws_parse_arn(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_aws_parse_arn(Args, Scope) ->
    case Args of
        [Arg] ->
            gleam@result:'try'(
                eval_to_string(Arg, Scope),
                fun(Arn) -> {ok, parse_arn(Arn)} end
            );

        _ ->
            {error, {unsupported, <<"aws.parseArn expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 733).
-spec bi_aws_partition(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_aws_partition(Args, Scope) ->
    case Args of
        [Region_expr] ->
            gleam@result:'try'(
                eval_to_string(Region_expr, Scope),
                fun(Region) -> {ok, {record_val, partition_for(Region)}} end
            );

        _ ->
            {error, {unsupported, <<"aws.partition expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 1004).
?DOC(
    " RFC 1123 host label validation. Accepts ASCII letters/digits/hyphens, no\n"
    " leading/trailing hyphen, length 1..63. With `allowSubdomains=true` the\n"
    " label may be `.`-separated and each piece is validated independently.\n"
).
-spec bi_is_valid_host_label(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_is_valid_host_label(Args, Scope) ->
    case Args of
        [Name_expr, Allow_subdomains_expr] ->
            gleam@result:'try'(
                eval_to_string(Name_expr, Scope),
                fun(Name) ->
                    gleam@result:'try'(
                        eval_to_bool(Allow_subdomains_expr, Scope),
                        fun(Allow_subdomains) ->
                            {ok,
                                {bool_val,
                                    is_valid_host_label(Name, Allow_subdomains)}}
                        end
                    )
                end
            );

        _ ->
            {error,
                {unsupported, <<"isValidHostLabel expects 2 arguments"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 813).
?DOC(
    " Parse a URL into `{ scheme, authority, path, normalizedPath, isIp }`.\n"
    " Returns `EmptyVal` if the URL doesn't look well-formed. We support the\n"
    " shapes AWS rule sets actually pass us; this is not a full RFC 3986\n"
    " parser.\n"
).
-spec bi_parse_url(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_parse_url(Args, Scope) ->
    case Args of
        [Arg] ->
            gleam@result:'try'(
                eval_to_string(Arg, Scope),
                fun(Url) -> {ok, parse_url(Url)} end
            );

        _ ->
            {error, {unsupported, <<"parseURL expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 788).
?DOC(" Percent-encode a URI component.\n").
-spec bi_uri_encode(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_uri_encode(Args, Scope) ->
    case Args of
        [Arg] ->
            gleam@result:'try'(
                eval_to_string(Arg, Scope),
                fun(Input) -> {ok, {string_val, percent_encode_uri(Input)}} end
            );

        _ ->
            {error, {unsupported, <<"uriEncode expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 1141).
-spec eval_to_int(expr(), gleam@dict:dict(binary(), value())) -> {ok, integer()} |
    {error, resolve_error()}.
eval_to_int(Expr, Scope) ->
    case Expr of
        {int_lit, N} ->
            {ok, N};

        _ ->
            gleam@result:'try'(eval(Expr, Scope), fun(Value) -> case Value of
                        {string_val, S} ->
                            case gleam_stdlib:parse_int(S) of
                                {ok, N@1} ->
                                    {ok, N@1};

                                {error, _} ->
                                    {error,
                                        {unsupported,
                                            <<"non-integer string: "/utf8,
                                                S/binary>>}}
                            end;

                        _ ->
                            {error,
                                {unsupported, <<"expected an integer"/utf8>>}}
                    end end)
    end.

-file("src/aws/endpoints.gleam", 750).
?DOC(
    " `substring(input, start, stop, reverse)` — Smithy spec says the result is\n"
    " empty (returns `EmptyVal`) if any character is non-ASCII, or if the range\n"
    " is invalid. With `reverse=true` the indexes count from the end of the\n"
    " string.\n"
).
-spec bi_substring(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_substring(Args, Scope) ->
    case Args of
        [Input_expr, Start_expr, Stop_expr, Reverse_expr] ->
            gleam@result:'try'(
                eval_to_string(Input_expr, Scope),
                fun(Input) ->
                    gleam@result:'try'(
                        eval_to_int(Start_expr, Scope),
                        fun(Start) ->
                            gleam@result:'try'(
                                eval_to_int(Stop_expr, Scope),
                                fun(Stop) ->
                                    gleam@result:'try'(
                                        eval_to_bool(Reverse_expr, Scope),
                                        fun(Reverse) ->
                                            {ok,
                                                do_substring(
                                                    Input,
                                                    Start,
                                                    Stop,
                                                    Reverse
                                                )}
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            );

        _ ->
            {error, {unsupported, <<"substring expects 4 arguments"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 677).
-spec bi_get_attr(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_get_attr(Args, Scope) ->
    case Args of
        [Obj, Key] ->
            gleam@result:'try'(
                eval(Obj, Scope),
                fun(Record) ->
                    gleam@result:'try'(
                        eval_to_string(Key, Scope),
                        fun(Key_str) ->
                            Path = parse_attr_path(Key_str),
                            {ok, traverse_value(Record, Path)}
                        end
                    )
                end
            );

        _ ->
            {error, {unsupported, <<"getAttr expects 2 arguments"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 663).
-spec bi_string_equals(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_string_equals(Args, Scope) ->
    case Args of
        [A, B] ->
            gleam@result:'try'(
                eval_to_string(A, Scope),
                fun(Sa) ->
                    gleam@result:'try'(
                        eval_to_string(B, Scope),
                        fun(Sb) -> {ok, {bool_val, Sa =:= Sb}} end
                    )
                end
            );

        _ ->
            {error, {unsupported, <<"stringEquals expects 2 arguments"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 644).
-spec bi_boolean_equals(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_boolean_equals(Args, Scope) ->
    case Args of
        [A, B] ->
            gleam@result:'try'(
                eval(A, Scope),
                fun(Va) ->
                    gleam@result:'try'(
                        eval(B, Scope),
                        fun(Vb) -> {ok, {bool_val, case {Va, Vb} of
                                        {{bool_val, X}, {bool_val, Y}} ->
                                            X =:= Y;

                                        {_, _} ->
                                            false
                                    end}} end
                    )
                end
            );

        _ ->
            {error, {unsupported, <<"booleanEquals expects 2 arguments"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 634).
-spec bi_not(list(expr()), gleam@dict:dict(binary(), value())) -> {ok, value()} |
    {error, resolve_error()}.
bi_not(Args, Scope) ->
    case Args of
        [Arg] ->
            gleam@result:'try'(
                eval(Arg, Scope),
                fun(Value) -> {ok, {bool_val, not is_truthy(Value)}} end
            );

        _ ->
            {error, {unsupported, <<"not expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 619).
-spec bi_is_set(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
bi_is_set(Args, Scope) ->
    case Args of
        [Arg] ->
            gleam@result:'try'(
                eval(Arg, Scope),
                fun(Value) -> {ok, {bool_val, case Value of
                                empty_val ->
                                    false;

                                _ ->
                                    true
                            end}} end
            );

        _ ->
            {error, {unsupported, <<"isSet expects 1 argument"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 593).
-spec eval_builtin(binary(), list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        value()} |
    {error, resolve_error()}.
eval_builtin(Name, Args, Scope) ->
    case Name of
        <<"isSet"/utf8>> ->
            bi_is_set(Args, Scope);

        <<"not"/utf8>> ->
            bi_not(Args, Scope);

        <<"booleanEquals"/utf8>> ->
            bi_boolean_equals(Args, Scope);

        <<"stringEquals"/utf8>> ->
            bi_string_equals(Args, Scope);

        <<"getAttr"/utf8>> ->
            bi_get_attr(Args, Scope);

        <<"substring"/utf8>> ->
            bi_substring(Args, Scope);

        <<"uriEncode"/utf8>> ->
            bi_uri_encode(Args, Scope);

        <<"parseURL"/utf8>> ->
            bi_parse_url(Args, Scope);

        <<"isValidHostLabel"/utf8>> ->
            bi_is_valid_host_label(Args, Scope);

        <<"aws.partition"/utf8>> ->
            bi_aws_partition(Args, Scope);

        <<"aws.parseArn"/utf8>> ->
            bi_aws_parse_arn(Args, Scope);

        <<"aws.isVirtualHostableS3Bucket"/utf8>> ->
            bi_is_virtual_hostable_s3_bucket(Args, Scope);

        _ ->
            {error,
                {unsupported,
                    <<<<"endpoint rule builtin '"/utf8, Name/binary>>/binary,
                        "' not implemented"/utf8>>}}
    end.

-file("src/aws/endpoints.gleam", 499).
-spec eval(expr(), gleam@dict:dict(binary(), value())) -> {ok, value()} |
    {error, resolve_error()}.
eval(Expr, Scope) ->
    case Expr of
        {ref, Name} ->
            case gleam_stdlib:map_get(Scope, Name) of
                {ok, Value} ->
                    {ok, Value};

                {error, _} ->
                    {ok, empty_val}
            end;

        {bool_lit, Value@1} ->
            {ok, {bool_val, Value@1}};

        {int_lit, N} ->
            {ok, {string_val, erlang:integer_to_binary(N)}};

        {template_expr, Parts} ->
            _pipe = eval_template(Parts, Scope),
            gleam@result:map(_pipe, fun(Field@0) -> {string_val, Field@0} end);

        {fn_call, Name@1, Args} ->
            eval_builtin(Name@1, Args, Scope)
    end.

-file("src/aws/endpoints.gleam", 435).
-spec do_check(list(condition()), gleam@dict:dict(binary(), value())) -> {ok,
        gleam@option:option(gleam@dict:dict(binary(), value()))} |
    {error, resolve_error()}.
do_check(Conditions, Scope) ->
    case Conditions of
        [] ->
            {ok, {some, Scope}};

        [{condition, Expr, Assign} | Rest] ->
            gleam@result:'try'(
                eval(Expr, Scope),
                fun(Value) -> case is_truthy(Value) of
                        false ->
                            {ok, none};

                        true ->
                            Scope@1 = case Assign of
                                {some, Name} ->
                                    gleam@dict:insert(Scope, Name, Value);

                                none ->
                                    Scope
                            end,
                            do_check(Rest, Scope@1)
                    end end
            )
    end.

-file("src/aws/endpoints.gleam", 428).
-spec check_conditions(list(condition()), gleam@dict:dict(binary(), value())) -> {ok,
        gleam@option:option(gleam@dict:dict(binary(), value()))} |
    {error, resolve_error()}.
check_conditions(Conditions, Scope) ->
    do_check(Conditions, Scope).

-file("src/aws/endpoints.gleam", 485).
-spec eval_string_list(list(expr()), gleam@dict:dict(binary(), value())) -> {ok,
        list(binary())} |
    {error, resolve_error()}.
eval_string_list(Exprs, Scope) ->
    case Exprs of
        [] ->
            {ok, []};

        [Head | Tail] ->
            gleam@result:'try'(
                eval_to_string(Head, Scope),
                fun(H) ->
                    gleam@result:'try'(
                        eval_string_list(Tail, Scope),
                        fun(T) -> {ok, [H | T]} end
                    )
                end
            )
    end.

-file("src/aws/endpoints.gleam", 474).
-spec eval_headers(
    gleam@dict:dict(binary(), list(expr())),
    gleam@dict:dict(binary(), value())
) -> {ok, gleam@dict:dict(binary(), list(binary()))} | {error, resolve_error()}.
eval_headers(Headers, Scope) ->
    gleam@dict:fold(
        Headers,
        {ok, maps:new()},
        fun(Acc, Key, Exprs) ->
            gleam@result:'try'(
                Acc,
                fun(Current) ->
                    gleam@result:'try'(
                        eval_string_list(Exprs, Scope),
                        fun(Values) ->
                            {ok, gleam@dict:insert(Current, Key, Values)}
                        end
                    )
                end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 388).
?DOC(
    " `Ok(Some(_))`  — matched + produced an endpoint\n"
    " `Ok(None)`     — conditions didn't match; try the next rule\n"
    " `Error(_)`     — an error rule fired, or evaluation hit an error\n"
).
-spec try_rule(rule(), gleam@dict:dict(binary(), value())) -> {ok,
        gleam@option:option(endpoint())} |
    {error, resolve_error()}.
try_rule(Rule, Scope) ->
    case Rule of
        {endpoint_rule, Conditions, Endpoint} ->
            case check_conditions(Conditions, Scope) of
                {ok, none} ->
                    {ok, none};

                {ok, {some, New_scope}} ->
                    gleam@result:'try'(
                        eval_to_string(erlang:element(2, Endpoint), New_scope),
                        fun(Url) ->
                            gleam@result:'try'(
                                eval_headers(
                                    erlang:element(3, Endpoint),
                                    New_scope
                                ),
                                fun(Headers) ->
                                    {ok, {some, {endpoint, Url, Headers}}}
                                end
                            )
                        end
                    );

                {error, Reason} ->
                    {error, Reason}
            end;

        {error_rule, Conditions@1, Message} ->
            case check_conditions(Conditions@1, Scope) of
                {ok, none} ->
                    {ok, none};

                {ok, {some, New_scope@1}} ->
                    gleam@result:'try'(
                        eval_to_string(Message, New_scope@1),
                        fun(Text) -> {error, {rule_error, Text}} end
                    );

                {error, Reason@1} ->
                    {error, Reason@1}
            end;

        {tree_rule, Conditions@2, Nested} ->
            case check_conditions(Conditions@2, Scope) of
                {ok, none} ->
                    {ok, none};

                {ok, {some, New_scope@2}} ->
                    case evaluate_rules(Nested, New_scope@2) of
                        {ok, Endpoint@1} ->
                            {ok, {some, Endpoint@1}};

                        {error, no_match} ->
                            {ok, none};

                        {error, Reason@2} ->
                            {error, Reason@2}
                    end;

                {error, Reason@3} ->
                    {error, Reason@3}
            end
    end.

-file("src/aws/endpoints.gleam", 370).
-spec evaluate_rules(list(rule()), gleam@dict:dict(binary(), value())) -> {ok,
        endpoint()} |
    {error, resolve_error()}.
evaluate_rules(Rules, Scope) ->
    case Rules of
        [] ->
            {error, no_match};

        [Rule | Rest] ->
            case try_rule(Rule, Scope) of
                {ok, {some, Endpoint}} ->
                    {ok, Endpoint};

                {ok, none} ->
                    evaluate_rules(Rest, Scope);

                {error, Reason} ->
                    {error, Reason}
            end
    end.

-file("src/aws/endpoints.gleam", 349).
-spec apply_defaults_and_check_required(
    rule_set(),
    gleam@dict:dict(binary(), value())
) -> {ok, gleam@dict:dict(binary(), value())} | {error, resolve_error()}.
apply_defaults_and_check_required(Rule_set, Params) ->
    gleam@dict:fold(
        erlang:element(2, Rule_set),
        {ok, Params},
        fun(Acc, Name, Parameter) ->
            gleam@result:'try'(
                Acc,
                fun(Current) -> case gleam_stdlib:map_get(Current, Name) of
                        {ok, _} ->
                            {ok, Current};

                        {error, _} ->
                            case erlang:element(4, Parameter) of
                                {some, Value} ->
                                    {ok,
                                        gleam@dict:insert(Current, Name, Value)};

                                none ->
                                    case erlang:element(3, Parameter) of
                                        true ->
                                            {error,
                                                {required_parameter_missing,
                                                    Name}};

                                        false ->
                                            {ok, Current}
                                    end
                            end
                    end end
            )
        end
    ).

-file("src/aws/endpoints.gleam", 102).
?DOC(
    " Walk the rule set with the given parameters. Returns the first matching\n"
    " endpoint, the first matching error rule (surfaced as `RuleError`), or\n"
    " `NoMatch` if every rule's conditions failed.\n"
).
-spec resolve(rule_set(), gleam@dict:dict(binary(), value())) -> {ok,
        endpoint()} |
    {error, resolve_error()}.
resolve(Rule_set, Params) ->
    gleam@result:'try'(
        apply_defaults_and_check_required(Rule_set, Params),
        fun(Prepared) ->
            evaluate_rules(erlang:element(3, Rule_set), Prepared)
        end
    ).