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]).
-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 | cc | bcc.
-file("src/gcourier/message.gleam", 37).
-spec build() -> message().
build() ->
{message, maps:from_list([]), [], [], [], <<""/utf8>>, none}.
-file("src/gcourier/message.gleam", 94).
-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", 189).
?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 ->
_record = Message,
{message,
erlang:element(2, _record),
[Email | erlang:element(3, Message)],
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record)};
cc ->
_record@1 = Message,
{message,
erlang:element(2, _record@1),
erlang:element(3, _record@1),
[Email | erlang:element(4, Message)],
erlang:element(5, _record@1),
erlang:element(6, _record@1),
erlang:element(7, _record@1)};
bcc ->
_record@2 = Message,
{message,
erlang:element(2, _record@2),
erlang:element(3, _record@2),
erlang:element(4, _record@2),
[Email | erlang:element(5, Message)],
erlang:element(6, _record@2),
erlang:element(7, _record@2)}
end.
-file("src/gcourier/message.gleam", 240).
-spec add_attachment(message(), binary(), binary(), binary()) -> message().
add_attachment(Message, Path, Name, Content_type) ->
_assert_subject = simplifile_erl:read_bits(Path),
{ok, Content} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"gcourier/message"/utf8>>,
function => <<"add_attachment"/utf8>>,
line => 246})
end,
Content@1 = gleam_stdlib:bit_array_base64_encode(Content, false),
Attachment = {attachment, Name, Content_type, Content@1},
Attachments = case erlang:element(7, Message) of
none ->
[Attachment];
{some, A} ->
[Attachment | A]
end,
_record = Message,
{message,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
{some, Attachments}}.
-file("src/gcourier/message.gleam", 257).
-spec set_content(message(), binary()) -> message().
set_content(Message, Text) ->
_record = Message,
{message,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
Text,
erlang:element(7, _record)}.
-file("src/gcourier/message.gleam", 263).
-spec set_header(message(), binary(), binary()) -> message().
set_header(Message, Name, Value) ->
_record = Message,
{message,
gleam@dict:insert(erlang:element(2, Message), Name, Value),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record)}.
-file("src/gcourier/message.gleam", 202).
?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", 222).
?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", 228).
-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", 234).
-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", 267).
-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", 178).
?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", 210).
?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", 314).
?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>>,
module => <<"gcourier/message"/utf8>>,
function => <<"day_of_week"/utf8>>,
line => 337})
end.
-file("src/gcourier/message.gleam", 277).
?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", 341).
-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", 107).
-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>>),
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 128})
end end,
Recipient_field = fun(Message@1, Recipient_type) -> case Recipient_type of
cc ->
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>>,
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 142});
_ ->
{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, cc),
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>>,
module => <<"gcourier/message"/utf8>>,
function => <<"get_headers"/utf8>>,
line => 170})
end end).
-file("src/gcourier/message.gleam", 48).
-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"/utf8>>/binary,
(erlang:element(6, Message))/binary>>/binary,
"\r\n."/utf8>>.
-file("src/gcourier/message.gleam", 57).
-spec render_multipart(message()) -> binary().
render_multipart(Message) ->
Boundary = youid@uuid:v4_string(),
_assert_subject = gleam_stdlib:map_get(
erlang:element(2, Message),
<<"Content-Type"/utf8>>
),
{ok, Body_ctype} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"gcourier/message"/utf8>>,
function => <<"render_multipart"/utf8>>,
line => 59})
end,
Message@1 = begin
_pipe = Message,
set_header(
_pipe,
<<"Content-Type"/utf8>>,
<<<<"multipart/mixed; boundary=\""/utf8, Boundary/binary>>/binary,
"\""/utf8>>
)
end,
_assert_subject@1 = erlang:element(7, Message@1),
{some, Attachments} = case _assert_subject@1 of
{some, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail@1,
module => <<"gcourier/message"/utf8>>,
function => <<"render_multipart"/utf8>>,
line => 67})
end,
Content = <<<<<<<<<<<<<<<<<<<<"--"/utf8, Boundary/binary>>/binary,
"\r\nContent-Type: "/utf8>>/binary,
Body_ctype/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),
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.