Current section

Files

Jump to
gcourier src gcourier@message.erl
Raw

src/gcourier@message.erl

-module(gcourier@message).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gcourier/message.gleam").
-export([build/0, add_recipient/3, add_attachment/4, set_subject/2, set_date/2, set_html/2, set_text/2, set_from/3, set_sender/3, day_of_week/3, date_from_cal/2, render/1]).
-export_type([attachment/0, message/0, recipient_type/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 provides tools for constructing RFC-compliant email messages.\n").
-type attachment() :: {attachment, binary(), binary(), binary()}.
-type message() :: {message,
gleam@dict:dict(binary(), binary()),
list(binary()),
list(binary()),
list(binary()),
binary(),
gleam@option:option(list(attachment()))}.
-type recipient_type() :: to | c_c | b_c_c.
-file("src/gcourier/message.gleam", 37).
-spec build() -> message().
build() ->
{message, maps:from_list([]), [], [], [], <<""/utf8>>, none}.
-file("src/gcourier/message.gleam", 95).
-spec render_attachment(binary(), attachment()) -> binary().
render_attachment(Boundary, Attachment) ->
<<<<<<<<<<<<<<<<<<"--"/utf8, Boundary/binary>>/binary,
"\r\nContent-Type: "/utf8>>/binary,
(erlang:element(3, Attachment))/binary>>/binary,
"\r\nContent-Disposition: "/utf8>>/binary,
"attachment; filename=\""/utf8>>/binary,
(erlang:element(2, Attachment))/binary>>/binary,
"\"\r\n"/utf8>>/binary,
"Content-Transfer-Encoding: base64\r\n\r\n"/utf8>>/binary,
(erlang:element(4, Attachment))/binary>>.
-file("src/gcourier/message.gleam", 190).
?DOC(
" Add the provided address to the list of recipients.\n"
" \n"
" recipient_type should be one of To, Cc, or Bcc.\n"
).
-spec add_recipient(message(), binary(), recipient_type()) -> message().
add_recipient(Message, Email, Recipient_type) ->
case Recipient_type of
to ->
{message,
erlang:element(2, Message),
[Email | erlang:element(3, Message)],
erlang:element(4, Message),
erlang:element(5, Message),
erlang:element(6, Message),
erlang:element(7, Message)};
c_c ->
{message,
erlang:element(2, Message),
erlang:element(3, Message),
[Email | erlang:element(4, Message)],
erlang:element(5, Message),
erlang:element(6, Message),
erlang:element(7, Message)};
b_c_c ->
{message,
erlang:element(2, Message),
erlang:element(3, Message),
erlang:element(4, Message),
[Email | erlang:element(5, Message)],
erlang:element(6, Message),
erlang:element(7, Message)}
end.
-file("src/gcourier/message.gleam", 241).
-spec add_attachment(message(), binary(), binary(), binary()) -> message().
add_attachment(Message, Path, Name, Content_type) ->
Content@1 = case simplifile_erl:read_bits(Path) of
{ok, Content} -> Content;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"add_attachment"/utf8>>,
line => 247,
value => _assert_fail,
start => 5728,
'end' => 5785,
pattern_start => 5739,
pattern_end => 5750})
end,
Content@2 = gleam_stdlib:bit_array_base64_encode(Content@1, false),
Attachment = {attachment, Name, Content_type, Content@2},
Attachments = case erlang:element(7, Message) of
none ->
[Attachment];
{some, A} ->
[Attachment | A]
end,
{message,
erlang:element(2, Message),
erlang:element(3, Message),
erlang:element(4, Message),
erlang:element(5, Message),
erlang:element(6, Message),
{some, Attachments}}.
-file("src/gcourier/message.gleam", 258).
-spec set_content(message(), binary()) -> message().
set_content(Message, Text) ->
{message,
erlang:element(2, Message),
erlang:element(3, Message),
erlang:element(4, Message),
erlang:element(5, Message),
Text,
erlang:element(7, Message)}.
-file("src/gcourier/message.gleam", 264).
-spec set_header(message(), binary(), binary()) -> message().
set_header(Message, Name, Value) ->
{message,
gleam@dict:insert(erlang:element(2, Message), Name, Value),
erlang:element(3, Message),
erlang:element(4, Message),
erlang:element(5, Message),
erlang:element(6, Message),
erlang:element(7, Message)}.
-file("src/gcourier/message.gleam", 203).
?DOC(" Set the message's subject line. Optional.\n").
-spec set_subject(message(), binary()) -> message().
set_subject(Message, Subject) ->
_pipe = Message,
set_header(_pipe, <<"Subject"/utf8>>, Subject).
-file("src/gcourier/message.gleam", 223).
?DOC(
" Set the Date header for the email. Optional.\n"
"\n"
" If this is not explicitly set, the current system time will be used automatically\n"
" when the message is sent. This header indicates when the email was created.\n"
).
-spec set_date(message(), binary()) -> message().
set_date(Message, Date) ->
_pipe = Message,
set_header(_pipe, <<"Date"/utf8>>, Date).
-file("src/gcourier/message.gleam", 229).
-spec set_html(message(), binary()) -> message().
set_html(Message, Html) ->
_pipe = Message,
_pipe@1 = set_header(_pipe, <<"Content-Type"/utf8>>, <<"text/html"/utf8>>),
set_content(_pipe@1, Html).
-file("src/gcourier/message.gleam", 235).
-spec set_text(message(), binary()) -> message().
set_text(Message, Text) ->
_pipe = Message,
_pipe@1 = set_header(_pipe, <<"Content-Type"/utf8>>, <<"text/plain"/utf8>>),
set_content(_pipe@1, Text).
-file("src/gcourier/message.gleam", 268).
-spec format_address(binary(), gleam@option:option(binary())) -> binary().
format_address(Address, Name) ->
case Name of
{some, Name@1} ->
<<<<<<Name@1/binary, " <"/utf8>>/binary, Address/binary>>/binary,
">"/utf8>>;
none ->
Address
end.
-file("src/gcourier/message.gleam", 179).
?DOC(" Set the FROM header in the email.\n").
-spec set_from(message(), binary(), gleam@option:option(binary())) -> message().
set_from(Message, Address, Name) ->
_pipe = Message,
set_header(_pipe, <<"From"/utf8>>, format_address(Address, Name)).
-file("src/gcourier/message.gleam", 211).
?DOC(
" Set the _optional_ sender header. Prefer FROM in most cases.\n"
" \n"
" This field is useful when the email is sent on behalf of \n"
" a third party or there are multiple emails in the FROM field.\n"
).
-spec set_sender(message(), binary(), gleam@option:option(binary())) -> message().
set_sender(Message, Address, Name) ->
_pipe = Message,
set_header(_pipe, <<"Sender"/utf8>>, format_address(Address, Name)).
-file("src/gcourier/message.gleam", 315).
?DOC(false).
-spec day_of_week(integer(), integer(), integer()) -> binary().
day_of_week(Q, M, Y) ->
Y@1 = case M < 3 of
true ->
Y - 1;
false ->
Y
end,
M@1 = case M < 3 of
true ->
M + 12;
false ->
M
end,
K = Y@1 rem 100,
J = Y@1 div 100,
H = ((((Q + ((13 * (M@1 + 1)) div 5)) + K) + (K div 4)) + (J div 4)) + (5 * J),
case H rem 7 of
0 ->
<<"Sat"/utf8>>;
1 ->
<<"Sun"/utf8>>;
2 ->
<<"Mon"/utf8>>;
3 ->
<<"Tue"/utf8>>;
4 ->
<<"Wed"/utf8>>;
5 ->
<<"Thu"/utf8>>;
6 ->
<<"Fri"/utf8>>;
_ ->
erlang:error(#{gleam_error => panic,
message => <<"`panic` expression evaluated."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"day_of_week"/utf8>>,
line => 338})
end.
-file("src/gcourier/message.gleam", 278).
?DOC(false).
-spec date_from_cal(
gleam@time@calendar:date(),
gleam@time@calendar:time_of_day()
) -> binary().
date_from_cal(Cal, Time) ->
Month = begin
_pipe = gleam@time@calendar:month_to_string(erlang:element(3, Cal)),
gleam@string:slice(_pipe, 0, 3)
end,
Offset = erlang:round(
gleam@time@duration:to_seconds(gleam@time@calendar:local_offset())
),
Offset_sign = case Offset > 0 of
true ->
<<"+"/utf8>>;
false ->
<<""/utf8>>
end,
Offset_hours = begin
_pipe@1 = (Offset div 3600),
_pipe@2 = erlang:integer_to_binary(_pipe@1),
gleam@string:pad_start(_pipe@2, 2, <<"0"/utf8>>)
end,
Offset_minutes = begin
_pipe@3 = ((Offset rem 3600) div 60),
_pipe@4 = erlang:integer_to_binary(_pipe@3),
gleam@string:pad_start(_pipe@4, 2, <<"0"/utf8>>)
end,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<(day_of_week(
erlang:element(
4,
Cal
),
gleam@time@calendar:month_to_int(
erlang:element(
3,
Cal
)
),
erlang:element(
2,
Cal
)
))/binary,
", "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
4,
Cal
)
))/binary>>/binary,
" "/utf8>>/binary,
Month/binary>>/binary,
" "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(2, Cal)
))/binary>>/binary,
" "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(2, Time)
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(3, Time)
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(4, Time)))/binary>>/binary,
" "/utf8>>/binary,
Offset_sign/binary>>/binary,
Offset_hours/binary>>/binary,
":"/utf8>>/binary,
Offset_minutes/binary>>.
-file("src/gcourier/message.gleam", 342).
-spec current_date() -> binary().
current_date() ->
Now = begin
_pipe = gleam@time@timestamp:system_time(),
gleam@time@timestamp:to_calendar(_pipe, gleam@time@duration:seconds(0))
end,
date_from_cal(erlang:element(1, Now), erlang:element(2, Now)).
-file("src/gcourier/message.gleam", 108).
-spec get_headers(message()) -> list({binary(), binary()}).
get_headers(Message) ->
Get = fun(Name) ->
gleam_stdlib:map_get(erlang:element(2, Message), Name)
end,
Optional = fun(Name@1) -> case Get(Name@1) of
{ok, Val} when Val =/= <<""/utf8>> ->
{some, {Name@1, Val}};
_ ->
none
end end,
Required = fun(Name@2, Default) -> case Get(Name@2) of
{ok, Val@1} when Val@1 =/= <<""/utf8>> ->
{some, {Name@2, Val@1}};
_ ->
{some, {Name@2, Default}}
end end,
Mandatory = fun(Name@3) -> case Get(Name@3) of
{ok, Val@2} when Val@2 =/= <<""/utf8>> ->
{some, {Name@3, Val@2}};
_ ->
erlang:error(#{gleam_error => panic,
message => (<<"Missing required header: "/utf8,
Name@3/binary>>),
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 129})
end end,
Recipient_field = fun(Message@1, Recipient_type) -> case Recipient_type of
c_c ->
case erlang:element(4, Message@1) of
[] ->
none;
_ ->
{some,
{<<"Cc"/utf8>>,
gleam@string:join(
erlang:element(4, Message@1),
<<", "/utf8>>
)}}
end;
to ->
case erlang:element(3, Message@1) of
[] ->
erlang:error(#{gleam_error => panic,
message => <<"Missing to field"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 143});
_ ->
{some,
{<<"To"/utf8>>,
gleam@string:join(
erlang:element(3, Message@1),
<<", "/utf8>>
)}}
end;
_ ->
none
end end,
_pipe = gleam@list:filter(
[Required(<<"Date"/utf8>>, current_date()),
Mandatory(<<"From"/utf8>>),
Recipient_field(Message, to),
Optional(<<"Sender"/utf8>>),
Recipient_field(Message, c_c),
Optional(<<"Subject"/utf8>>),
Required(<<"Content-Type"/utf8>>, <<"text/plain"/utf8>>)],
fun(A) -> case A of
none ->
false;
{some, _} ->
true
end end
),
gleam@list:map(_pipe, fun(A@1) -> case A@1 of
{some, Field} ->
Field;
none ->
erlang:error(#{gleam_error => panic,
message => <<"`panic` expression evaluated."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 171})
end end).
-file("src/gcourier/message.gleam", 49).
-spec render_single(message()) -> binary().
render_single(Message) ->
Headers = begin
_pipe = get_headers(Message),
_pipe@1 = gleam@list:map(
_pipe,
fun(Header) ->
<<<<(erlang:element(1, Header))/binary, ": "/utf8>>/binary,
(erlang:element(2, Header))/binary>>
end
),
gleam@string:join(_pipe@1, <<"\r\n"/utf8>>)
end,
<<<<<<Headers/binary, "\r\n\r\n"/utf8>>/binary,
(erlang:element(6, Message))/binary>>/binary,
"\r\n."/utf8>>.
-file("src/gcourier/message.gleam", 58).
-spec render_multipart(message()) -> binary().
render_multipart(Message) ->
Boundary = youid@uuid:v4_string(),
Body_ctype@1 = case gleam_stdlib:map_get(
erlang:element(2, Message),
<<"Content-Type"/utf8>>
) of
{ok, Body_ctype} -> Body_ctype;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"render_multipart"/utf8>>,
line => 60,
value => _assert_fail,
start => 1240,
'end' => 1309,
pattern_start => 1251,
pattern_end => 1265})
end,
Message@1 = begin
_pipe = Message,
set_header(
_pipe,
<<"Content-Type"/utf8>>,
<<<<"multipart/mixed; boundary=\""/utf8, Boundary/binary>>/binary,
"\""/utf8>>
)
end,
Attachments@1 = case erlang:element(7, Message@1) of
{some, Attachments} -> Attachments;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gcourier/message"/utf8>>,
function => <<"render_multipart"/utf8>>,
line => 68,
value => _assert_fail@1,
start => 1446,
'end' => 1496,
pattern_start => 1457,
pattern_end => 1474})
end,
Content = <<<<<<<<<<<<<<<<<<<<"--"/utf8, Boundary/binary>>/binary,
"\r\nContent-Type: "/utf8>>/binary,
Body_ctype@1/binary>>/binary,
"\r\n\r\n"/utf8>>/binary,
(erlang:element(6, Message@1))/binary>>/binary,
"\r\n"/utf8>>/binary,
(begin
_pipe@1 = gleam@list:map(
lists:reverse(Attachments@1),
fun(A) -> render_attachment(Boundary, A) end
),
gleam@string:join(_pipe@1, <<"\r\n"/utf8>>)
end)/binary>>/binary,
"\r\n--"/utf8>>/binary,
Boundary/binary>>/binary,
"--\r\n"/utf8>>,
Headers = begin
_pipe@2 = get_headers(Message@1),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Header) ->
<<<<(erlang:element(1, Header))/binary, ": "/utf8>>/binary,
(erlang:element(2, Header))/binary>>
end
),
gleam@string:join(_pipe@3, <<"\r\n"/utf8>>)
end,
<<<<<<Headers/binary, "\r\n"/utf8>>/binary, Content/binary>>/binary,
"\r\n."/utf8>>.
-file("src/gcourier/message.gleam", 41).
-spec render(message()) -> binary().
render(Message) ->
case erlang:element(7, Message) of
{some, _} ->
render_multipart(Message);
none ->
render_single(Message)
end.