lib: replace CATCH_BLOCK macros with template magic

This commit is contained in:
Dirk-Jan C. Binnema 2021-10-18 12:22:26 +03:00
parent 7156ff7fac
commit 49637dbc3a
6 changed files with 172 additions and 223 deletions

View File

@ -46,25 +46,22 @@ MuMsgDoc*
Mu::mu_msg_doc_new (XapianDocument *doc, GError **err)
{
g_return_val_if_fail (doc, NULL);
try {
MuMsgDoc *mdoc = xapian_try([&]{
return new MuMsgDoc ((Xapian::Document*)doc);
}, (MuMsgDoc*)nullptr);
} MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, NULL);
return FALSE;
if (!mdoc)
mu_util_g_set_error (err, MU_ERROR_INTERNAL,
"failed to create message doc");
return mdoc;
}
void
Mu::mu_msg_doc_destroy (MuMsgDoc *self)
{
try {
delete self;
} MU_XAPIAN_CATCH_BLOCK;
xapian_try([&]{delete self;});
}
gchar*
Mu::mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid)
{
@ -78,14 +75,12 @@ Mu::mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid)
// have to convert to numbers first, esp. when it's a date
// time_t)
try {
return xapian_try([&]{
const std::string s (self->doc().get_value(mfid));
return s.empty() ? NULL : g_strdup (s.c_str());
} MU_XAPIAN_CATCH_BLOCK_RETURN(NULL);
},(gchar*)nullptr);
}
GSList*
Mu::mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid)
{
@ -93,12 +88,11 @@ Mu::mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid)
g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL);
g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL);
try {
return xapian_try([&]{
/* return a comma-separated string as a GSList */
const std::string s (self->doc().get_value(mfid));
return s.empty() ? NULL : mu_str_to_list(s.c_str(),',',TRUE);
} MU_XAPIAN_CATCH_BLOCK_RETURN(NULL);
},(GSList*)nullptr);
}
@ -109,17 +103,16 @@ Mu::mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid)
g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), -1);
g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1);
try {
return xapian_try([&]{
const std::string s (self->doc().get_value(mfid));
if (s.empty())
return 0;
return (gint64)0;
else if (mfid == MU_MSG_FIELD_ID_DATE ||
mfid == MU_MSG_FIELD_ID_SIZE)
return strtol (s.c_str(), NULL, 10);
return static_cast<gint64>(strtol (s.c_str(), NULL, 10));
else {
return static_cast<gint64>
(Xapian::sortable_unserialise(s));
}
} MU_XAPIAN_CATCH_BLOCK_RETURN(-1);
}, (gint64)-1);
}

View File

