Skip to main content

src/sendr@message@attachment.erl

-module(sendr@message@attachment).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sendr/message/attachment.gleam").
-export([set_filename_utf8/3, set_filename/3, from_data/2, from_file/1, set_content_id/2, set_content_type/2]).
-export_type([attachment/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 a lax representation of an attachment as defined in\n"
    " [RFC 2045](https://tools.ietf.org/html/rfc2045).\n"
    "\n"
    " It does not enforce any of the rules and requirements of the RFC. Since we\n"
    " do not know what transformations the Mail Submission Agent will perform and\n"
    " what the Mail Submission Agent is capable of handling.\n"
).

-type attachment() :: {attached_attachment,
        binary(),
        binary(),
        binary(),
        bitstring()} |
    {inlined_attachment, binary(), binary(), binary(), bitstring(), binary()}.

-file("src/sendr/message/attachment.gleam", 165).
-spec guess_content_type(binary()) -> binary().
guess_content_type(Path) ->
    _pipe = filepath:extension(Path),
    _pipe@1 = gleam@result:map(_pipe, fun marceau:extension_to_mime_type/1),
    gleam@result:unwrap(_pipe@1, <<"application/octet-stream"/utf8>>).

-file("src/sendr/message/attachment.gleam", 145).
?DOC(
    " Set the `filename_utf8` on the attachment (for UTF-8 filenames).\n"
    " Optionally also updates the content type.\n"
    "\n"
    " The filename is used to suggest a name for the attachment when it is saved\n"
    " by the recipient. This is particularly useful for attachments that are\n"
    " intended to be downloaded or saved.\n"
).
-spec set_filename_utf8(attachment(), binary(), boolean()) -> attachment().
set_filename_utf8(Attachment, Path, Update) ->
    Filename_utf8 = filepath:base_name(Path),
    Content_type = case Update of
        false ->
            erlang:element(2, Attachment);

        true ->
            guess_content_type(Filename_utf8)
    end,
    case Attachment of
        {attached_attachment, _, _, _, _} ->
            {attached_attachment,
                Content_type,
                erlang:element(3, Attachment),
                Filename_utf8,
                erlang:element(5, Attachment)};

        {inlined_attachment, _, _, _, _, _} ->
            {inlined_attachment,
                Content_type,
                erlang:element(3, Attachment),
                Filename_utf8,
                erlang:element(5, Attachment),
                erlang:element(6, Attachment)}
    end.

-file("src/sendr/message/attachment.gleam", 119).
?DOC(
    " Set the `filename` on the attachment (for ASCII filenames).\n"
    " Optionally also updates the content type.\n"
    "\n"
    " The filename is used to suggest a name for the attachment when it is saved\n"
    " by the recipient. This is particularly useful for attachments that are\n"
    " intended to be downloaded or saved.\n"
).
-spec set_filename(attachment(), binary(), boolean()) -> attachment().
set_filename(Attachment, Path, Update) ->
    Filename = filepath:base_name(Path),
    Content_type = case Update of
        false ->
            erlang:element(2, Attachment);

        true ->
            guess_content_type(Filename)
    end,
    case Attachment of
        {attached_attachment, _, _, _, _} ->
            {attached_attachment,
                Content_type,
                Filename,
                erlang:element(4, Attachment),
                erlang:element(5, Attachment)};

        {inlined_attachment, _, _, _, _, _} ->
            {inlined_attachment,
                Content_type,
                Filename,
                erlang:element(4, Attachment),
                erlang:element(5, Attachment),
                erlang:element(6, Attachment)}
    end.

-file("src/sendr/message/attachment.gleam", 171).
-spec is_ascii_only(binary()) -> boolean().
is_ascii_only(Value) ->
    _pipe = Value,
    _pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
    gleam@list:all(
        _pipe@1,
        fun(Char) -> gleam@string:compare(Char, <<"\x{80}"/utf8>>) =:= lt end
    ).

-file("src/sendr/message/attachment.gleam", 41).
?DOC(
    " Create a new attachment from raw data.\n"
    "\n"
    " This function allows creating an attachment directly from binary data. It is\n"
    " useful when the attachment data is already available in memory.\n"
).
-spec from_data(binary(), bitstring()) -> attachment().
from_data(Path, Data) ->
    Attachment = {attached_attachment,
        <<""/utf8>>,
        <<""/utf8>>,
        <<""/utf8>>,
        Data},
    Filename = filepath:base_name(Path),
    case is_ascii_only(Filename) of
        true ->
            set_filename(Attachment, Filename, true);

        false ->
            set_filename_utf8(Attachment, Filename, true)
    end.

-file("src/sendr/message/attachment.gleam", 64).
?DOC(
    " Create a new attachment from a file.\n"
    "\n"
    " This function reads the file, determines its content type based on its\n"
    " extension, and creates an attachment with the file's data. If the content\n"
    " type cannot be determined, it defaults to \"application/octet-stream\".\n"
).
-spec from_file(binary()) -> {ok, attachment()} |
    {error, simplifile:file_error()}.
from_file(Path) ->
    _pipe = simplifile_erl:read_bits(Path),
    gleam@result:map(_pipe, fun(_capture) -> from_data(Path, _capture) end).

-file("src/sendr/message/attachment.gleam", 74).
?DOC(
    " Set the `content-id` on the attachment.\n"
    "\n"
    " The content ID is used to uniquely identify the attachment within the\n"
    " message. This is particularly useful for referencing the attachment in the\n"
    " message body.\n"
).
-spec set_content_id(attachment(), binary()) -> attachment().
set_content_id(Attachment, Content_id) ->
    case Content_id of
        <<""/utf8>> ->
            {attached_attachment,
                erlang:element(2, Attachment),
                erlang:element(3, Attachment),
                erlang:element(4, Attachment),
                erlang:element(5, Attachment)};

        _ ->
            {inlined_attachment,
                erlang:element(2, Attachment),
                erlang:element(3, Attachment),
                erlang:element(4, Attachment),
                erlang:element(5, Attachment),
                Content_id}
    end.

-file("src/sendr/message/attachment.gleam", 101).
?DOC(
    " Set the `content-type` on the attachment.\n"
    "\n"
    " The content type specifies the type of data contained in the attachment.\n"
    " This helps the recipient's system to handle the attachment appropriately.\n"
).
-spec set_content_type(attachment(), binary()) -> attachment().
set_content_type(Attachment, Content_type) ->
    case Attachment of
        {attached_attachment, _, _, _, _} ->
            {attached_attachment,
                gleam@string:trim(Content_type),
                erlang:element(3, Attachment),
                erlang:element(4, Attachment),
                erlang:element(5, Attachment)};

        {inlined_attachment, _, _, _, _, _} ->
            {inlined_attachment,
                gleam@string:trim(Content_type),
                erlang:element(3, Attachment),
                erlang:element(4, Attachment),
                erlang:element(5, Attachment),
                erlang:element(6, Attachment)}
    end.