Skip to main content

src/aws@internal@codec@xml.erl

-module(aws@internal@codec@xml).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/codec/xml.gleam").
-export([element/2, escape_text/1, escape_attr/1, element_with_attrs/3, empty_element/1, text/1, bool_text/1, int_text/1, float_text/1, blob_text/1, list_element/3, flat_list/2, map_entries/3, map_element/4, flat_map/4, list_element_ns/5, flat_list_ns/3, map_entries_ns/5, map_element_ns/7, flat_map_ns/7]).

-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(
    " XML encoder helpers for restXml / awsQuery / ec2Query bodies.\n"
    " Minimum viable: element-per-member, text-content for primitives,\n"
    " proper escaping of `<>&\"'`. Honours `@xmlName` overrides at the\n"
    " call site (the emitter passes the resolved element name).\n"
    "\n"
    " The XML response decoder lives in\n"
    " `src/aws/internal/codec/xml_decode.gleam` — kept separate because\n"
    " it pulls in xmerl via FFI, which is heavier than the pure-string\n"
    " encoder side.\n"
).

-file("src/aws/internal/codec/xml.gleam", 17).
?DOC(" `<Name>...</Name>` wrapper around inner content.\n").
-spec element(binary(), binary()) -> binary().
element(Name, Inner) ->
    <<<<<<<<<<<<"<"/utf8, Name/binary>>/binary, ">"/utf8>>/binary,
                    Inner/binary>>/binary,
                "</"/utf8>>/binary,
            Name/binary>>/binary,
        ">"/utf8>>.

-file("src/aws/internal/codec/xml.gleam", 50).
?DOC(" Escape `<`, `>`, `&` in element text content.\n").
-spec escape_text(binary()) -> binary().
escape_text(S) ->
    _pipe = S,
    _pipe@1 = gleam@string:replace(_pipe, <<"&"/utf8>>, <<"&amp;"/utf8>>),
    _pipe@2 = gleam@string:replace(_pipe@1, <<"<"/utf8>>, <<"&lt;"/utf8>>),
    gleam@string:replace(_pipe@2, <<">"/utf8>>, <<"&gt;"/utf8>>).

-file("src/aws/internal/codec/xml.gleam", 61).
?DOC(
    " Escape `&`, `<`, `>`, `\"`, and `'` in attribute values. AWS\n"
    " service XML is double-quoted, so `'` doesn't strictly need\n"
    " escaping, but the entity is harmless and keeps the encoder\n"
    " quote-style agnostic.\n"
).
-spec escape_attr(binary()) -> binary().
escape_attr(S) ->
    _pipe = S,
    _pipe@1 = escape_text(_pipe),
    _pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"&quot;"/utf8>>),
    gleam@string:replace(_pipe@2, <<"'"/utf8>>, <<"&apos;"/utf8>>).

-file("src/aws/internal/codec/xml.gleam", 24).
?DOC(
    " `<Name attr1=\"v1\" attr2=\"v2\">inner</Name>`. Attribute values are\n"
    " escaped the same way as text content; attribute names are passed\n"
    " verbatim and assumed to be safe (Smithy member names).\n"
).
-spec element_with_attrs(binary(), list({binary(), binary()}), binary()) -> binary().
element_with_attrs(Name, Attrs, Inner) ->
    Attr_str = case Attrs of
        [] ->
            <<""/utf8>>;

        _ ->
            gleam@list:fold(
                Attrs,
                <<""/utf8>>,
                fun(Acc, P) ->
                    {K, V} = P,
                    <<<<<<<<<<Acc/binary, " "/utf8>>/binary, K/binary>>/binary,
                                "=\""/utf8>>/binary,
                            (escape_attr(V))/binary>>/binary,
                        "\""/utf8>>
                end
            )
    end,
    <<<<<<<<<<<<<<"<"/utf8, Name/binary>>/binary, Attr_str/binary>>/binary,
                        ">"/utf8>>/binary,
                    Inner/binary>>/binary,
                "</"/utf8>>/binary,
            Name/binary>>/binary,
        ">"/utf8>>.

-file("src/aws/internal/codec/xml.gleam", 41).
?DOC(" `<Name/>` — short form for empty elements.\n").
-spec empty_element(binary()) -> binary().
empty_element(Name) ->
    <<<<"<"/utf8, Name/binary>>/binary, "/>"/utf8>>.

