-module(aws@internal@codec@rest).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/codec/rest.gleam").
-export([substitute_label/4, add_query/3, bool_to_query/1, int_to_query/1, float_to_query/1, maybe_set_list_header/3, append_content_encoding/2, idempotency_token/0, quote_list_string_entry/1, build_path/2, maybe_set_header/3, set_default_header/3, add_prefix_headers/3, add_query_params/2, add_query_params_list/2, timestamp_to_header/1, enum_wire_value/1, string_header/2, int_header/2, bool_header/2, float_header/2, enum_header/3, http_date_header/2, iso8601_header/2, epoch_seconds_header/2, with_content_md5_header/2, glacier_tree_hash/1, with_glacier_tree_hash_headers/2, checksum_header/2, with_checksum_header/3, checksum_algorithm_from_wire/1, with_checksum_header_for_wire/3]).
-export_type([checksum_algorithm/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(
" Shared runtime helpers for the rest-protocol emitters\n"
" (restJson1, restXml). Holds the URI / query / header glue that\n"
" generated `build_*_request` functions call into for each\n"
" `@httpLabel`, `@httpQuery`, `@httpHeader` member.\n"
).
-type checksum_algorithm() :: checksum_sha256 |
checksum_sha1 |
checksum_crc32 |
checksum_crc32_c.
-file("src/aws/internal/codec/rest.gleam", 41).
?DOC(" Encode each path segment but keep the `/` separators intact.\n").
-spec encode_path_preserve_slash(binary()) -> binary().
encode_path_preserve_slash(Path) ->
_pipe = gleam@string:split(Path, <<"/"/utf8>>),
_pipe@1 = gleam@list:map(_pipe, fun aws@internal@uri:encode_segment/1),
gleam@string:join(_pipe@1, <<"/"/utf8>>).
-file("src/aws/internal/codec/rest.gleam", 23).
?DOC(
" Substitute a single `@httpLabel` member into the URI template.\n"
" Templates use `{Name}` or `{Name+}` (the `+` marks a greedy label\n"
" that may contain `/`). Values are percent-encoded; greedy labels\n"
" preserve `/` in the value.\n"
).
-spec substitute_label(binary(), binary(), binary(), boolean()) -> binary().
substitute_label(Template, Name, Value, Greedy) ->
Placeholder = <<<<"{"/utf8, Name/binary>>/binary, "}"/utf8>>,
Greedy_placeholder = <<<<"{"/utf8, Name/binary>>/binary, "+}"/utf8>>,
Encoded = case Greedy of
true ->
encode_path_preserve_slash(Value);
false ->
aws@internal@uri:encode_segment(Value)
end,
_pipe = Template,
_pipe@1 = gleam@string:replace(_pipe, Greedy_placeholder, Encoded),
gleam@string:replace(_pipe@1, Placeholder, Encoded).
-file("src/aws/internal/codec/rest.gleam", 49).
?DOC(
" Append a query parameter pair. Returns the resulting query string\n"
" (without the leading `?`); call sites prepend it themselves.\n"
).
-spec add_query(binary(), binary(), binary()) -> binary().
add_query(Existing, Name, Value) ->
Pair = <<<<(aws@internal@uri:encode_component(Name))/binary, "="/utf8>>/binary,
(aws@internal@uri:encode_component(Value))/binary>>,
case Existing of
<<""/utf8>> ->
Pair;
_ ->
<<<<Existing/binary, "&"/utf8>>/binary, Pair/binary>>
end.
-file("src/aws/internal/codec/rest.gleam", 58).
?DOC(" Bool → query value: \"true\" / \"false\".\n").
-spec bool_to_query(boolean()) -> binary().
bool_to_query(B) ->
case B of
true ->
<<"true"/utf8>>;
false ->
<<"false"/utf8>>
end.
-file("src/aws/internal/codec/rest.gleam", 66).
?DOC(" Int → query / header value as decimal.\n").
-spec int_to_query(integer()) -> binary().
int_to_query(N) ->
erlang:integer_to_binary(N).
-file("src/aws/internal/codec/rest.gleam", 74).
?DOC(
" Float → query / header / URI-label value. Uses Erlang's `short`\n"
" formatter so `1.1` round-trips as the literal `\"1.1\"` — the AWS\n"
" SimpleScalarProperties protocol-test corpus rejects scientific\n"
" notation in these positions.\n"
).
-spec float_to_query(float()) -> binary().
float_to_query(F) ->
aws_ffi:float_short(F).
-file("src/aws/internal/codec/rest.gleam", 84).
?DOC(
" `@httpHeader` on a list member emits the values comma-joined, per\n"
" HTTP/1.1 header-folding rules. `Some([\"a\", \"b\"])` becomes\n"
" `Name: a, b`. Empty lists drop the header entirely.\n"
).
-spec maybe_set_list_header(
gleam@dict:dict(binary(), binary()),
binary(),
list(binary())
) -> gleam@dict:dict(binary(), binary()).
maybe_set_list_header(Headers, Name, Values) ->
gleam@dict:insert(Headers, Name, gleam@string:join(Values, <<", "/utf8>>)).
-file("src/aws/internal/codec/rest.gleam", 100).
?DOC(
" Append a `@requestCompression` encoding to the `Content-Encoding`\n"
" header. Existing encodings (e.g. caller-set `Content-Encoding:\n"
" custom`) are preserved with the new value appended after a comma:\n"
" `custom` + `gzip` ⇒ `custom, gzip`.\n"
).
-spec append_content_encoding(gleam@dict:dict(binary(), binary()), binary()) -> gleam@dict:dict(binary(), binary()).
append_content_encoding(Headers, Encoding) ->
case gleam_stdlib:map_get(Headers, <<"Content-Encoding"/utf8>>) of
{ok, Existing} ->
gleam@dict:insert(
Headers,
<<"Content-Encoding"/utf8>>,
<<<<Existing/binary, ", "/utf8>>/binary, Encoding/binary>>
);
{error, _} ->
gleam@dict:insert(Headers, <<"Content-Encoding"/utf8>>, Encoding)
end.
-file("src/aws/internal/codec/rest.gleam", 115).
?DOC(
" Generated `@idempotencyToken` value, used when a request member\n"
" with that trait is left as `Option.None`. Backed by the runtime\n"
" FFI so tests can pin a deterministic UUID via `application:set_env`.\n"
).
-spec idempotency_token() -> binary().
idempotency_token() ->
aws_ffi:idempotency_token().
-file("src/aws/internal/codec/rest.gleam", 120).
?DOC(
" RFC 7230 list-header quoting for string elements. Smithy applies\n"
" this only to string-typed entries — timestamp values use the raw\n"
" `Mon, 16 Dec ... GMT` form even though it contains a comma.\n"
).
-spec quote_list_string_entry(binary()) -> binary().
quote_list_string_entry(V) ->
case gleam_stdlib:contains_string(V, <<","/utf8>>) orelse gleam_stdlib:contains_string(
V,
<<"\""/utf8>>
) of
true ->
<<<<"\""/utf8,
(gleam@string:replace(V, <<"\""/utf8>>, <<"\\\""/utf8>>))/binary>>/binary,
"\""/utf8>>;
false ->
V
end.
-file("src/aws/internal/codec/rest.gleam", 134).
?DOC(
" Build the full path: substitute labels, then append query (with `?`)\n"
" if non-empty.\n"
" Merge a path that may already carry a static query string (from the\n"
" `@http` URI template, e.g. `/Foo?bar=baz`) with the dynamically\n"
" built query string from `@httpQuery` members. Either or both can be\n"
" empty. The static-query side wins on key order; dynamic params are\n"
" appended.\n"
).
-spec build_path(binary(), binary()) -> binary().
build_path(Uri_path, Query) ->
{Path_only, Static_query} = case gleam@string:split_once(
Uri_path,
<<"?"/utf8>>
) of
{ok, {P, Q}} ->
{P, Q};
{error, _} ->
{Uri_path, <<""/utf8>>}
end,
Combined = case {Static_query, Query} of
{<<""/utf8>>, <<""/utf8>>} ->
<<""/utf8>>;
{<<""/utf8>>, Q@1} ->
Q@1;
{Sq, <<""/utf8>>} ->
Sq;
{Sq@1, Q@2} ->
<<<<Sq@1/binary, "&"/utf8>>/binary, Q@2/binary>>
end,
case Combined of
<<""/utf8>> ->
Path_only;
_ ->
<<<<Path_only/binary, "?"/utf8>>/binary, Combined/binary>>
end.
-file("src/aws/internal/codec/rest.gleam", 153).
?DOC(
" Set a header on the headers dict if the value is non-empty (Smithy\n"
" `@httpHeader` typically omits the header when the value is None).\n"
).
-spec maybe_set_header(gleam@dict:dict(binary(), binary()), binary(), binary()) -> gleam@dict:dict(binary(), binary()).
maybe_set_header(Headers, Name, Value) ->
gleam@dict:insert(Headers, Name, Value).
-file("src/aws/internal/codec/rest.gleam", 169).
?DOC(
" Insert a service-level default header only when the caller hasn't\n"
" already provided one with the same name. Mirrors `set_default_header`\n"
" in the Rust SDK and the `contains_key` guard inside Glacier's\n"
" `add_checksum_treehash` so caller `@httpHeader`-bound values win on\n"
" collision.\n"
).
-spec set_default_header(
gleam@dict:dict(binary(), binary()),
binary(),
binary()
) -> gleam@dict:dict(binary(), binary()).
set_default_header(Headers, Name, Value) ->
case gleam@dict:has_key(Headers, Name) of
true ->
Headers;
false ->
gleam@dict:insert(Headers, Name, Value)
end.
-file("src/aws/internal/codec/rest.gleam", 182).
?DOC(
" Iterate `@httpPrefixHeaders` map members: for each entry,\n"
" emit a header `<prefix><key>: <value>`.\n"
).
-spec add_prefix_headers(
gleam@dict:dict(binary(), binary()),
binary(),
gleam@dict:dict(binary(), binary())
) -> gleam@dict:dict(binary(), binary()).
add_prefix_headers(Headers, Prefix, Entries) ->
gleam@dict:fold(
Entries,
Headers,
fun(Acc, K, V) ->
gleam@dict:insert(Acc, <<Prefix/binary, K/binary>>, V)
end
).
-file("src/aws/internal/codec/rest.gleam", 191).
?DOC(" Iterate `@httpQueryParams` map members (Map<String, String>).\n").
-spec add_query_params(binary(), gleam@dict:dict(binary(), binary())) -> binary().
add_query_params(Query, Entries) ->
gleam@dict:fold(Entries, Query, fun(Acc, K, V) -> add_query(Acc, K, V) end).
-file("src/aws/internal/codec/rest.gleam", 200).
?DOC(
" Iterate `@httpQueryParams` map members (Map<String, List<String>>).\n"
" Each list value emits one query param per element.\n"
).
-spec add_query_params_list(binary(), gleam@dict:dict(binary(), list(binary()))) -> binary().
add_query_params_list(Query, Entries) ->
gleam@dict:fold(
Entries,
Query,
fun(Acc, K, Vs) ->
gleam@list:fold(Vs, Acc, fun(Q, V) -> add_query(Q, K, V) end)
end
).
-file("src/aws/internal/codec/rest.gleam", 214).
?DOC(
" Format a timestamp for use in URI labels / query strings / headers.\n"
" Smithy's default `@timestampFormat` for `date-time` (the\n"
" restJson1 + restXml default) is RFC 3339, e.g. `2019-12-16T23:48:18Z`.\n"
" We always emit Z-suffixed UTC; the wire form remains stable even if\n"
" the input came from a system clock in a different zone.\n"
).
-spec timestamp_to_header(integer()) -> binary().
timestamp_to_header(Epoch_seconds) ->
aws_ffi:format_iso8601(Epoch_seconds).
-file("src/aws/internal/codec/rest.gleam", 225).
?DOC(
" Extract the raw wire string from a JSON-encoded enum value. The\n"
" generated `encode_<enum>_enum(v)` returns a `json.Json` like\n"
" `json.string(\"VALUE\")`; URI / query / header position wants just\n"
" `VALUE`. We render to JSON text and strip the surrounding quotes.\n"
).
-spec enum_wire_value(gleam@json:json()) -> binary().
enum_wire_value(J) ->
S = gleam@json:to_string(J),
Len = string:length(S),
case Len > 2 of
true ->
gleam@string:slice(S, 1, Len - 2);
false ->
S
end.
-file("src/aws/internal/codec/rest.gleam", 242).
-spec string_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(binary()).
string_header(Headers, Name) ->
_pipe = gleam_stdlib:map_get(Headers, string:lowercase(Name)),
gleam@option:from_result(_pipe).
-file("src/aws/internal/codec/rest.gleam", 250).
-spec int_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(integer()).
int_header(Headers, Name) ->
gleam@option:then(string_header(Headers, Name), fun(Raw) -> _pipe = Raw,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = gleam_stdlib:parse_int(_pipe@1),
gleam@option:from_result(_pipe@2) end).
-file("src/aws/internal/codec/rest.gleam", 258).
-spec bool_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(boolean()).
bool_header(Headers, Name) ->
gleam@option:then(
string_header(Headers, Name),
fun(Raw) -> case string:lowercase(gleam@string:trim(Raw)) of
<<"true"/utf8>> ->
{some, true};
<<"false"/utf8>> ->
{some, false};
_ ->
none
end end
).
-file("src/aws/internal/codec/rest.gleam", 547).
-spec parse_float(binary()) -> gleam@option:option(float()).
parse_float(S) ->
_pipe = gleam_stdlib:parse_float(S),
_pipe@2 = gleam@result:lazy_or(
_pipe,
fun() -> _pipe@1 = gleam_stdlib:parse_int(S),
gleam@result:map(_pipe@1, fun erlang:float/1) end
),
gleam@option:from_result(_pipe@2).
-file("src/aws/internal/codec/rest.gleam", 273).
?DOC(
" Float header — used for shapes that bind a `Float` member to a\n"
" response header. Falls through to `None` if the value can't be\n"
" parsed; same forgiving contract as `int_header` / `bool_header`.\n"
).
-spec float_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(float()).
float_header(Headers, Name) ->
gleam@option:then(
string_header(Headers, Name),
fun(Raw) -> parse_float(gleam@string:trim(Raw)) end
).
-file("src/aws/internal/codec/rest.gleam", 288).
?DOC(
" Enum header — looks up a string header, then runs the supplied\n"
" `<enum>_from_wire` decoder. Falls through to `None` if the\n"
" header is missing or the wire value doesn't match a known\n"
" variant (forgiving contract — unknown variants don't crash\n"
" the response parse). The codegen passes the generated\n"
" `<enum>_from_wire` function directly so this stays\n"
" enum-agnostic.\n"
).
-spec enum_header(
gleam@dict:dict(binary(), binary()),
binary(),
fun((binary()) -> {ok, MWX} | {error, binary()})
) -> gleam@option:option(MWX).
enum_header(Headers, Name, From_wire) ->
case string_header(Headers, Name) of
{some, S} ->
case From_wire(S) of
{ok, V} ->
{some, V};
{error, _} ->
none
end;
none ->
none
end.
-file("src/aws/internal/codec/rest.gleam", 308).
?DOC(
" HTTP-date header — RFC 7231 §7.1.1.1 form\n"
" (`Tue, 29 Apr 2014 18:30:38 GMT`). The default `@timestampFormat`\n"
" for header bindings per Smithy core — covers `Last-Modified`,\n"
" `Expires`, `Date`, etc. Forgiving contract: missing header or\n"
" unparseable string → `None`.\n"
).
-spec http_date_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(aws@internal@codec@json_timestamp:timestamp()).
http_date_header(Headers, Name) ->
case string_header(Headers, Name) of
{some, Raw} ->
case aws@internal@codec@json_timestamp:parse_http_date(
gleam@string:trim(Raw)
) of
{ok, T} ->
{some, T};
{error, _} ->
none
end;
none ->
none
end.
-file("src/aws/internal/codec/rest.gleam", 324).
?DOC(
" ISO 8601 timestamp header (`@timestampFormat(\"date-time\")`,\n"
" `2024-01-02T03:04:05Z`).\n"
).
-spec iso8601_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(aws@internal@codec@json_timestamp:timestamp()).
iso8601_header(Headers, Name) ->
case string_header(Headers, Name) of
{some, Raw} ->
case aws@internal@codec@json_timestamp:parse_iso8601(
gleam@string:trim(Raw)
) of
{ok, T} ->
{some, T};
{error, _} ->
none
end;
none ->
none
end.
-file("src/aws/internal/codec/rest.gleam", 341).
?DOC(
" Epoch-seconds timestamp header\n"
" (`@timestampFormat(\"epoch-seconds\")`, integer seconds since 1970\n"
" in the header value).\n"
).
-spec epoch_seconds_header(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(aws@internal@codec@json_timestamp:timestamp()).
epoch_seconds_header(Headers, Name) ->
case int_header(Headers, Name) of
{some, N} ->
{some, {timestamp, N, 0}};
none ->
none
end.
-file("src/aws/internal/codec/rest.gleam", 366).
?DOC(
" Set the `Content-MD5` header to `base64(md5(body))`. Used by the\n"
" Smithy `smithy.api#httpChecksumRequired` trait — the codegen\n"
" emits a call to this helper at the tail of `build_<op>_request`\n"
" for any operation that carries the trait.\n"
"\n"
" Always overwrites a previous `Content-MD5` entry: the SDK is\n"
" responsible for the canonical value, and a stale caller-supplied\n"
" one would surface as a 400 from the service. Other headers pass\n"
" through unchanged.\n"
"\n"
" MD5 is not a security primitive here. The wire contract requires\n"
" it (S3-control + restJson1 protocol tests fix the exact bytes);\n"
" SigV4 covers the actual auth on the request.\n"
).
-spec with_content_md5_header(gleam@dict:dict(binary(), binary()), bitstring()) -> gleam@dict:dict(binary(), binary()).
with_content_md5_header(Headers, Body) ->
Digest = gleam_stdlib:base64_encode(aws_ffi:md5(Body), true),
gleam@dict:insert(Headers, <<"Content-MD5"/utf8>>, Digest).
-file("src/aws/internal/codec/rest.gleam", 446).
-spec pair_hash(list(bitstring())) -> list(bitstring()).
pair_hash(Hashes) ->
case Hashes of
[] ->
[];
[Single] ->
[Single];
[Left, Right | Rest] ->
[aws_ffi:sha256(gleam@bit_array:append(Left, Right)) |
pair_hash(Rest)]
end.
-file("src/aws/internal/codec/rest.gleam", 439).
-spec combine_tree_hashes(list(bitstring())) -> bitstring().
combine_tree_hashes(Hashes) ->
case Hashes of
[Single] ->
Single;
_ ->
combine_tree_hashes(pair_hash(Hashes))
end.
-file("src/aws/internal/codec/rest.gleam", 416).
-spec chunk_hashes(bitstring(), integer(), list(bitstring())) -> list(bitstring()).
chunk_hashes(Body, Chunk_size, Acc) ->
case erlang:byte_size(Body) of
0 ->
lists:reverse(Acc);
Size ->
Take = case Size > Chunk_size of
true ->
Chunk_size;
false ->
Size
end,
Chunk@1 = case gleam_stdlib:bit_array_slice(Body, 0, Take) of
{ok, Chunk} -> Chunk;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aws/internal/codec/rest"/utf8>>,
function => <<"chunk_hashes"/utf8>>,
line => 431,
value => _assert_fail,
start => 14691,
'end' => 14744,
pattern_start => 14702,
pattern_end => 14711})
end,
Rest_size = Size - Take,
Rest@1 = case gleam_stdlib:bit_array_slice(Body, Take, Rest_size) of
{ok, Rest} -> Rest;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aws/internal/codec/rest"/utf8>>,
function => <<"chunk_hashes"/utf8>>,
line => 433,
value => _assert_fail@1,
start => 14785,
'end' => 14845,
pattern_start => 14796,
pattern_end => 14804})
end,
chunk_hashes(Rest@1, Chunk_size, [aws_ffi:sha256(Chunk@1) | Acc])
end.
-file("src/aws/internal/codec/rest.gleam", 406).
?DOC(
" Compute the Glacier tree hash of `body` as raw bytes (caller\n"
" hex-encodes for the wire). Empty body degenerates to\n"
" `SHA-256(\"\")` so the function is total. Matches\n"
" `glacier_interceptors::compute_hash_tree` in the Rust SDK.\n"
).
-spec glacier_tree_hash(bitstring()) -> bitstring().
glacier_tree_hash(Body) ->
case erlang:byte_size(Body) of
0 ->
aws_ffi:sha256(<<>>);
_ ->
Chunks = chunk_hashes(Body, 1048576, []),
combine_tree_hashes(Chunks)
end.
-file("src/aws/internal/codec/rest.gleam", 389).
?DOC(
" Glacier's tree-hash + content-sha256 headers. Both end up as the\n"
" Computed against the recursive 1 MiB chunk algorithm at\n"
" https://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-\n"
" calculations.html: split the body into 1 MiB chunks, SHA-256 each,\n"
" then pair-hash adjacent digests until one remains. Single-chunk\n"
" bodies degenerate to plain SHA-256 (tree-hash == content-sha256),\n"
" which is what every Glacier protocol-test fixture happens to use,\n"
" but the recursive form is required for the > 1 MiB upload archive\n"
" path used by real callers.\n"
"\n"
" `X-Amz-Sha256-Tree-Hash` carries the tree-hash digest;\n"
" `X-Amz-Content-Sha256` carries the full-body SHA-256. The Rust SDK\n"
" uses the same pair of headers in `glacier_interceptors::\n"
" add_checksum_treehash`. Both are skipped when already present so a\n"
" caller-supplied value wins.\n"
).
-spec with_glacier_tree_hash_headers(
gleam@dict:dict(binary(), binary()),
bitstring()
) -> gleam@dict:dict(binary(), binary()).
with_glacier_tree_hash_headers(Headers, Body) ->
Content_sha256 = aws_ffi:hex_encode(aws_ffi:sha256(Body)),
Tree_hash = aws_ffi:hex_encode(glacier_tree_hash(Body)),
_pipe = Headers,
_pipe@1 = set_default_header(
_pipe,
<<"X-Amz-Sha256-Tree-Hash"/utf8>>,
Tree_hash
),
set_default_header(_pipe@1, <<"X-Amz-Content-Sha256"/utf8>>, Content_sha256).
-file("src/aws/internal/codec/rest.gleam", 475).
?DOC(
" `(header_name, base64_digest)` pair for a body checksum. The\n"
" header name follows the AWS convention `x-amz-checksum-<algo>`\n"
" (lowercase). The digest is base64 of the raw bytes — same\n"
" padding rules as `Content-MD5`. Pure function; the\n"
" `aws.protocols#httpChecksum` middleware in the codegen layer\n"
" calls this and inserts the result into the request headers.\n"
).
-spec checksum_header(checksum_algorithm(), bitstring()) -> {binary(), binary()}.
checksum_header(Algorithm, Body) ->
case Algorithm of
checksum_sha256 ->
{<<"x-amz-checksum-sha256"/utf8>>,
gleam_stdlib:base64_encode(aws_ffi:sha256(Body), true)};
checksum_sha1 ->
{<<"x-amz-checksum-sha1"/utf8>>,
gleam_stdlib:base64_encode(aws_ffi:sha1(Body), true)};
checksum_crc32 ->
{<<"x-amz-checksum-crc32"/utf8>>,
gleam_stdlib:base64_encode(
aws@internal@crypto:crc32_be_bytes(erlang:crc32(Body)),
true
)};
checksum_crc32_c ->
{<<"x-amz-checksum-crc32c"/utf8>>,
gleam_stdlib:base64_encode(
aws@internal@crypto:crc32_be_bytes(aws_ffi:crc32c(Body)),
true
)}
end.
-file("src/aws/internal/codec/rest.gleam", 503).
?DOC(
" Add an `x-amz-checksum-<algo>` header to the request. Convenience\n"
" wrapper around `checksum_header` that lets call sites stay\n"
" pipeline-style with the existing `Dict(String, String)` header\n"
" shape — same ergonomics as `with_content_md5_header`.\n"
).
-spec with_checksum_header(
gleam@dict:dict(binary(), binary()),
checksum_algorithm(),
bitstring()
) -> gleam@dict:dict(binary(), binary()).
with_checksum_header(Headers, Algorithm, Body) ->
{Name, Value} = checksum_header(Algorithm, Body),
gleam@dict:insert(Headers, Name, Value).
-file("src/aws/internal/codec/rest.gleam", 519).
?DOC(
" Translate a Smithy `ChecksumAlgorithm` enum's wire value\n"
" (e.g. `\"SHA256\"`, `\"CRC32C\"`) to the runtime `ChecksumAlgorithm`\n"
" variant, falling back to `ChecksumSha256` when the wire value\n"
" doesn't match a supported algorithm. Used by the codegen's\n"
" algorithm-member dispatch for `aws.protocols#httpChecksum` so\n"
" generated request builders can read the caller's typed enum\n"
" choice without needing a per-service jump table.\n"
).
-spec checksum_algorithm_from_wire(binary()) -> checksum_algorithm().
checksum_algorithm_from_wire(Wire) ->
case Wire of
<<"SHA256"/utf8>> ->
checksum_sha256;
<<"SHA1"/utf8>> ->
checksum_sha1;
<<"CRC32"/utf8>> ->
checksum_crc32;
<<"CRC32C"/utf8>> ->
checksum_crc32_c;
_ ->
checksum_sha256
end.
-file("src/aws/internal/codec/rest.gleam", 539).
?DOC(
" Add the `x-amz-checksum-<algo>` header using a wire-form\n"
" algorithm name. Equivalent to\n"
" `with_checksum_header(headers, checksum_algorithm_from_wire(wire), body)`.\n"
" Exists so the codegen can emit a single call that takes the\n"
" generated enum's wire-encoder output directly, without\n"
" needing per-service algorithm-mapping helpers.\n"
).
-spec with_checksum_header_for_wire(
gleam@dict:dict(binary(), binary()),
binary(),
bitstring()
) -> gleam@dict:dict(binary(), binary()).
with_checksum_header_for_wire(Headers, Wire, Body) ->
with_checksum_header(Headers, checksum_algorithm_from_wire(Wire), Body).