@ -98,12 +98,14 @@ struct MatchDecider : public Xapian::MatchDecider {
DeciderInfo & decider_info_;
private:
Option<std::string> opt_string (const Xapian::Document &doc, MuMsgFieldId id) const noexcept
try {
auto &&val{doc.get_value (id)};
return val.empty() ? Nothing : Some (val);
}
MU_XAPIAN_CATCH_BLOCK_RETURN (Nothing);
Option<std::string> opt_string (const Xapian::Document &doc, MuMsgFieldId id)
const noexcept {
std::string val = xapian_try([&]{ return doc.get_value (id);}, std::string{""});
if (val.empty())
return Nothing;
else
return Some(std::move(val));
}
};
struct MatchDeciderLeader final : public MatchDecider {

View File

@ -280,12 +280,14 @@ class QueryResultsIterator
*
* @return the value
*/
Option<std::string> opt_string (MuMsgFieldId id) const noexcept
try {
auto &&val{document().get_value (id)};
return val.empty() ? Nothing : Some (val);
}
MU_XAPIAN_CATCH_BLOCK_RETURN (Nothing);
Option<std::string> opt_string (MuMsgFieldId id) const noexcept {
std::string empty;
std::string val = xapian_try([&]{ return document().get_value (id);}, empty);
if (val.empty())
return Nothing;
else
return Some(std::move(val));
}
/**
* Get the Query match info for this message.
@ -306,27 +308,27 @@ class QueryResultsIterator
/**
* get the corresponding MuMsg for this iter; this instance is owned by
* @this, and becomes invalid when iterating to the next, or @this is
k * destroyed.; it's a 'floating' reference.
* destroyed.; it's a 'floating' reference.
*
* @return a MuMsg* or NUL in case of error
*/
MuMsg *floating_msg() G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT
try {
auto docp{reinterpret_cast<XapianDocument *> (new Xapian::Document (document()))};
GError *err{};
g_clear_pointer (&msg_, mu_msg_unref);
if (!(msg_ = mu_msg_new_from_doc (docp, &err))) {
delete docp;
g_warning ("failed to crate message for %s: %s",
path().value_or ("<none>").c_str(),
err ? err->message : "somethng went wrong");
g_clear_error (&err);
}
return msg_;
}
MU_XAPIAN_CATCH_BLOCK_RETURN (NULL);
MuMsg *floating_msg() G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT {
return xapian_try([&]{
auto docp{reinterpret_cast<XapianDocument *> (
new Xapian::Document (document()))};
GError *err{};
g_clear_pointer (&msg_, mu_msg_unref);
if (!(msg_ = mu_msg_new_from_doc (docp, &err))) {
delete docp;
g_warning ("failed to crate message for %s: %s",
path().value_or ("<none>").c_str(),
err ? err->message : "somethng went wrong");
g_clear_error (&err);
}
return msg_;
}, (MuMsg*)NULL);
}
private:
Xapian::MSetIterator mset_it_;
QueryMatches & query_matches_;

View File

@ -174,10 +174,14 @@ Query::Private::run_singular (const std::string& expr, MuMsgFieldId sortfieldid,
}
static Option<std::string>
opt_string(const Xapian::Document& doc, MuMsgFieldId id) noexcept try {
auto&& val{doc.get_value(id)};
return val.empty() ? Nothing : Some(val);
} MU_XAPIAN_CATCH_BLOCK_RETURN (Nothing);
opt_string (const Xapian::Document &doc, MuMsgFieldId id) noexcept
{
std::string val = xapian_try([&]{ return doc.get_value (id);}, std::string{""});
if (val.empty())
return Nothing;
else
return Some(std::move(val));
}
Option<QueryResults>
Query::Private::run_related (const std::string& expr, MuMsgFieldId sortfieldid,
@ -259,15 +263,15 @@ Query::run (const std::string& expr, MuMsgFieldId sortfieldid,
size_t
Query::count (const std::string& expr) const try
Query::count (const std::string& expr) const
{
const auto enq{priv_->make_enquire(expr, MU_MSG_FIELD_ID_NONE, {})};
auto mset{enq.get_mset(0, priv_->store_.size())};
mset.fetch();
return mset.size();
}MU_XAPIAN_CATCH_BLOCK_RETURN (0);
return xapian_try([&] {
const auto enq{priv_->make_enquire(expr, MU_MSG_FIELD_ID_NONE, {})};
auto mset{enq.get_mset(0, priv_->store_.size())};
mset.fetch();
return mset.size();
}, 0);
}

View File

@ -115,7 +115,7 @@ struct Store::Private {
contacts_{db().get_metadata(ContactsKey), mdata_.personal_addresses} {
if (!readonly)
writable_db().begin_transaction();
begin_transaction();
}
Private (const std::string& path, const std::string& root_maildir,
@ -125,7 +125,7 @@ struct Store::Private {
mdata_{init_metadata(conf, path, root_maildir, personal_addresses)},
contacts_{"", mdata_.personal_addresses} {
writable_db().begin_transaction();
begin_transaction();
}
Private (const std::string& root_maildir,
@ -136,25 +136,32 @@ struct Store::Private {
contacts_{"", mdata_.personal_addresses} {
}
~Private() try {
~Private() {
g_debug("closing store @ %s", mdata_.database_path.c_str());
if (!read_only_) {
writable_db().set_metadata (ContactsKey, contacts_.serialize());
commit();
xapian_try([&]{
writable_db().set_metadata (ContactsKey, contacts_.serialize());
commit();
});
}
} MU_XAPIAN_CATCH_BLOCK;
}
std::unique_ptr<Xapian::Database> make_xapian_db (const std::string db_path, XapianOpts opts) try {
std::unique_ptr<Xapian::Database> make_xapian_db (
const std::string db_path, XapianOpts opts) try {
in_transaction_ = false;
switch (opts) {
case XapianOpts::ReadOnly:
return std::make_unique<Xapian::Database>(db_path);
case XapianOpts::Open:
return std::make_unique<Xapian::WritableDatabase>(db_path, Xapian::DB_OPEN);
case XapianOpts::CreateOverwrite:
return std::make_unique<Xapian::WritableDatabase>(db_path, Xapian::DB_CREATE_OR_OVERWRITE);
return std::make_unique<Xapian::WritableDatabase>(
db_path, Xapian::DB_CREATE_OR_OVERWRITE);
case XapianOpts::InMemory:
return std::make_unique<Xapian::WritableDatabase>(std::string{}, Xapian::DB_BACKEND_INMEMORY);
return std::make_unique<Xapian::WritableDatabase>(
std::string{}, Xapian::DB_BACKEND_INMEMORY);
default:
throw std::logic_error ("invalid xapian options");
}
@ -176,22 +183,32 @@ struct Store::Private {
return dynamic_cast<Xapian::WritableDatabase&>(*db_.get());
}
void dirty () try {
void dirty () {
if (++dirtiness_ > mdata_.batch_size)
commit();
} MU_XAPIAN_CATCH_BLOCK;
xapian_try([this]{commit();});
}
void commit () try {
void begin_transaction() noexcept {
g_return_if_fail (!in_transaction_);
xapian_try([this]{
writable_db().begin_transaction();
in_transaction_ = true;
});
}
void commit () noexcept {
g_debug("committing %zu modification(s)", dirtiness_);
dirtiness_ = 0;
if (mdata_.in_memory)
return; // not supported in the in-memory backend.
if (in_transaction_)
writable_db().commit_transaction();
writable_db().begin_transaction();
in_transaction_ = true;
} MU_XAPIAN_CATCH_BLOCK;
xapian_try([this]{
if (in_transaction_)
writable_db().commit_transaction();
in_transaction_ = false;
begin_transaction();
});
}
void add_synonyms () {
mu_flags_foreach ((MuFlagsForeachFunc)add_synonym_for_flag,
&writable_db());
@ -250,7 +267,7 @@ struct Store::Private {
return make_metadata(path);
}
Xapian::docid add_or_update_msg (Xapian::docid docid, MuMsg *msg, GError **err);
Xapian::docid add_or_update_msg (Xapian::docid docid, MuMsg *msg);
Xapian::Document new_doc_from_message (MuMsg *msg);
const bool read_only_{};
@ -404,11 +421,11 @@ Store::add_message (const std::string& path)
throw Error{Error::Code::Message, "failed to create message: %s",
gerr ? gerr->message : "something went wrong"};
const auto docid{priv_->add_or_update_msg (0, msg, &gerr)};
const auto docid{priv_->add_or_update_msg (0, msg)};
mu_msg_unref (msg);
if (G_UNLIKELY(docid == InvalidId))
throw Error{Error::Code::Message, "failed to add message: %s",
gerr ? gerr->message : "something went wrong"};
if (G_UNLIKELY(docid == InvalidId))
throw Error{Error::Code::Message, "failed to add message"};
g_debug ("added message @ %s; docid = %u", path.c_str(), docid);
priv_->dirty();
@ -419,14 +436,12 @@ Store::add_message (const std::string& path)
bool
Store::update_message (MuMsg *msg, unsigned docid)
{
GError *gerr{};
const auto docid2{priv_->add_or_update_msg (docid, msg, &gerr)};
const auto docid2{priv_->add_or_update_msg (docid, msg)};
if (G_UNLIKELY(docid != docid2))
throw Error{Error::Code::Internal, "failed to update message",
gerr ? gerr->message : "something went wrong"};
throw Error{Error::Code::Internal, "failed to update message"};
g_debug ("updated message @ %s; docid = %u",
g_debug ("updated message @ %s; docid = %u",
mu_msg_get_path(msg), docid);
priv_->dirty();
@ -437,33 +452,29 @@ Store::update_message (MuMsg *msg, unsigned docid)
bool
Store::remove_message (const std::string& path)
{
LOCKED;
try {
return xapian_try([&]{
LOCKED;
const std::string term{(get_uid_term(path.c_str()))};
priv_->writable_db().delete_document(term);
} MU_XAPIAN_CATCH_BLOCK_RETURN (false);
g_debug ("deleted message @ %s from store", path.c_str());
priv_->dirty();
g_debug ("deleted message @ %s from store", path.c_str());
priv_->dirty();
return true;
return true;
}, false);
}
void
Store::remove_messages (const std::vector<Store::Id>& ids)
{
LOCKED;
try {
xapian_try([&]{
LOCKED;
for (auto&& id: ids) {
priv_->writable_db().delete_document(id);
priv_->dirty();
}
} MU_XAPIAN_CATCH_BLOCK;
});
}
time_t
@ -494,9 +505,8 @@ Store::set_dirstamp (const std::string& path, time_t tstamp)
MuMsg*
Store::find_message (unsigned docid) const
{
LOCKED;
try {
return xapian_try([&]{
LOCKED;
Xapian::Document *doc{new Xapian::Document{priv_->db().get_document (docid)}};
GError *gerr{};
auto msg{mu_msg_new_from_doc (reinterpret_cast<XapianDocument*>(doc), &gerr)};
@ -505,48 +515,40 @@ Store::find_message (unsigned docid) const
"something went wrong");
g_clear_error(&gerr);
}
return msg;
} MU_XAPIAN_CATCH_BLOCK_RETURN (nullptr);
}, (MuMsg*)nullptr);
}
bool
Store::contains_message (const std::string& path) const
{
LOCKED;
try {
return xapian_try([&]{
LOCKED;
const std::string term (get_uid_term(path.c_str()));
return priv_->db().term_exists (term);
} MU_XAPIAN_CATCH_BLOCK_RETURN(false);
}, false);
}
std::size_t
Store::for_each_message_path (Store::ForEachMessageFunc func) const
Store::for_each_message_path (Store::ForEachMessageFunc msg_func) const
{
LOCKED;
size_t n{};
try {
Xapian::Enquire enq{priv_->db()};
size_t n{};
xapian_try([&]{
LOCKED;
Xapian::Enquire enq{priv_->db()};
enq.set_query (Xapian::Query::MatchAll);
enq.set_cutoff (0,0);
Xapian::MSet matches(enq.get_mset (0, priv_->db().get_doccount()));
for (auto&& it = matches.begin(); it != matches.end(); ++it, ++n)
if (!func (*it, it.get_document().get_value(MU_MSG_FIELD_ID_PATH)))
if (!msg_func (*it, it.get_document().get_value(MU_MSG_FIELD_ID_PATH)))
break;
});
} MU_XAPIAN_CATCH_BLOCK;
return n;
return n;
}
static MuMsgFieldId
@ -570,36 +572,31 @@ field_id (const std::string& field)
std::size_t
Store::for_each_term (const std::string& field, Store::ForEachTermFunc func) const
{
LOCKED;
size_t n{};
try {
xapian_try([&]{
LOCKED;
const auto id = field_id (field.c_str());
if (id == MU_MSG_FIELD_ID_NONE)
return {};
return;
char pfx[] = { mu_msg_field_xapian_prefix(id), '\0' };
std::vector<std::string> terms;
for (auto it = priv_->db().allterms_begin(pfx);
it != priv_->db().allterms_end(pfx); ++it) {
if (!func(*it))
break;
}
} MU_XAPIAN_CATCH_BLOCK;
});
return n;
}
void
Store::commit () try
Store::commit ()
{
LOCKED;
priv_->commit();
} MU_XAPIAN_CATCH_BLOCK;
xapian_try([&]{ LOCKED; priv_->commit(); });
}
static void
add_terms_values_date (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid)
@ -1059,11 +1056,11 @@ update_threading_info (MuMsg *msg, Xapian::Document& doc)
Xapian::docid
Store::Private::add_or_update_msg (unsigned docid, MuMsg *msg, GError **err)
Store::Private::add_or_update_msg (unsigned docid, MuMsg *msg)
{
g_return_val_if_fail (msg, InvalidId);
try {
return xapian_try([&]{
Xapian::Document doc (new_doc_from_message(msg));
const std::string term (get_uid_term (mu_msg_get_path(msg)));
@ -1080,7 +1077,5 @@ Store::Private::add_or_update_msg (unsigned docid, MuMsg *msg, GError **err)
writable_db().replace_document (docid, doc);
return docid;
} MU_XAPIAN_CATCH_BLOCK_G_ERROR (err, MU_ERROR_XAPIAN_STORE_FAILED);
return InvalidId;
}, InvalidId);
}

View File

@ -28,6 +28,8 @@
#include <glib.h>
#include <ostream>
#include <iostream>
#include <type_traits>
#include <xapian.h>
namespace Mu {
@ -64,8 +66,6 @@ std::string utf8_clean (const std::string& dirty);
std::string remove_ctrl (const std::string& str);
/**
* Split a string in parts
*
@ -245,78 +245,31 @@ private:
const bool color_;
};
/**
*
* don't repeat these catch blocks everywhere...
*
*/
#define MU_XAPIAN_CATCH_BLOCK \
catch (const Xapian::Error &xerr) { \
g_critical ("%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
} catch (const std::runtime_error& re) { \
g_critical ("%s: error: %s", __func__, re.what()); \
} catch (...) { \
g_critical ("%s: caught exception", __func__); \
}
#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \
catch (const Xapian::DatabaseLockError &xerr) { \
mu_util_g_set_error ((GE), \
MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \
"%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
} catch (const Xapian::DatabaseError &xerr) { \
mu_util_g_set_error ((GE),MU_ERROR_XAPIAN, \
"%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
} catch (const Xapian::Error &xerr) { \
mu_util_g_set_error ((GE),(E), \
"%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
} catch (const std::runtime_error& ex) { \
mu_util_g_set_error ((GE),(MU_ERROR_INTERNAL), \
"%s: error: %s", __func__, ex.what()); \
\
} catch (...) { \
mu_util_g_set_error ((GE),(MU_ERROR_INTERNAL), \
"%s: caught exception", __func__); \
}
#define MU_XAPIAN_CATCH_BLOCK_RETURN(R) \
catch (const Xapian::Error &xerr) { \
g_critical ("%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
return (R); \
} catch (const std::runtime_error& ex) { \
g_critical("%s: error: %s", __func__, ex.what()); \
return (R); \
} catch (...) { \
g_critical ("%s: caught exception", __func__); \
return (R); \
}
#define MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(GE,E,R) \
catch (const Xapian::Error &xerr) { \
mu_util_g_set_error ((GE),(E), \
"%s: xapian error '%s'", \
__func__, xerr.get_msg().c_str()); \
return (R); \
} catch (const std::runtime_error& ex) { \
mu_util_g_set_error ((GE),(MU_ERROR_INTERNAL), \
"%s: error: %s", __func__, ex.what()); \
return (R); \
} catch (...) { \
if ((GE)&&!(*(GE))) \
mu_util_g_set_error ((GE), \
(MU_ERROR_INTERNAL), \
"%s: caught exception", __func__); \
return (R); \
}
template <typename Func> void xapian_try(Func&& func) noexcept try {
func();
} catch (const Xapian::Error &xerr) {
g_critical ("%s: xapian error '%s'",
__func__, xerr.get_msg().c_str());
} catch (const std::runtime_error& re) {
g_critical ("%s: error: %s", __func__, re.what());
} catch (...) {
g_critical ("%s: caught exception", __func__);
}
template <typename Func, typename Default=std::invoke_result<Func>>
auto xapian_try(Func&& func, Default&& def) noexcept -> std::decay_t<decltype(func())> try {
return func();
} catch (const Xapian::Error &xerr) {
g_critical ("%s: xapian error '%s'",
__func__, xerr.get_msg().c_str());
return static_cast<Default>(def);
} catch (const std::runtime_error& re) {
g_critical ("%s: error: %s", __func__, re.what());
return static_cast<Default>(def);
} catch (...) {
g_critical ("%s: caught exception", __func__);
return static_cast<Default>(def);
}
/// Allow using enum structs as bitflags