-file("src/aws/internal/codec/xml.gleam", 45).
-spec text(binary()) -> binary().
text(Value) ->
    escape_text(Value).

-file("src/aws/internal/codec/xml.gleam", 69).
?DOC(" Format helpers for primitive members.\n").
-spec bool_text(boolean()) -> binary().
bool_text(B) ->
    case B of
        true ->
            <<"true"/utf8>>;

        false ->
            <<"false"/utf8>>
    end.

-file("src/aws/internal/codec/xml.gleam", 77).
-spec int_text(integer()) -> binary().
int_text(N) ->
    erlang:integer_to_binary(N).

-file("src/aws/internal/codec/xml.gleam", 80).
-spec float_text(float()) -> binary().
float_text(F) ->
    aws_ffi:float_short(F).

-file("src/aws/internal/codec/xml.gleam", 84).
?DOC(
    " Base64-encode a blob value for XML body inclusion. S3 uses base64\n"
    " for binary fields inside XML bodies (Content-MD5, ChecksumSHA256).\n"
).
-spec blob_text(bitstring()) -> binary().
blob_text(B) ->
    gleam_stdlib:base64_encode(B, true).

-file("src/aws/internal/codec/xml.gleam", 92).
?DOC(
    " Build a list element with each entry as `<member_name>value</member_name>`\n"
    " children. The wrapper element comes from the list shape's name; the\n"
    " per-entry element name comes from the list member's `@xmlName` (the\n"
    " emitter passes both).\n"
).
-spec list_element(binary(), binary(), list(binary())) -> binary().
list_element(Wrapper, Member_name, Entries) ->
    Inner = gleam@list:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, Entry_inner) ->
            <<Acc/binary, (element(Member_name, Entry_inner))/binary>>
        end
    ),
    element(Wrapper, Inner).

-file("src/aws/internal/codec/xml.gleam", 106).
?DOC(
    " Same as `list_element` but for `@xmlFlattened` lists — no\n"
    " wrapper element; entries become direct siblings.\n"
).
-spec flat_list(binary(), list(binary())) -> binary().
flat_list(Member_name, Entries) ->
    gleam@list:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, Entry_inner) ->
            <<Acc/binary, (element(Member_name, Entry_inner))/binary>>
        end
    ).

-file("src/aws/internal/codec/xml.gleam", 127).
?DOC(
    " Just the `<entry>...</entry>` siblings of a map, no wrapper.\n"
    " Used for nested maps — the outer map's `<value>` element wraps\n"
    " the inner map's entries directly.\n"
).
-spec map_entries(binary(), binary(), gleam@dict:dict(binary(), binary())) -> binary().
map_entries(Key_name, Value_name, Entries) ->
    gleam@dict:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, K, V) ->
            <<Acc/binary,
                (element(
                    <<"entry"/utf8>>,
                    <<(element(Key_name, escape_text(K)))/binary,
                        (element(Value_name, V))/binary>>
                ))/binary>>
        end
    ).

-file("src/aws/internal/codec/xml.gleam", 115).
?DOC(
    " Build a map element: each entry becomes `<entry><key>K</key><value>V</value></entry>`\n"
    " inside a wrapper. Smithy default uses `entry` / `key` / `value` —\n"
    " `@xmlName` overrides land in the caller.\n"
).
-spec map_element(
    binary(),
    binary(),
    binary(),
    gleam@dict:dict(binary(), binary())
) -> binary().
map_element(Wrapper, Key_name, Value_name, Entries) ->
    element(Wrapper, map_entries(Key_name, Value_name, Entries)).

-file("src/aws/internal/codec/xml.gleam", 147).
?DOC(
    " `@xmlFlattened` map: each entry becomes a top-level\n"
    " `<member_name><key>K</key><value>V</value></member_name>` block\n"
    " — no outer wrapper, no `<entry>` tag. The caller's containing\n"
    " element provides the sequencing. `value_name` is the key / value\n"
    " inner-element name (`\"key\"` / `\"value\"` by default; `@xmlName`\n"
    " overrides land in the caller).\n"
).
-spec flat_map(
    binary(),
    binary(),
    binary(),
    gleam@dict:dict(binary(), binary())
) -> binary().
flat_map(Member_name, Key_name, Value_name, Entries) ->
    gleam@dict:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, K, V) ->
            <<Acc/binary,
                (element(
                    Member_name,
                    <<(element(Key_name, escape_text(K)))/binary,
                        (element(Value_name, V))/binary>>
                ))/binary>>
        end
    ).

