Skip to main content

src/internal@encoder@text.erl

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

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

-file("src/internal/encoder/text.gleam", 195).
-spec should_escape_character(binary(), internal@encoder@encoding:field_type()) -> boolean().
should_escape_character(Character, Field_type) ->
    ((Field_type =:= structured) andalso (gleam@string:compare(
        Character,
        <<"\t"/utf8>>
    )
    /= eq))
    andalso (((gleam@string:compare(Character, <<"\x{20}"/utf8>>) =:= lt) orelse (gleam@string:compare(
        Character,
        <<"\\"/utf8>>
    )
    =:= eq))
    orelse (gleam@string:compare(Character, <<"\""/utf8>>) =:= eq)).

-file("src/internal/encoder/text.gleam", 208).
-spec is_valid_character(binary(), internal@encoder@encoding:encoding_mode()) -> boolean().
is_valid_character(Character, Mode) ->
    case Mode of
        ascii ->
            (gleam@string:compare(Character, <<"\x{1f}"/utf8>>) =:= gt) andalso (gleam@string:compare(
                Character,
                <<"\x{80}"/utf8>>
            )
            =:= lt);

        utf8 ->
            gleam@string:compare(Character, <<"\x{1f}"/utf8>>) =:= gt
    end.

-file("src/internal/encoder/text.gleam", 130).
-spec validate_chunk(
    binary(),
    internal@encoder@encoding:field_type(),
    internal@encoder@encoding:encoding_mode(),
    integer()
) -> {ok, integer()} | {error, internal@encoder@encoding:encoder_error()}.
validate_chunk(Chunk, Field_type, Mode, Maximum_size) ->
    Chunk_size = erlang:byte_size(Chunk),
    _pipe = Chunk,
    _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
    _pipe@2 = gleam@list:try_fold(
        _pipe@1,
        Chunk_size,
        fun(Accumulator, Character) ->
            gleam@bool:guard(
                not is_valid_character(Character, Mode),
                {error, {invalid_character, Character}},
                fun() ->
                    {ok,
                        Accumulator + gleam@bool:guard(
                            should_escape_character(Character, Field_type),
                            1,
                            fun() -> 0 end
                        )}
                end
            )
        end
    ),
    gleam@result:'try'(
        _pipe@2,
        fun(Chunk_size@1) -> case Chunk_size@1 =< Maximum_size of
                true ->
                    {ok, Chunk_size@1};

                false ->
                    {error, {maximum_size_exceeded, Maximum_size}}
            end end
    ).

-file("src/internal/encoder/text.gleam", 217).
-spec is_whitespace(binary()) -> boolean().
is_whitespace(Character) ->
    (gleam@string:compare(Character, <<"\t"/utf8>>) =:= eq) orelse (gleam@string:compare(
        Character,
        <<" "/utf8>>
    )
    =:= eq).

-file("src/internal/encoder/text.gleam", 107).
-spec chunk_on_whitespace(binary()) -> list(binary()).
chunk_on_whitespace(Value) ->
    case Value of
        <<""/utf8>> ->
            [];

        _ ->
            _pipe = Value,
            _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
            _pipe@2 = gleam@list:index_fold(
                _pipe@1,
                [0],
                fun(Accumulator, Character, Index) ->
                    case is_whitespace(Character) of
                        true ->
                            [Index | Accumulator];

                        false ->
                            Accumulator
                    end
                end
            ),
            _pipe@3 = gleam@list:prepend(_pipe@2, string:length(Value)),
            _pipe@4 = lists:reverse(_pipe@3),
            _pipe@5 = gleam@list:window_by_2(_pipe@4),
            gleam@list:map(
                _pipe@5,
                fun(Chunk) ->
                    {Start, End} = Chunk,
                    gleam@string:slice(Value, Start, End - Start)
                end
            )
    end.

