-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).