Skip to main content

src/aws@internal@codec@json_float.erl

-module(aws@internal@codec@json_float).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/codec/json_float.gleam").
-export([encode/1, decoder/0]).
-export_type([smithy_float/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(
    " awsJson special-float helpers.\n"
    "\n"
    " The AWS JSON protocols (`awsJson1_0`, `awsJson1_1`, plus other\n"
    " JSON-family wire formats) encode IEEE 754 specials as JSON strings:\n"
    " `\"NaN\"`, `\"Infinity\"`, `\"-Infinity\"`. Plain JSON numbers cover\n"
    " every finite float.\n"
    "\n"
    " **Why a wrapper type instead of raw `Float`:** Erlang's `float()`\n"
    " type cannot hold NaN or Infinity. `0.0/0.0` raises `badarith`;\n"
    " `binary_to_term` rejects the IEEE 754 NaN bit pattern with\n"
    " `badarg`; `<<F/float>>` bit-syntax matching refuses to bind such\n"
    " values. So a faithful representation of an awsJson Float field on\n"
    " the BEAM target requires an explicit sum type — there's no way to\n"
    " pack `NaN` into an Erlang float. Rust's `f64` happens to support\n"
    " these natively, which is why aws-sdk-rust gets away with `Option<f64>`.\n"
    "\n"
    " Matches `aws-sdk-rust`'s `aws-smithy-types::Number::SpecialFloat`\n"
    " — same three string spellings, same case-sensitivity.\n"
).

-type smithy_float() :: {float_value, float()} |
    na_n |
    pos_infinity |
    neg_infinity.

-file("src/aws/internal/codec/json_float.gleam", 43).
?DOC(
    " Encode a `SmithyFloat` as a JSON value. Finite values become JSON\n"
    " numbers; the three IEEE 754 specials become JSON strings, matching\n"
    " the awsJson wire spec.\n"
).
-spec encode(smithy_float()) -> gleam@json:json().
encode(V) ->
    case V of
        {float_value, F} ->
            gleam@json:float(F);

        na_n ->
            gleam@json:string(<<"NaN"/utf8>>);

        pos_infinity ->
            gleam@json:string(<<"Infinity"/utf8>>);

        neg_infinity ->
            gleam@json:string(<<"-Infinity"/utf8>>)
    end.

-file("src/aws/internal/codec/json_float.gleam", 58).
-spec from_string() -> gleam@dynamic@decode:decoder(smithy_float()).
from_string() ->
    gleam@dynamic@decode:then(
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(S) -> case S of
                <<"NaN"/utf8>> ->
                    gleam@dynamic@decode:success(na_n);

                <<"Infinity"/utf8>> ->
                    gleam@dynamic@decode:success(pos_infinity);

                <<"-Infinity"/utf8>> ->
                    gleam@dynamic@decode:success(neg_infinity);

                _ ->
                    gleam@dynamic@decode:failure(
                        na_n,
                        <<"expected NaN / Infinity / -Infinity"/utf8>>
                    )
            end end
    ).

-file("src/aws/internal/codec/json_float.gleam", 54).
?DOC(
    " Decoder for a `SmithyFloat`. Accepts plain JSON numbers and the\n"
    " three special-float strings.\n"
).
-spec decoder() -> gleam@dynamic@decode:decoder(smithy_float()).
decoder() ->
    gleam@dynamic@decode:one_of(
        gleam@dynamic@decode:map(
            {decoder, fun gleam@dynamic@decode:decode_float/1},
            fun(Field@0) -> {float_value, Field@0} end
        ),
        [from_string()]
    ).