-file("src/internal/encoder/text.gleam", 17).
?DOC(
    " Estimates the quoted string encoded the size of string in bytes,\n"
    " taking the encoding mode and the maximum size of a line into\n"
    " account.\n"
).
-spec estimate_encoded_size(
    binary(),
    internal@encoder@encoding:field_type(),
    internal@encoder@encoding:encoding_mode(),
    integer()
) -> {ok, integer()} | {error, internal@encoder@encoding:encoder_error()}.
estimate_encoded_size(String, Field_type, Mode, Maximum_size) ->
    _pipe = String,
    _pipe@1 = chunk_on_whitespace(_pipe),
    gleam@list:try_fold(
        _pipe@1,
        0,
        fun(Current_size, Chunk) ->
            case validate_chunk(Chunk, Field_type, Mode, Maximum_size - 2) of
                {ok, Chunk_size} ->
                    {ok, Current_size + Chunk_size};

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

-file("src/internal/encoder/text.gleam", 184).
-spec should_encode_character(binary(), internal@encoder@encoding:field_type()) -> boolean().
should_encode_character(Character, Field_type) ->
    should_escape_character(Character, Field_type) orelse ((Field_type =:= structured)
    andalso gleam_stdlib:contains_string(<<"()<>@,;:.[]"/utf8>>, Character)).

-file("src/internal/encoder/text.gleam", 158).
-spec encode_chunk(
    binary(),
    internal@encoder@encoding:field_type(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, {boolean(), binary()}} |
    {error, internal@encoder@encoding:encoder_error()}.
encode_chunk(Chunk, Field_type, Mode) ->
    _pipe = Chunk,
    _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
    gleam@list:try_fold(
        _pipe@1,
        {false, <<""/utf8>>},
        fun(Accumulator, Character) ->
            gleam@bool:guard(
                not is_valid_character(Character, Mode),
                {error, {invalid_character, Character}},
                fun() ->
                    {Should_quote, Characters} = Accumulator,
                    Should_quote@1 = Should_quote orelse should_encode_character(
                        Character,
                        Field_type
                    ),
                    case should_escape_character(Character, Field_type) of
                        true ->
                            {ok,
                                {Should_quote@1,
                                    begin
                                        _pipe@2 = Characters,
                                        _pipe@3 = gleam@string:append(
                                            _pipe@2,
                                            <<"\\"/utf8>>
                                        ),
                                        gleam@string:append(_pipe@3, Character)
                                    end}};

                        false ->
                            {ok,
                                {Should_quote@1,
                                    begin
                                        _pipe@4 = Characters,
                                        gleam@string:append(_pipe@4, Character)
                                    end}}
                    end
                end
            )
        end
    ).

-file("src/internal/encoder/text.gleam", 51).
-spec join_chunks(
    list(binary()),
    internal@encoder@encoding:field_type(),
    internal@encoder@encoding:encoding_mode(),
    integer(),
    integer(),
    integer()
) -> {ok, list(binary())} | {error, internal@encoder@encoding:encoder_error()}.
join_chunks(Chunks, Field_type, Mode, Count, Preferred_size, Maximum_size) ->
    _pipe = Chunks,
    _pipe@1 = gleam@list:try_fold(
        _pipe,
        {false, []},
        fun(Accumulator, Chunk) ->
            {Needs_quoting, Chunks@1} = Accumulator,
            case encode_chunk(Chunk, Field_type, Mode) of
                {ok, {Chunk_needs_quoting, Encoded_chunk}} ->
                    {ok,
                        {Needs_quoting orelse Chunk_needs_quoting,
                            [Encoded_chunk | Chunks@1]}};

                {error, Error} ->
                    {error, Error}
            end
        end
    ),
    gleam@result:'try'(
        _pipe@1,
        fun(Result) ->
            {Should_quote, Chunks@2} = Result,
            Chunks@3 = case {Should_quote, Chunks@2} of
                {true, [Chunk@1 | Other_chunks]} ->
                    lists:reverse(
                        [<<Chunk@1/binary, "\""/utf8>> | Other_chunks]
                    );

                {true, []} ->
                    lists:reverse(Chunks@2);

                {false, _} ->
                    lists:reverse(Chunks@2)
            end,
            Chunks@4 = case {Should_quote, Chunks@3} of
                {true, [Chunk@2 | Other_chunks@1]} ->
                    [<<"\""/utf8, Chunk@2/binary>> | Other_chunks@1];

                {true, []} ->
                    Chunks@3;

                {false, _} ->
                    Chunks@3
            end,
            _pipe@2 = Chunks@4,
            _pipe@3 = gleam@list:try_fold(
                _pipe@2,
                {Count, []},
                fun(Accumulator@1, Chunk@3) ->
                    {Used_size, Lines} = Accumulator@1,
                    Chunk_size = erlang:byte_size(Chunk@3),
                    gleam@bool:guard(
                        Chunk_size > Maximum_size,
                        {error, {maximum_size_exceeded, Maximum_size}},
                        fun() -> case Lines of
                                [] ->
                                    {ok, {Chunk_size, [Chunk@3]}};

                                Lines@1 when (Used_size + Chunk_size) > Preferred_size ->
                                    {ok, {Chunk_size, [Chunk@3 | Lines@1]}};

                                [First_line | Other_lines] ->
                                    {ok,
                                        {Used_size + Chunk_size,
                                            [<<First_line/binary,
                                                    Chunk@3/binary>> |
                                                Other_lines]}}
                            end end
                    )
                end
            ),
            gleam@result:map(_pipe@3, fun gleam@pair:second/1)
        end
    ).

-file("src/internal/encoder/text.gleam", 37).
?DOC(
    " Quoted string encode a string, using the specified encoding mode,\n"
    " taking the preferred line size and maximum line size into account,\n"
    " and pretending to start the first line at the passed start\n"
    " position.\n"
).
-spec encode_string(
    binary(),
    internal@encoder@encoding:field_type(),
    internal@encoder@encoding:encoding_mode(),
    integer(),
    integer(),
    integer()
) -> {ok, list(binary())} | {error, internal@encoder@encoding:encoder_error()}.
encode_string(String, Field_type, Mode, Position, Preferred_size, Maximum_size) ->
    _pipe = String,
    _pipe@1 = chunk_on_whitespace(_pipe),
    _pipe@2 = join_chunks(
        _pipe@1,
        Field_type,
        Mode,
        Position,
        Preferred_size,
        Maximum_size
    ),
    gleam@result:map(_pipe@2, fun lists:reverse/1).