Files
mu4e/scm/mu-scm-message.cc
Dirk-Jan C. Binnema ca46c09ccb mu-scm: add full-message support (body/header)
Implement support for "header" and "body" procedures, with require loading the
message file from disk, and create a foreign object for the message.

We keep those alive in a vector, and hook up a finalizer.

Update docs & tests as well.
2025-06-30 21:57:29 +03:00

146 lines
4.1 KiB
C++

/*
** Copyright (C) 2025 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
** Free Software Foundation; either version 3, or (at your option) any
** later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/
#include "mu-scm-types.hh"
#include "message/mu-message.hh"
#include <mutex>
using namespace Mu;
using namespace Mu::Scm;
namespace {
static SCM message_type;
static bool initialized;
std::mutex map_lock;
constexpr auto max_message_map_size{512};
struct MessageObject {
const Message message;
SCM foreign_object{};
};
using MessageMap = std::unordered_map<std::string, MessageObject>;
static MessageMap message_map;
}
static const Message&
to_message(SCM scm)
{
scm_assert_foreign_object_type(message_type, scm);
return *reinterpret_cast<Message*>(scm_foreign_object_ref(scm, 0));
}
static void
finalize_message(SCM scm)
{
std::unique_lock lock{map_lock};
const auto msg = reinterpret_cast<const Message*>(scm_foreign_object_ref(scm, 0));
//mu_debug("finalizing message @ {}", msg->path());
if (const auto n = message_map.erase(msg->path()); n != 1)
mu_warning("huh?! deleted {}", n);
}
static SCM
subr_message_object_make(SCM message_path_scm)
{
// message objects eat fds, tickle the gc... letting it handle it
// automatically is not soon enough.
if (message_map.size() >= 0.8 * max_message_map_size)
scm_gc();
std::unique_lock lock{map_lock};
// qttempt to give an good error message rather then getting something
// from GMime)
if (message_map.size() >= max_message_map_size)
raise_error("failure", "message-object",
"too many open messages");
// if we already have the message in our map, return it.
auto path{from_scm<std::string>(message_path_scm)};
if (const auto it = message_map.find(path); it != message_map.end())
return it->second.foreign_object;
// don't have it yet; attempt to create one
if (auto res{Message::make_from_path(path)}; !res) {
raise_error("failure", "message-object",
"failed to create message from {}: {}", path, res.error());
return SCM_BOOL_F;
} else {
// create a new object, store it in our map and return the foreign ptr.
auto it = message_map.emplace(std::move(path), std::move(*res));
return it.first->second.foreign_object = scm_make_foreign_object_1(
message_type, const_cast<Message*>(&it.first->second.message));
}
}
static SCM
subr_message_body(SCM message_scm, SCM html_scm)
{
const auto& message{to_message(message_scm)};
const auto html{from_scm<bool>(html_scm)};
if (const auto body{html ? message.body_html() : message.body_text()}; body)
return to_scm(*body);
else
return SCM_BOOL_F;
}
static SCM
subr_message_header(SCM message_scm, SCM field_scm)
{
const auto& message{to_message(message_scm)};
const auto field{from_scm<std::string>(field_scm)};
if (const auto val{message.header(field)}; val)
return to_scm(*val);
else
return SCM_BOOL_F;
}
static void
init_subrs()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
scm_c_define_gsubr("message-object-make", 1/*req*/, 0/*opt*/, 0/*rst*/,
reinterpret_cast<scm_t_subr>(subr_message_object_make));
scm_c_define_gsubr("message-body", 2/*req*/, 0/*opt*/, 0/*rst*/,
reinterpret_cast<scm_t_subr>(subr_message_body));
scm_c_define_gsubr("message-header",2/*req*/, 0/*opt*/, 0/*rst*/,
reinterpret_cast<scm_t_subr>(subr_message_header));
#pragma GCC diagnostic pop
}
void
Mu::Scm::init_message()
{
if (initialized)
return;
message_type = scm_make_foreign_object_type(
make_symbol("message"),
scm_list_1(make_symbol("data")),
finalize_message);
init_subrs();
initialized = true;
}