Skip to main content

src/internal@encoder@bit.erl

-module(internal@encoder@bit).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/internal/encoder/bit.gleam").
-export([estimate_encoded_size/4, encode_string/4]).

-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(
    " Encode bit strings according to RFC 2045.\n"
    "\n"
    " See the following link for reference:\n"
    " - <https://tools.ietf.org/html/rfc2045#section-2.7>\n"
    " - <https://tools.ietf.org/html/rfc2045#section-2.8>\n"
).

-file("src/internal/encoder/bit.gleam", 113).
-spec is_valid_character(binary(), gleam@option:option(binary())) -> boolean().
is_valid_character(Character, Max_ascii) ->
    (((gleam@string:compare(Character, <<"\r"/utf8>>) /= eq) andalso (gleam@string:compare(
        Character,
        <<"\n"/utf8>>
    )
    /= eq))
    andalso (gleam@string:compare(Character, <<"\x{0}"/utf8>>) =:= gt))
    andalso begin
        _pipe = Max_ascii,
        _pipe@1 = gleam@option:map(
            _pipe,
            fun(Ascii) -> gleam@string:compare(Character, Ascii) =:= lt end
        ),
        gleam@option:unwrap(_pipe@1, true)
    end.

-file("src/internal/encoder/bit.gleam", 91).
-spec validate_line(binary(), gleam@option:option(binary()), integer()) -> {ok,
        integer()} |
    {error, internal@encoder@encoding:encoder_error()}.
validate_line(Line, Max_ascii, Maximum_size) ->
    Line_size = erlang:byte_size(Line),
    gleam@bool:guard(
        Line_size > Maximum_size,
        {error, {maximum_size_exceeded, Maximum_size}},
        fun() ->
            Validation = begin
                _pipe = Line,
                _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
                gleam@list:find(
                    _pipe@1,
                    fun(Character) ->
                        not is_valid_character(Character, Max_ascii)
                    end
                )
            end,
            case Validation of
                {ok, Character@1} ->
                    {error, {invalid_character, Character@1}};

                {error, nil} ->
                    {ok, Line_size}
            end
        end
    ).

-file("src/internal/encoder/bit.gleam", 64).
-spec max_ascii_from_bits(integer(), internal@encoder@encoding:encoding_mode()) -> gleam@option:option(binary()).
max_ascii_from_bits(Bits, Mode) ->
    case {Bits, Mode} of
        {8, utf8} ->
            none;

        {_, ascii} ->
            _pipe = gleam@int:power(2, erlang:float(Bits)),
            _pipe@1 = gleam@result:map(
                _pipe,
                fun(Ascii_code) ->
                    gleam@int:min(erlang:trunc(Ascii_code) - 1, 127)
                end
            ),
            _pipe@2 = gleam@result:'try'(
                _pipe@1,
                fun gleam@string:utf_codepoint/1
            ),
            _pipe@3 = gleam@result:map(
                _pipe@2,
                fun(Codepoint) ->
                    gleam_stdlib:utf_codepoint_list_to_string([Codepoint])
                end
            ),
            _pipe@4 = gleam@result:unwrap(_pipe@3, <<""/utf8>>),
            {some, _pipe@4};

        {_, utf8} ->
            _pipe@5 = gleam@int:power(2, erlang:float(Bits)),
            _pipe@6 = gleam@result:map(
                _pipe@5,
                fun(Ascii_code@1) -> erlang:trunc(Ascii_code@1) - 1 end
            ),
            _pipe@7 = gleam@result:'try'(
                _pipe@6,
                fun gleam@string:utf_codepoint/1
            ),
            _pipe@8 = gleam@result:map(
                _pipe@7,
                fun(Codepoint@1) ->
                    gleam_stdlib:utf_codepoint_list_to_string([Codepoint@1])
                end
            ),
            _pipe@9 = gleam@result:unwrap(_pipe@8, <<""/utf8>>),
            {some, _pipe@9}
    end.

-file("src/internal/encoder/bit.gleam", 20).
?DOC(
    " Estimates the `bits' bit encoded size of a string in bytes, taking\n"
    " maximum size of a line into account.\n"
).
-spec estimate_encoded_size(
    binary(),
    integer(),
    internal@encoder@encoding:encoding_mode(),
    integer()
) -> {ok, integer()} | {error, internal@encoder@encoding:encoder_error()}.
estimate_encoded_size(String, Bits, Mode, Maximum_size) ->
    Max_ascii = max_ascii_from_bits(Bits, Mode),
    _pipe = String,
    _pipe@1 = gleam@string:split(_pipe, <<"\r\n"/utf8>>),
    gleam@list:try_fold(
        _pipe@1,
        0,
        fun(Size, Line) -> case validate_line(Line, Max_ascii, Maximum_size) of
                {ok, Line_size} ->
                    {ok, Size + Line_size};

                {error, _} = Error ->
                    Error
            end end
    ).

-file("src/internal/encoder/bit.gleam", 41).
?DOC(
    " Bit encode a string with the required number of bits, using the\n"
    " specified encoding mode and taking into account the maximum allowed\n"
    " line size.\n"
).
-spec encode_string(
    binary(),
    integer(),
    internal@encoder@encoding:encoding_mode(),
    integer()
) -> {ok, list(binary())} | {error, internal@encoder@encoding:encoder_error()}.
encode_string(String, Bits, Mode, Maximum_size) ->
    case String of
        <<""/utf8>> ->
            {ok, []};

        _ ->
            Max_ascii = max_ascii_from_bits(Bits, Mode),
            _pipe = String,
            _pipe@1 = gleam@string:split(_pipe, <<"\r\n"/utf8>>),
            gleam@list:try_map(
                _pipe@1,
                fun(Line) ->
                    case validate_line(Line, Max_ascii, Maximum_size) of
                        {ok, _} ->
                            {ok, Line};

                        {error, Error} ->
                            {error, Error}
                    end
                end
            )
    end.