Skip to main content

src/internal@renderer@internet_message.erl

-module(internal@renderer@internet_message).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/internal/renderer/internet_message.gleam").
-export([encode/2]).
-export_type([field_body_error/0, render_error/0, body_part/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(
    " This module implements functions to render a sendr message into the\n"
    " internet message format as specified in\n"
    " [RFC 5322](https://tools.ietf.org/html/rfc5322) and updated by\n"
    " [RFC 6854](https://tools.ietf.org/html/rfc6854).\n"
).

-type field_body_error() :: {invalid_date,
        {gleam@time@timestamp:timestamp(), gleam@time@duration:duration()}} |
    {invalid_email_address, binary()}.

-type render_error() :: {invalid_header_field_name, binary()} |
    {invalid_header_field_body, field_body_error()} |
    {invalid_encoding, internal@encoder@encoding:encoding()} |
    {no_usable_encoding_found, list(internal@encoder@encoding:encoding())} |
    {encoding_failed, internal@encoder@encoding:encoder_error()}.

-type body_part() :: {body_part,
        list({binary(), list(binary())}),
        list(binary())}.

-file("src/internal/renderer/internet_message.gleam", 449).
-spec prepend_field_name(binary(), list(binary())) -> list(binary()).
prepend_field_name(Field_name, Field_body) ->
    case Field_body of
        [] ->
            [<<Field_name/binary, ":"/utf8>>];

        [Head | Rest] ->
            [<<<<Field_name/binary, ": "/utf8>>/binary, Head/binary>> | Rest]
    end.

-file("src/internal/renderer/internet_message.gleam", 679).
-spec part_to_strings(body_part()) -> list(binary()).
part_to_strings(Part) ->
    {body_part, Headers, Body} = Part,
    _pipe = Headers,
    _pipe@1 = gleam@list:flat_map(
        _pipe,
        fun(Header) ->
            {Field_name, Field_body} = Header,
            prepend_field_name(Field_name, Field_body)
        end
    ),
    _pipe@2 = lists:append(_pipe@1, [<<""/utf8>>]),
    lists:append(_pipe@2, Body).

-file("src/internal/renderer/internet_message.gleam", 649).
-spec create_multipart(binary(), list(list(body_part()))) -> list(body_part()).
create_multipart(Subtype, Parts) ->
    case lists:append(Parts) of
        [] ->
            [];

        [Parts@1] ->
            [Parts@1];

        Parts@2 ->
            Boundary = <<"__sendr__"/utf8,
                (gleam_stdlib:base16_encode(crypto:strong_rand_bytes(10)))/binary>>,
            Headers = [{<<"Content-Type"/utf8>>,
                    [<<<<<<<<"multipart/"/utf8, Subtype/binary>>/binary,
                                    "; boundary=\""/utf8>>/binary,
                                Boundary/binary>>/binary,
                            "\""/utf8>>]}],
            Body = begin
                _pipe@2 = gleam@list:fold(
                    Parts@2,
                    [],
                    fun(Acc, Part) -> _pipe = Acc,
                        _pipe@1 = lists:append(
                            _pipe,
                            [<<"--"/utf8, Boundary/binary>>]
                        ),
                        lists:append(_pipe@1, part_to_strings(Part)) end
                ),
                lists:append(
                    _pipe@2,
                    [<<<<"--"/utf8, Boundary/binary>>/binary, "--"/utf8>>]
                )
            end,
            [{body_part, Headers, Body}]
    end.

-file("src/internal/renderer/internet_message.gleam", 722).
-spec string_chunk(list(binary()), binary(), integer()) -> list(binary()).
string_chunk(Accumulator, String, Chunk_size) ->
    case String of
        <<""/utf8>> ->
            lists:reverse(Accumulator);

        String@1 ->
            string_chunk(
                [gleam@string:slice(String@1, 0, Chunk_size) | Accumulator],
                gleam@string:drop_start(String@1, Chunk_size),
                Chunk_size
            )
    end.

-file("src/internal/renderer/internet_message.gleam", 600).
-spec disposition(binary(), sendr@message@attachment:attachment()) -> {ok,
        list(binary())} |
    {error, internal@encoder@encoding:encoder_error()}.
disposition(Disposition, Attachment) ->
    gleam@result:'try'(
        internal@encoder:encode_string(
            erlang:element(3, Attachment),
            {text, structured},
            ascii,
            10,
            78,
            998
        ),
        fun(Filename) ->
            gleam@result:map(
                internal@encoder:encode_string(
                    erlang:element(4, Attachment),
                    percent_encoding,
                    ascii,
                    7,
                    78 - 13,
                    998 - 13
                ),
                fun(Filename_utf8) ->
                    Filename@1 = case Filename of
                        [] ->
                            [];

                        [<<""/utf8>>] ->
                            [];

                        [Line | Rest] ->
                            [<<" filename="/utf8, Line/binary>> | Rest]
                    end,
                    Filename_utf8@1 = case Filename_utf8 of
                        [] ->
                            [];

                        [<<""/utf8>>] ->
                            [];

                        [Line@1] ->
                            [<<" filename*=UTF-8''"/utf8, Line@1/binary>>];

                        Lines ->
                            gleam@list:index_map(
                                Lines,
                                fun(Line@2, Index) -> case Index of
                                        0 ->
                                            <<" filename*1*=UTF-8''"/utf8,
                                                Line@2/binary>>;

                                        Index@1 ->
                                            <<<<<<" filename*"/utf8,
                                                        (erlang:integer_to_binary(
                                                            Index@1 + 1
                                                        ))/binary>>/binary,
                                                    "="/utf8>>/binary,
                                                Line@2/binary>>
                                    end end
                            )
                    end,
                    Disposition_header = begin
                        _pipe = [Disposition | Filename@1],
                        lists:append(_pipe, Filename_utf8@1)
                    end,
                    Disposition_header_last_item = erlang:length(
                        Disposition_header
                    )
                    - 1,
                    gleam@list:index_map(
                        Disposition_header,
                        fun(Line@3, Index@2) -> case Line@3 of
                                Line@4 when Index@2 =:= Disposition_header_last_item ->
                                    Line@4;

                                Line@5 ->
                                    <<Line@5/binary, ";"/utf8>>
                            end end
                    )
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 569).
-spec create_part(sendr@message@attachment:attachment()) -> {ok, body_part()} |
    {error, internal@encoder@encoding:encoder_error()}.
create_part(Attachment) ->
    gleam@result:map(
        begin
            _pipe@5 = case Attachment of
                {inlined_attachment, _, _, _, _, Content_id} ->
                    _pipe = disposition(<<"inline"/utf8>>, Attachment),
                    _pipe@1 = gleam@result:map(
                        _pipe,
                        fun(_capture) ->
                            gleam@pair:new(
                                <<"Content-Disposition"/utf8>>,
                                _capture
                            )
                        end
                    ),
                    _pipe@2 = gleam@result:map(_pipe@1, fun gleam@list:wrap/1),
                    gleam@result:map(
                        _pipe@2,
                        fun(_capture@1) ->
                            lists:append(
                                _capture@1,
                                [{<<"Content-ID"/utf8>>,
                                        [<<<<"<"/utf8, Content_id/binary>>/binary,
                                                ">"/utf8>>]}]
                            )
                        end
                    );

                {attached_attachment, _, _, _, _} ->
                    _pipe@3 = disposition(<<"attachment"/utf8>>, Attachment),
                    _pipe@4 = gleam@result:map(
                        _pipe@3,
                        fun(_capture@2) ->
                            gleam@pair:new(
                                <<"Content-Disposition"/utf8>>,
                                _capture@2
                            )
                        end
                    ),
                    gleam@result:map(_pipe@4, fun gleam@list:wrap/1)
            end,
            gleam@result:map(
                _pipe@5,
                fun(_capture@3) ->
                    lists:append(
                        _capture@3,
                        [{<<"Content-Type"/utf8>>,
                                [erlang:element(2, Attachment)]},
                            {<<"Content-Transfer-Encoding"/utf8>>,
                                [<<"base64"/utf8>>]}]
                    )
                end
            )
        end,
        fun(Headers) ->
            Body = begin
                _pipe@6 = erlang:element(5, Attachment),
                _pipe@7 = gleam_stdlib:base64_encode(_pipe@6, true),
                string_chunk([], _pipe@7, 78)
            end,
            {body_part, Headers, Body}
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 544).
-spec create_attachments(list(sendr@message@attachment:attachment()), boolean()) -> {ok,
        {list(body_part()), list(body_part())}} |
    {error, render_error()}.
create_attachments(Attachments, Should_inline) ->
    _pipe@2 = gleam@list:try_fold(
        Attachments,
        {[], []},
        fun(Accumulator, Attachment) -> _pipe = Attachment,
            _pipe@1 = create_part(_pipe),
            gleam@result:map(
                _pipe@1,
                fun(Part) ->
                    {Inlined_attachments, Attached_attachments} = Accumulator,
                    case Attachment of
                        {inlined_attachment, _, _, _, _, _} when Should_inline ->
                            {[Part | Inlined_attachments], Attached_attachments};

                        _ ->
                            {Inlined_attachments, [Part | Attached_attachments]}
                    end
                end
            ) end
    ),
    _pipe@3 = gleam@result:map(
        _pipe@2,
        fun(Attachments@1) ->
            {Inlined_attachments@1, Attached_attachments@1} = Attachments@1,
            {lists:reverse(Inlined_attachments@1),
                lists:reverse(Attached_attachments@1)}
        end
    ),
    gleam@result:map_error(_pipe@3, fun(Error) -> {encoding_failed, Error} end).

-file("src/internal/renderer/internet_message.gleam", 738).
-spec guess_charset(binary()) -> binary().
guess_charset(Text) ->
    Is_ascii = fun(Char) ->
        (gleam@string:compare(Char, <<"\x{0}"/utf8>>) =:= gt) andalso (gleam@string:compare(
            Char,
            <<"\x{80}"/utf8>>
        )
        =:= lt)
    end,
    case gleam@list:all(gleam@string:split(Text, <<""/utf8>>), Is_ascii) of
        true ->
            <<"us-ascii"/utf8>>;

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

-file("src/internal/renderer/internet_message.gleam", 515).
-spec encode_body_part(
    binary(),
    binary(),
    internal@encoder@encoding:encoding(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, body_part()} | {error, internal@encoder@encoding:encoder_error()}.
encode_body_part(Subtype, Value, Encoding, Mode) ->
    _pipe = Value,
    _pipe@1 = internal@encoder:encode_string(_pipe, Encoding, Mode, 0, 78, 998),
    gleam@result:map(
        _pipe@1,
        fun(Lines) ->
            Charset = guess_charset(Value),
            Content_type = gleam@string:join(
                [<<"text/"/utf8>>, Subtype, <<"; charset="/utf8>>, Charset],
                <<""/utf8>>
            ),
            {body_part,
                [{<<"Content-Transfer-Encoding"/utf8>>,
                        [internal@encoder:name(Encoding)]},
                    {<<"Content-Type"/utf8>>, [Content_type]}],
                Lines}
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 490).
-spec create_body(
    binary(),
    gleam@option:option(binary()),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(body_part())} | {error, render_error()}.
create_body(Subtype, Value, Mode) ->
    case Value of
        none ->
            {ok, []};

        {some, Value@1} ->
            _pipe = [{bit, 7}, {bit, 8}, {quoted_printable, rfc2045}, base64],
            _pipe@1 = internal@encoder:determine_encoding_order(
                _pipe,
                Value@1,
                Mode,
                998
            ),
            gleam@list:fold_until(
                _pipe@1,
                {error, {encoding_failed, no_encoder_found}},
                fun(_, Enc) ->
                    Result = encode_body_part(Subtype, Value@1, Enc, Mode),
                    case Result of
                        {ok, Body_part} ->
                            {stop, {ok, [Body_part]}};

                        {error, Result@1} ->
                            {continue, {error, {encoding_failed, Result@1}}}
                    end
                end
            )
    end.

-file("src/internal/renderer/internet_message.gleam", 462).
-spec body(
    {sendr@message@body:body(), list(sendr@message@attachment:attachment())},
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
body(Value, Mode) ->
    {Body, Attachments} = Value,
    gleam@result:'try'(
        create_body(<<"plain"/utf8>>, erlang:element(2, Body), Mode),
        fun(Text_body) ->
            gleam@result:'try'(
                create_body(<<"html"/utf8>>, erlang:element(3, Body), Mode),
                fun(Html_body) ->
                    gleam@result:'try'(
                        create_attachments(
                            Attachments,
                            not gleam@list:is_empty(Html_body)
                        ),
                        fun(_use0) ->
                            {Inlined_attachments, Attached_attachments} = _use0,
                            _pipe = create_multipart(
                                <<"mixed"/utf8>>,
                                [create_multipart(
                                        <<"alternative"/utf8>>,
                                        [Text_body,
                                            create_multipart(
                                                <<"related"/utf8>>,
                                                [Html_body, Inlined_attachments]
                                            )]
                                    ),
                                    Attached_attachments]
                            ),
                            (fun(Message_part) -> case Message_part of
                                    [] ->
                                        {ok, []};

                                    [Part] ->
                                        {ok,
                                            [<<""/utf8>> |
                                                part_to_strings(Part)]};

                                    _ ->
                                        {error,
                                            {encoding_failed, unknown_error}}
                                end end)(_pipe)
                        end
                    )
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 694).
-spec append_strings(
    {ok, list(binary())} | {error, render_error()},
    GST,
    fun((GST) -> {ok, list(binary())} | {error, render_error()})
) -> {ok, list(binary())} | {error, render_error()}.
append_strings(Data, Value, Callback) ->
    _pipe = Data,
    gleam@result:'try'(_pipe, fun(Data@1) -> _pipe@1 = Value,
            _pipe@2 = Callback(_pipe@1),
            gleam@result:map(_pipe@2, fun(Lines) -> _pipe@3 = Lines,
                    _pipe@4 = lists:reverse(_pipe@3),
                    lists:append(_pipe@4, Data@1) end) end).

-file("src/internal/renderer/internet_message.gleam", 766).
-spec try_encode_field_body(
    internal@encoder@encoding:encoding(),
    binary(),
    internal@encoder@encoding:encoding_mode(),
    integer()
) -> {ok, list(binary())} | {error, render_error()}.
try_encode_field_body(Encoding, Value, Mode, Used) ->
    gleam@result:'try'(case Encoding of
            base64 ->
                {ok,
                    {<<"=?utf-8?B?"/utf8>>,
                        <<" =?utf-8?B?"/utf8>>,
                        <<"?="/utf8>>}};

            {quoted_printable, _} ->
                {ok,
                    {<<"=?utf-8?Q?"/utf8>>,
                        <<" =?utf-8?Q?"/utf8>>,
                        <<"?="/utf8>>}};

            {text, _} ->
                {ok, {<<""/utf8>>, <<""/utf8>>, <<""/utf8>>}};

            Encoding@1 ->
                {error, {invalid_encoding, Encoding@1}}
        end, fun(_use0) ->
            {First_prefix, Prefix, Postfix} = _use0,
            Encoding_size = erlang:byte_size(Prefix) - erlang:byte_size(Postfix),
            _pipe = internal@encoder:encode_string(
                Value,
                Encoding,
                Mode,
                Used + erlang:byte_size(First_prefix),
                78 - Encoding_size,
                998 - Encoding_size
            ),
            _pipe@1 = gleam@result:map_error(
                _pipe,
                fun(Field@0) -> {encoding_failed, Field@0} end
            ),
            gleam@result:map(
                _pipe@1,
                fun(Encoded_string) -> _pipe@2 = Encoded_string,
                    gleam@list:index_map(
                        _pipe@2,
                        fun(Line, Index) -> case Index of
                                0 ->
                                    <<<<First_prefix/binary, Line/binary>>/binary,
                                        Postfix/binary>>;

                                _ ->
                                    <<<<Prefix/binary, Line/binary>>/binary,
                                        Postfix/binary>>
                            end end
                    ) end
            )
        end).

-file("src/internal/renderer/internet_message.gleam", 750).
-spec encode_field_body(
    binary(),
    integer(),
    list(internal@encoder@encoding:encoding()),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
encode_field_body(Value, Used, Encoders, Mode) ->
    _pipe = Encoders,
    _pipe@1 = internal@encoder:determine_encoding_order(_pipe, Value, Mode, 998),
    gleam@list:fold_until(
        _pipe@1,
        {error, {no_usable_encoding_found, Encoders}},
        fun(_, Encoding) ->
            case try_encode_field_body(Encoding, Value, Mode, Used) of
                {ok, _} = Result ->
                    {stop, Result};

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

-file("src/internal/renderer/internet_message.gleam", 436).
-spec is_valid_field_name(binary()) -> boolean().
is_valid_field_name(Field_name) ->
    (Field_name /= <<""/utf8>>) andalso begin
        _pipe = Field_name,
        _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
        gleam@list:all(
            _pipe@1,
            fun(Char) ->
                ((gleam@string:compare(Char, <<"\x{20}"/utf8>>) =:= gt) andalso (gleam@string:compare(
                    Char,
                    <<"\x{7f}"/utf8>>
                )
                =:= lt))
                andalso (gleam@string:compare(Char, <<":"/utf8>>) /= eq)
            end
        )
    end.

-file("src/internal/renderer/internet_message.gleam", 422).
-spec normalize_field_name(binary()) -> binary().
normalize_field_name(Field_name) ->
    _pipe = Field_name,
    _pipe@1 = gleam@string:replace(_pipe, <<"_"/utf8>>, <<"-"/utf8>>),
    _pipe@2 = gleam@string:split(_pipe@1, <<"-"/utf8>>),
    _pipe@3 = gleam@list:map(
        _pipe@2,
        fun(Part) ->
            Part@1 = string:uppercase(gleam@string:trim(Part)),
            case Part@1 of
                <<"ID"/utf8>> ->
                    Part@1;

                <<"MIME"/utf8>> ->
                    Part@1;

                _ ->
                    gleam@string:capitalise(Part@1)
            end
        end
    ),
    gleam@string:join(_pipe@3, <<"-"/utf8>>).

-file("src/internal/renderer/internet_message.gleam", 414).
-spec encode_field_name(binary()) -> {ok, binary()} | {error, render_error()}.
encode_field_name(Field_name) ->
    Normalized_field_name = normalize_field_name(Field_name),
    case is_valid_field_name(Normalized_field_name) of
        true ->
            {ok, Normalized_field_name};

        false ->
            {error, {invalid_header_field_name, Field_name}}
    end.

-file("src/internal/renderer/internet_message.gleam", 395).
-spec unstructured_header(
    binary(),
    binary(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
unstructured_header(Name, Value, Mode) ->
    gleam@result:'try'(
        encode_field_name(Name),
        fun(Field_name) ->
            gleam@result:map(
                encode_field_body(
                    Value,
                    erlang:byte_size(Field_name) + 2,
                    [{text, unstructured}, {quoted_printable, rfc2047}, base64],
                    Mode
                ),
                fun(Field_body) ->
                    prepend_field_name(Field_name, Field_body)
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 351).
-spec encode_email_address(binary(), internal@encoder@encoding:encoding_mode()) -> {ok,
        binary()} |
    {error, render_error()}.
encode_email_address(Address, Mode) ->
    _pipe = internal@encoder:encode_email_address(Address, Mode),
    gleam@result:map_error(
        _pipe,
        fun(Field@0) -> {encoding_failed, Field@0} end
    ).

-file("src/internal/renderer/internet_message.gleam", 363).
-spec message_id_header(
    binary(),
    sendr@message@mailbox:mailbox(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
message_id_header(Name, Mailbox, Mode) ->
    gleam@result:'try'(
        encode_field_name(Name),
        fun(Field_name) ->
            _pipe = gleam@string:split_once(
                erlang:element(3, Mailbox),
                <<"@"/utf8>>
            ),
            _pipe@1 = gleam@result:replace_error(
                _pipe,
                {invalid_header_field_body,
                    {invalid_email_address, erlang:element(3, Mailbox)}}
            ),
            gleam@result:'try'(
                _pipe@1,
                fun(Local_domain) ->
                    {Seconds, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(
                        gleam@time@timestamp:system_time()
                    ),
                    Random = gleam_stdlib:base64_encode(
                        crypto:strong_rand_bytes(10),
                        false
                    ),
                    Message_id = <<<<<<<<(gleam@int:to_base16(Seconds))/binary,
                                    "."/utf8>>/binary,
                                Random/binary>>/binary,
                            "@"/utf8>>/binary,
                        (gleam@pair:second(Local_domain))/binary>>,
                    _pipe@2 = Message_id,
                    _pipe@3 = encode_email_address(_pipe@2, Mode),
                    gleam@result:map(
                        _pipe@3,
                        fun(Address) ->
                            prepend_field_name(Field_name, [Address])
                        end
                    )
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 711).
-spec maybe_append_strings(
    {ok, list(binary())} | {error, render_error()},
    gleam@option:option(GTD),
    fun((GTD) -> {ok, list(binary())} | {error, render_error()})
) -> {ok, list(binary())} | {error, render_error()}.
maybe_append_strings(Data, Value, Callback) ->
    case Value of
        none ->
            Data;

        {some, Value@1} ->
            append_strings(Data, Value@1, Callback)
    end.

-file("src/internal/renderer/internet_message.gleam", 337).
-spec encode_mailbox_address(
    sendr@message@mailbox:mailbox(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, binary()} | {error, render_error()}.
encode_mailbox_address(Mailbox, Mode) ->
    _pipe = erlang:element(3, Mailbox),
    _pipe@1 = encode_email_address(_pipe, Mode),
    gleam@result:map(_pipe@1, fun(Address) -> case erlang:element(2, Mailbox) of
                <<""/utf8>> ->
                    Address;

                _ ->
                    <<<<"<"/utf8, Address/binary>>/binary, ">"/utf8>>
            end end).

-file("src/internal/renderer/internet_message.gleam", 279).
-spec encode_mailbox(
    sendr@message@mailbox:mailbox(),
    integer(),
    internal@encoder@encoding:encoding_mode(),
    binary(),
    binary()
) -> {ok, list(binary())} | {error, render_error()}.
encode_mailbox(Mailbox, Used_size, Mode, Indent, Separator) ->
    gleam@result:'try'(
        encode_field_body(
            erlang:element(2, Mailbox),
            Used_size,
            [{text, structured}, {quoted_printable, rfc2047}, base64],
            Mode
        ),
        fun(Display_name) ->
            gleam@result:'try'(
                encode_mailbox_address(Mailbox, Mode),
                fun(Address) ->
                    Address@1 = <<Address/binary, Separator/binary>>,
                    Address_size = erlang:byte_size(Address@1),
                    Reversed_display_name = lists:reverse(Display_name),
                    Last_display_name_size = begin
                        _pipe = gleam@list:first(Reversed_display_name),
                        _pipe@1 = gleam@result:unwrap(_pipe, <<""/utf8>>),
                        erlang:byte_size(_pipe@1)
                    end,
                    Indent_size = erlang:byte_size(Indent),
                    gleam@bool:guard(
                        (Address_size + Indent_size) >= 998,
                        {error, {encoding_failed, {maximum_size_exceeded, 998}}},
                        fun() -> _pipe@4 = case Reversed_display_name of
                                [] when ((Used_size + Indent_size) + Address_size) =< 78 ->
                                    [<<Indent/binary, Address@1/binary>>];

                                [] ->
                                    [<<""/utf8>>,
                                        <<" "/utf8, Address@1/binary>>];

                                [Display_name@1] when ((((Used_size + Indent_size) + Last_display_name_size) + 1) + Address_size) =< 78 ->
                                    [<<<<Display_name@1/binary, " "/utf8>>/binary,
                                            Address@1/binary>>];

                                [Last_display_name | Other_display_name] when ((Last_display_name_size + 1) + Address_size) =< 78 ->
                                    _pipe@2 = [<<<<Last_display_name/binary,
                                                " "/utf8>>/binary,
                                            Address@1/binary>> |
                                        Other_display_name],
                                    lists:reverse(_pipe@2);

                                Display_name@2 ->
                                    _pipe@3 = [<<" "/utf8, Address@1/binary>> |
                                        Display_name@2],
                                    lists:reverse(_pipe@3)
                            end,
                            (fun(Lines) -> case Lines of
                                    [] ->
                                        {ok, []};

                                    [<<""/utf8>> | Rest] ->
                                        {ok, [<<""/utf8>> | Rest]};

                                    [First_line | Rest@1] ->
                                        {ok,
                                            [<<Indent/binary,
                                                    First_line/binary>> |
                                                Rest@1]}
                                end end)(_pipe@4) end
                    )
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 805).
-spec try_index_fold(
    list(GTU),
    GTW,
    fun((GTW, GTU, integer()) -> {ok, GTW} | {error, GTX})
) -> {ok, GTW} | {error, GTX}.
try_index_fold(List, Initial, Fun) ->
    _pipe@1 = gleam@list:try_fold(
        List,
        {0, Initial},
        fun(Accumulator, Item) ->
            {Index, Accumulator@1} = Accumulator,
            _pipe = Fun(Accumulator@1, Item, Index),
            gleam@result:map(
                _pipe,
                fun(Accumulator@2) -> {Index + 1, Accumulator@2} end
            )
        end
    ),
    gleam@result:map(_pipe@1, fun gleam@pair:second/1).

-file("src/internal/renderer/internet_message.gleam", 235).
-spec encode_mailbox_list(
    list(sendr@message@mailbox:mailbox()),
    integer(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
encode_mailbox_list(Mailboxes, Count, Mode) ->
    Last_mailbox_index = erlang:length(Mailboxes) - 1,
    _pipe = Mailboxes,
    _pipe@3 = try_index_fold(
        _pipe,
        {Count, []},
        fun(Accumulator, Mailbox, Index) ->
            {Count@1, Lines} = Accumulator,
            Indent = case Index of
                0 ->
                    <<""/utf8>>;

                _ ->
                    <<" "/utf8>>
            end,
            Separator = case Index of
                Index@1 when Index@1 < Last_mailbox_index ->
                    <<","/utf8>>;

                _ ->
                    <<""/utf8>>
            end,
            _pipe@1 = encode_mailbox(
                Mailbox,
                Count@1 + 1,
                Mode,
                Indent,
                Separator
            ),
            _pipe@2 = gleam@result:map(
                _pipe@1,
                fun(Mailbox_lines) -> case {Mailbox_lines, Lines} of
                        {[], Lines@1} ->
                            Lines@1;

                        {Mailbox_lines@1, []} ->
                            Mailbox_lines@1;

                        {[<<""/utf8>> | Other_mailbox_lines],
                            [Last_line | Other_lines]} ->
                            lists:append(
                                Other_mailbox_lines,
                                [Last_line | Other_lines]
                            );

                        {[First_mailbox_line | Other_mailbox_lines@1],
                            [Last_line@1 | Other_lines@1]} ->
                            lists:append(
                                Other_mailbox_lines@1,
                                [<<Last_line@1/binary,
                                        First_mailbox_line/binary>> |
                                    Other_lines@1]
                            )
                    end end
            ),
            gleam@result:map(_pipe@2, fun(Lines@2) -> case Lines@2 of
                        [] ->
                            {0, Lines@2};

                        [Last_line@2 | _] ->
                            {erlang:byte_size(Last_line@2), Lines@2}
                    end end)
        end
    ),
    gleam@result:map(_pipe@3, fun(Result) -> gleam@pair:second(Result) end).

-file("src/internal/renderer/internet_message.gleam", 221).
-spec mailbox_list_header(
    binary(),
    list(sendr@message@mailbox:mailbox()),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
mailbox_list_header(Field_name, Field_body, Mode) ->
    gleam@result:'try'(
        encode_field_name(Field_name),
        fun(Field_name@1) ->
            gleam@result:map(
                encode_mailbox_list(
                    Field_body,
                    erlang:byte_size(Field_name@1) + 2,
                    Mode
                ),
                fun(Encoded_mailboxes) ->
                    prepend_field_name(Field_name@1, Encoded_mailboxes)
                end
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 213).
-spec mailbox_header(
    binary(),
    sendr@message@mailbox:mailbox(),
    internal@encoder@encoding:encoding_mode()
) -> {ok, list(binary())} | {error, render_error()}.
mailbox_header(Field_name, Field_body, Mode) ->
    mailbox_list_header(Field_name, [Field_body], Mode).

-file("src/internal/renderer/internet_message.gleam", 205).
-spec pad(integer()) -> binary().
pad(Value) ->
    gleam@string:pad_start(erlang:integer_to_binary(Value), 2, <<"0"/utf8>>).

-file("src/internal/renderer/internet_message.gleam", 193).
-spec duration_to_string(gleam@time@duration:duration()) -> binary().
duration_to_string(Duration) ->
    {Seconds, _} = gleam@time@duration:to_seconds_and_nanoseconds(Duration),
    Hours = gleam@int:absolute_value(Seconds div 3600),
    Minutes = gleam@int:absolute_value((Seconds div 60) rem 60),
    case Seconds >= 0 of
        true ->
            <<<<"+"/utf8, (pad(Hours))/binary>>/binary, (pad(Minutes))/binary>>;

        false ->
            <<<<"-"/utf8, (pad(Hours))/binary>>/binary, (pad(Minutes))/binary>>
    end.

-file("src/internal/renderer/internet_message.gleam", 176).
-spec month_to_string(gleam@time@calendar:month()) -> binary().
month_to_string(Month) ->
    case Month of
        january ->
            <<"Jan"/utf8>>;

        february ->
            <<"Feb"/utf8>>;

        march ->
            <<"Mar"/utf8>>;

        april ->
            <<"Apr"/utf8>>;

        may ->
            <<"May"/utf8>>;

        june ->
            <<"Jun"/utf8>>;

        july ->
            <<"Jul"/utf8>>;

        august ->
            <<"Aug"/utf8>>;

        september ->
            <<"Sep"/utf8>>;

        october ->
            <<"Oct"/utf8>>;

        november ->
            <<"Nov"/utf8>>;

        december ->
            <<"Dec"/utf8>>
    end.

-file("src/internal/renderer/internet_message.gleam", 156).
-spec weekday_to_string(gleam@time@timestamp:timestamp()) -> binary().
weekday_to_string(Timestamp) ->
    {Seconds, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(
        Timestamp
    ),
    Weekday = ((Seconds div 86400) + 4) rem 7,
    case Weekday of
        0 ->
            <<"Sun"/utf8>>;

        1 ->
            <<"Mon"/utf8>>;

        2 ->
            <<"Tue"/utf8>>;

        3 ->
            <<"Wed"/utf8>>;

        4 ->
            <<"Thu"/utf8>>;

        5 ->
            <<"Fri"/utf8>>;

        6 ->
            <<"Sat"/utf8>>;

        _ ->
            erlang:error(#{gleam_error => panic,
                    message => (<<"Unexpectely received "/utf8,
                        (erlang:integer_to_binary(Weekday))/binary>>),
                    file => <<?FILEPATH/utf8>>,
                    module => <<"internal/renderer/internet_message"/utf8>>,
                    function => <<"weekday_to_string"/utf8>>,
                    line => 172})
    end.

-file("src/internal/renderer/internet_message.gleam", 136).
-spec date_header(
    binary(),
    {gleam@time@timestamp:timestamp(), gleam@time@duration:duration()}
) -> {ok, list(binary())} | {error, render_error()}.
date_header(Field_name, Field_body) ->
    gleam@result:map(
        encode_field_name(Field_name),
        fun(Field_name@1) ->
            {Timestamp, Offset} = Field_body,
            {{date, Year, Month, Day},
                {time_of_day, Hours, Minutes, Seconds, _}} = gleam@time@timestamp:to_calendar(
                Timestamp,
                {duration, 0, 0}
            ),
            Weekday = weekday_to_string(Timestamp),
            Date = gleam@string:join(
                [pad(Day),
                    month_to_string(Month),
                    erlang:integer_to_binary(Year)],
                <<" "/utf8>>
            ),
            Time = gleam@string:join(
                [pad(Hours), pad(Minutes), pad(Seconds)],
                <<":"/utf8>>
            ),
            Zone = duration_to_string(Offset),
            prepend_field_name(
                Field_name@1,
                [<<<<<<<<<<<<Weekday/binary, ", "/utf8>>/binary, Date/binary>>/binary,
                                    " "/utf8>>/binary,
                                Time/binary>>/binary,
                            " "/utf8>>/binary,
                        Zone/binary>>]
            )
        end
    ).

-file("src/internal/renderer/internet_message.gleam", 104).
?DOC(
    " Render a sendr Message into an Internet Message format string.\n"
    "\n"
    " This function converts a `sendr/message.Message` into a string\n"
    " conforming to [RFC 5322](https://tools.ietf.org/html/rfc5322)\n"
    " and updated by [RFC 6854](https://tools.ietf.org/html/rfc6854).\n"
    "\n"
    " The rendered message includes:\n"
    " - `Date` header (automatically generated from the current system time)\n"
    " - `From`, `Reply-To`, `To`, `Cc` headers (if set on the message)\n"
    " - `Subject` header (if set on the message)\n"
    " - `Message-ID` header (generated from the sender's address)\n"
    " - `MIME-Version: 1.0` header\n"
    " - Message body (text and/or HTML) with appropriate MIME encoding\n"
    " - Attachments (if any), wrapped in multipart/mixed or multipart/related\n"
).
-spec encode(sendr@message:message(), internal@encoder@encoding:encoding_mode()) -> {ok,
        list(binary())} |
    {error, render_error()}.
encode(Message, Mode) ->
    Date = {gleam@time@timestamp:system_time(), {duration, 0, 0}},
    Message_id = gleam@option:'or'(
        erlang:element(2, Message),
        {some, sendr@message@mailbox:new(<<""/utf8>>, <<"@localhost"/utf8>>)}
    ),
    _pipe = {ok, []},
    _pipe@1 = append_strings(
        _pipe,
        Date,
        fun(_capture) -> date_header(<<"Date"/utf8>>, _capture) end
    ),
    _pipe@2 = maybe_append_strings(
        _pipe@1,
        erlang:element(2, Message),
        fun(_capture@1) -> mailbox_header(<<"From"/utf8>>, _capture@1, Mode) end
    ),
    _pipe@3 = maybe_append_strings(
        _pipe@2,
        erlang:element(3, Message),
        fun(_capture@2) ->
            mailbox_list_header(<<"Reply-To"/utf8>>, _capture@2, Mode)
        end
    ),
    _pipe@4 = maybe_append_strings(
        _pipe@3,
        erlang:element(4, Message),
        fun(_capture@3) ->
            mailbox_list_header(<<"To"/utf8>>, _capture@3, Mode)
        end
    ),
    _pipe@5 = maybe_append_strings(
        _pipe@4,
        erlang:element(5, Message),
        fun(_capture@4) ->
            mailbox_list_header(<<"Cc"/utf8>>, _capture@4, Mode)
        end
    ),
    _pipe@6 = maybe_append_strings(
        _pipe@5,
        erlang:element(7, Message),
        fun(_capture@5) ->
            unstructured_header(<<"Subject"/utf8>>, _capture@5, Mode)
        end
    ),
    _pipe@7 = maybe_append_strings(
        _pipe@6,
        Message_id,
        fun(_capture@6) ->
            message_id_header(<<"Message-ID"/utf8>>, _capture@6, Mode)
        end
    ),
    _pipe@8 = append_strings(
        _pipe@7,
        <<"1.0"/utf8>>,
        fun(_capture@7) ->
            unstructured_header(<<"MIME-Version"/utf8>>, _capture@7, Mode)
        end
    ),
    _pipe@9 = append_strings(
        _pipe@8,
        {erlang:element(9, Message), erlang:element(8, Message)},
        fun(_capture@8) -> body(_capture@8, Mode) end
    ),
    gleam@result:map(_pipe@9, fun lists:reverse/1).