-module(sendr_sweego).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sendr_sweego.gleam").
-export([config/1, dry_run/1, request/2, response/1]).
-export_type([sweego_error/0, api_error_detail/0, sweego_config/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(
" sendr_sweego — Sweego.io email backend for sendr.\n"
"\n"
" Builds HTTP `Request` values for the Sweego send API and parses\n"
" `Response` values returned by it.\n"
).
-type sweego_error() :: {invalid_uri, binary()} |
{invalid_response, integer(), gleam@json:decode_error()} |
{api_error, integer(), list(api_error_detail())}.
-type api_error_detail() :: {api_error_detail, binary(), binary()}.
-opaque sweego_config() :: {sweego_config, binary(), boolean()}.
-file("src/sendr_sweego.gleam", 52).
?DOC(" Create a new `SweegoConfig` with the given API key.\n").
-spec config(binary()) -> sweego_config().
config(Api_key) ->
{sweego_config, Api_key, false}.
-file("src/sendr_sweego.gleam", 59).
?DOC(
" Enable dry-run mode.\n"
"\n"
" When enabled, the email will not be delivered (test mode).\n"
).
-spec dry_run(sweego_config()) -> sweego_config().
dry_run(Config) ->
{sweego_config, erlang:element(2, Config), true}.
-file("src/sendr_sweego.gleam", 291).
-spec add(list({binary(), gleam@json:json()}), binary(), gleam@json:json()) -> list({binary(),
gleam@json:json()}).
add(Entries, Key, Value) ->
lists:append(Entries, [{Key, Value}]).
-file("src/sendr_sweego.gleam", 299).
-spec add_if_some(
list({binary(), gleam@json:json()}),
binary(),
gleam@option:option(gleam@json:json())
) -> list({binary(), gleam@json:json()}).
add_if_some(Entries, Key, Value) ->
case Value of
{some, Value@1} ->
add(Entries, Key, Value@1);
none ->
Entries
end.
-file("src/sendr_sweego.gleam", 265).
-spec attachment_to_json(sendr@message@attachment:attachment()) -> gleam@json:json().
attachment_to_json(Attachment) ->
Filename@1 = case erlang:element(3, Attachment) of
<<""/utf8>> ->
erlang:element(4, Attachment);
Filename ->
Filename
end,
{Disposition, Content_id@1, Is_related} = case Attachment of
{attached_attachment, _, _, _, _} ->
{<<"attachment"/utf8>>, none, none};
{inlined_attachment, _, _, _, _, Content_id} ->
{<<"inline"/utf8>>, {some, Content_id}, {some, true}}
end,
Content = gleam_stdlib:base64_encode(erlang:element(5, Attachment), true),
gleam@json:object(
begin
_pipe = [{<<"filename"/utf8>>, gleam@json:string(Filename@1)},
{<<"disposition"/utf8>>, gleam@json:string(Disposition)},
{<<"content"/utf8>>, gleam@json:string(Content)}],
_pipe@1 = add_if_some(
_pipe,
<<"content_id"/utf8>>,
gleam@option:map(Content_id@1, fun gleam@json:string/1)
),
add_if_some(
_pipe@1,
<<"is_related"/utf8>>,
gleam@option:map(Is_related, fun gleam@json:bool/1)
)
end
).
-file("src/sendr_sweego.gleam", 255).
-spec mailbox_to_json(sendr@message@mailbox:mailbox()) -> gleam@json:json().
mailbox_to_json(Mailbox) ->
gleam@json:object(
begin
_pipe = [{<<"email"/utf8>>,
gleam@json:string(erlang:element(3, Mailbox))}],
add_if_some(
_pipe,
<<"name"/utf8>>,
begin
_pipe@1 = erlang:element(2, Mailbox),
_pipe@2 = gleam@string:to_option(_pipe@1),
gleam@option:map(_pipe@2, fun gleam@json:string/1)
end
)
end
).
-file("src/sendr_sweego.gleam", 220).
-spec build_body(sendr@message:message(), boolean()) -> gleam@json:json().
build_body(Message, Dry_run) ->
_pipe = [{<<"provider"/utf8>>, gleam@json:string(<<"sweego"/utf8>>)}],
_pipe@1 = add_if_some(_pipe, <<"dry-run"/utf8>>, case Dry_run of
true ->
{some, gleam@json:bool(true)};
false ->
none
end),
_pipe@2 = add_if_some(
_pipe@1,
<<"from"/utf8>>,
gleam@option:map(erlang:element(2, Message), fun mailbox_to_json/1)
),
_pipe@6 = add_if_some(
_pipe@2,
<<"reply-to"/utf8>>,
gleam@option:then(
erlang:element(3, Message),
fun(Reply_to) -> _pipe@3 = Reply_to,
_pipe@4 = gleam@list:first(_pipe@3),
_pipe@5 = gleam@option:from_result(_pipe@4),
gleam@option:map(_pipe@5, fun mailbox_to_json/1) end
)
),
_pipe@7 = add_if_some(
_pipe@6,
<<"recipients"/utf8>>,
gleam@option:map(
erlang:element(4, Message),
fun(_capture) ->
gleam@json:array(_capture, fun mailbox_to_json/1)
end
)
),
_pipe@8 = add_if_some(
_pipe@7,
<<"cc"/utf8>>,
gleam@option:map(
erlang:element(5, Message),
fun(_capture@1) ->
gleam@json:array(_capture@1, fun mailbox_to_json/1)
end
)
),
_pipe@9 = add_if_some(
_pipe@8,
<<"bcc"/utf8>>,
gleam@option:map(
erlang:element(6, Message),
fun(_capture@2) ->
gleam@json:array(_capture@2, fun mailbox_to_json/1)
end
)
),
_pipe@10 = add_if_some(
_pipe@9,
<<"subject"/utf8>>,
gleam@option:map(erlang:element(7, Message), fun gleam@json:string/1)
),
_pipe@11 = add_if_some(
_pipe@10,
<<"message-txt"/utf8>>,
gleam@option:map(
erlang:element(2, erlang:element(9, Message)),
fun gleam@json:string/1
)
),
_pipe@12 = add_if_some(
_pipe@11,
<<"message-html"/utf8>>,
gleam@option:map(
erlang:element(3, erlang:element(9, Message)),
fun gleam@json:string/1
)
),
_pipe@13 = add(
_pipe@12,
<<"attachments"/utf8>>,
gleam@json:array(erlang:element(8, Message), fun attachment_to_json/1)
),
gleam@json:object(_pipe@13).
-file("src/sendr_sweego.gleam", 213).
-spec validate_body(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_body(Message) ->
case {erlang:element(2, erlang:element(9, Message)),
erlang:element(3, erlang:element(9, Message))} of
{none, none} ->
{error, {invalid_body, no_body}};
{_, _} ->
{ok, nil}
end.
-file("src/sendr_sweego.gleam", 199).
-spec validate_attachments(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_attachments(Message) ->
gleam@list:try_each(
erlang:element(8, Message),
fun(Attachment) ->
case {erlang:element(3, Attachment), erlang:element(4, Attachment)} of
{<<""/utf8>>, <<""/utf8>>} ->
{error,
{invalid_attachment,
required_filename_missing,
Attachment}};
{_, _} ->
case Attachment of
{inlined_attachment, _, _, _, _, <<""/utf8>>} ->
{error,
{invalid_attachment,
required_content_id_missing,
Attachment}};
_ ->
{ok, nil}
end
end
end
).
-file("src/sendr_sweego.gleam", 192).
-spec validate_subject(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_subject(Message) ->
case erlang:element(7, Message) of
none ->
{error, {required_field_missing, subject}};
{some, <<""/utf8>>} ->
{error, {required_field_missing, subject}};
_ ->
{ok, nil}
end.
-file("src/sendr_sweego.gleam", 182).
-spec validate_mailbox(sendr@message@mailbox:mailbox(), sendr:field()) -> {ok,
nil} |
{error, sendr:sendr_error(any())}.
validate_mailbox(Mailbox, Field) ->
case gleam@string:split_once(erlang:element(3, Mailbox), <<"@"/utf8>>) of
{ok, {Local, Domain}} when (Local =/= <<""/utf8>>) andalso (Domain =/= <<""/utf8>>) ->
{ok, nil};
_ ->
{error, {invalid_mailbox, Field, Mailbox}}
end.
-file("src/sendr_sweego.gleam", 161).
-spec validate_recipients(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_recipients(Message) ->
Recipients = begin
_pipe = [erlang:element(4, Message),
erlang:element(5, Message),
erlang:element(6, Message)],
_pipe@1 = gleam@option:values(_pipe),
lists:append(_pipe@1)
end,
Validate = fun(Mailboxes, Field) -> _pipe@2 = Mailboxes,
_pipe@3 = gleam@option:unwrap(_pipe@2, []),
gleam@list:try_each(
_pipe@3,
fun(_capture) -> validate_mailbox(_capture, Field) end
) end,
case Recipients of
[] ->
{error, no_recipients};
_ ->
_pipe@4 = Validate(erlang:element(6, Message), bcc),
_pipe@5 = gleam@result:'or'(
_pipe@4,
Validate(erlang:element(5, Message), cc)
),
gleam@result:'or'(_pipe@5, Validate(erlang:element(4, Message), to))
end.
-file("src/sendr_sweego.gleam", 153).
-spec validate_reply_to(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_reply_to(Message) ->
case erlang:element(3, Message) of
none ->
{ok, nil};
{some, []} ->
{ok, nil};
{some, [Mailbox]} ->
validate_mailbox(Mailbox, reply_to);
{some, _} ->
{error, {too_many_entries, reply_to}}
end.
-file("src/sendr_sweego.gleam", 146).
-spec validate_from(sendr@message:message()) -> {ok, nil} |
{error, sendr:sendr_error(any())}.
validate_from(Message) ->
case erlang:element(2, Message) of
none ->
{error, {required_field_missing, from}};
{some, Mailbox} ->
validate_mailbox(Mailbox, from)
end.
-file("src/sendr_sweego.gleam", 67).
?DOC(
" Build an HTTP `Request` for the Sweego send API from a sendr `Message`.\n"
"\n"
" Validates the message (from, reply-to, recipients, subject, attachments)\n"
" and returns `Error(SendrError(SweegoError))` on validation failure.\n"
).
-spec request(sendr@message:message(), sweego_config()) -> {ok,
gleam@http@request:request(binary())} |
{error, sendr:sendr_error(sweego_error())}.
request(Message, Config) ->
gleam@result:'try'(
validate_from(Message),
fun(_) ->
gleam@result:'try'(
validate_reply_to(Message),
fun(_) ->
gleam@result:'try'(
validate_recipients(Message),
fun(_) ->
gleam@result:'try'(
validate_subject(Message),
fun(_) ->
gleam@result:'try'(
validate_attachments(Message),
fun(_) ->
gleam@result:'try'(
validate_body(Message),
fun(_) ->
Body = build_body(
Message,
erlang:element(
3,
Config
)
),
_pipe = <<"https://api.sweego.io/send"/utf8>>,
_pipe@1 = gleam_stdlib:uri_parse(
_pipe
),
_pipe@2 = gleam@result:'try'(
_pipe@1,
fun gleam@http@request:from_uri/1
),
_pipe@3 = gleam@result:replace_error(
_pipe@2,
{invalid_uri,
<<"https://api.sweego.io/send"/utf8>>}
),
_pipe@8 = gleam@result:map(
_pipe@3,
fun(Request) ->
_pipe@4 = Request,
_pipe@5 = gleam@http@request:set_method(
_pipe@4,
post
),
_pipe@6 = gleam@http@request:set_header(
_pipe@5,
<<"content-type"/utf8>>,
<<"application/json"/utf8>>
),
_pipe@7 = gleam@http@request:set_header(
_pipe@6,
<<"api-key"/utf8>>,
erlang:element(
2,
Config
)
),
gleam@http@request:set_body(
_pipe@7,
gleam@json:to_string(
Body
)
)
end
),
gleam@result:map_error(
_pipe@8,
fun(Field@0) -> {backend_error, Field@0} end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/sendr_sweego.gleam", 99).
?DOC(
" Parse a `Response` from the Sweego send API.\n"
"\n"
" On HTTP 200 the response body is decoded and the single `swg_uid` value\n"
" is returned. On HTTP 422 the `detail` array is decoded into\n"
" `ApiErrorDetail` entries. All other statuses are treated as generic\n"
" API errors.\n"
).
-spec response(gleam@http@response:response(binary())) -> {ok, binary()} |
{error, sendr:sendr_error(sweego_error())}.
response(Response) ->
case erlang:element(2, Response) of
200 = Status ->
Success_decoder = begin
gleam@dynamic@decode:field(
<<"transaction_id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) -> gleam@dynamic@decode:success(Id) end
)
end,
_pipe = gleam@json:parse(
erlang:element(4, Response),
Success_decoder
),
gleam@result:map_error(
_pipe,
fun(Error) ->
{backend_error, {invalid_response, Status, Error}}
end
);
422 = Status@1 ->
Error_decoder = begin
gleam@dynamic@decode:field(
<<"detail"/utf8>>,
gleam@dynamic@decode:list(
begin
gleam@dynamic@decode:field(
<<"msg"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Msg) ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Typ) ->
gleam@dynamic@decode:success(
{api_error_detail, Msg, Typ}
)
end
)
end
)
end
),
fun(Details) -> gleam@dynamic@decode:success(Details) end
)
end,
case gleam@json:parse(erlang:element(4, Response), Error_decoder) of
{ok, Details@1} ->
{error, {backend_error, {api_error, Status@1, Details@1}}};
{error, Error@1} ->
{error,
{backend_error, {invalid_response, Status@1, Error@1}}}
end;
Status@2 ->
Error_decoder@1 = begin
gleam@dynamic@decode:field(
<<"detail"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Msg@1) ->
gleam@dynamic@decode:success(
[{api_error_detail,
<<"Failed invocation"/utf8>>,
Msg@1}]
)
end
)
end,
case gleam@json:parse(erlang:element(4, Response), Error_decoder@1) of
{ok, Details@2} ->
{error, {backend_error, {api_error, Status@2, Details@2}}};
{error, Error@2} ->
{error,
{backend_error, {invalid_response, Status@2, Error@2}}}
end
end.