-file("src/aws/internal/codec/xml.gleam", 167).
?DOC(
    " `list_element` variant that carries member-level XML namespace\n"
    " attributes. `wrapper_attrs` go on the outer wrapping element;\n"
    " `member_attrs` go on every per-entry wrapping. Either list may\n"
    " be empty, in which case the corresponding wrapper renders\n"
    " without attributes — equivalent to `list_element` in that case.\n"
).
-spec list_element_ns(
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    list(binary())
) -> binary().
list_element_ns(Wrapper, Wrapper_attrs, Member_name, Member_attrs, Entries) ->
    Inner = gleam@list:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, Entry_inner) ->
            <<Acc/binary,
                (element_with_attrs(Member_name, Member_attrs, Entry_inner))/binary>>
        end
    ),
    element_with_attrs(Wrapper, Wrapper_attrs, Inner).

-file("src/aws/internal/codec/xml.gleam", 183).
?DOC(
    " `flat_list` variant with namespace attributes on every emitted\n"
    " member element. Empty `member_attrs` collapses to a bare element.\n"
).
-spec flat_list_ns(binary(), list({binary(), binary()}), list(binary())) -> binary().
flat_list_ns(Member_name, Member_attrs, Entries) ->
    gleam@list:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, Entry_inner) ->
            <<Acc/binary,
                (element_with_attrs(Member_name, Member_attrs, Entry_inner))/binary>>
        end
    ).

-file("src/aws/internal/codec/xml.gleam", 216).
?DOC(
    " `map_entries` variant with namespace attributes on the key /\n"
    " value wrappers. Used for nested maps and as the body of\n"
    " `map_element_ns`.\n"
).
-spec map_entries_ns(
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    gleam@dict:dict(binary(), binary())
) -> binary().
map_entries_ns(Key_name, Key_attrs, Value_name, Value_attrs, Entries) ->
    gleam@dict:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, K, V) ->
            <<Acc/binary,
                (element(
                    <<"entry"/utf8>>,
                    <<(element_with_attrs(Key_name, Key_attrs, escape_text(K)))/binary,
                        (element_with_attrs(Value_name, Value_attrs, V))/binary>>
                ))/binary>>
        end
    ).

-file("src/aws/internal/codec/xml.gleam", 197).
?DOC(
    " `map_element` variant with namespace attributes on the outer\n"
    " wrapper plus the key / value wrappers. The intermediate\n"
    " `<entry>` wrapper is unattributed (Smithy doesn't expose a\n"
    " trait for it).\n"
).
-spec map_element_ns(
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    gleam@dict:dict(binary(), binary())
) -> binary().
map_element_ns(
    Wrapper,
    Wrapper_attrs,
    Key_name,
    Key_attrs,
    Value_name,
    Value_attrs,
    Entries
) ->
    element_with_attrs(
        Wrapper,
        Wrapper_attrs,
        map_entries_ns(Key_name, Key_attrs, Value_name, Value_attrs, Entries)
    ).

-file("src/aws/internal/codec/xml.gleam", 235).
?DOC(
    " `flat_map` variant with namespace attributes on the repeated\n"
    " member wrappers and on the key / value wrappers inside.\n"
).
-spec flat_map_ns(
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    binary(),
    list({binary(), binary()}),
    gleam@dict:dict(binary(), binary())
) -> binary().
flat_map_ns(
    Member_name,
    Member_attrs,
    Key_name,
    Key_attrs,
    Value_name,
    Value_attrs,
    Entries
) ->
    gleam@dict:fold(
        Entries,
        <<""/utf8>>,
        fun(Acc, K, V) ->
            <<Acc/binary,
                (element_with_attrs(
                    Member_name,
                    Member_attrs,
                    <<(element_with_attrs(Key_name, Key_attrs, escape_text(K)))/binary,
                        (element_with_attrs(Value_name, Value_attrs, V))/binary>>
                ))/binary>>
        end
    ).