mirror of https://github.com/djcb/mu.git
utils: rework Mu::Error and g_ logging macros with fmt
A bit more C++ template magic to remove a lot of code.
This commit is contained in:
parent
e6ab61d233
commit
742ca33740
3
Makefile
3
Makefile
|
@ -36,7 +36,8 @@ endif
|
|||
ifneq (${MU_HACKER},)
|
||||
MESON_FLAGS:=$(MESON_FLAGS) '-Dbuildtype=debug' \
|
||||
'-Db_sanitize=address' \
|
||||
'-Dreadline=enabled'
|
||||
'-Dreadline=enabled' \
|
||||
'-Dcpp_std=c++20'
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
|
|
|
@ -38,7 +38,9 @@ install: false)
|
|||
|
||||
lib_mu_utils_dep = declare_dependency(
|
||||
link_with: lib_mu_utils,
|
||||
include_directories: include_directories(['.', '..'])
|
||||
compile_args: '-DFMT_HEADER_ONLY',
|
||||
include_directories:
|
||||
include_directories(['.', '..', '../thirdparty/fmt'])
|
||||
)
|
||||
|
||||
#
|
||||
|
|
|
@ -28,6 +28,13 @@
|
|||
#include "mu-utils-format.hh"
|
||||
#include <glib.h>
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace Mu {
|
||||
|
||||
// calculate an error enum value.
|
||||
|
@ -76,68 +83,31 @@ struct Error final : public std::exception {
|
|||
/**
|
||||
* Construct an error
|
||||
*
|
||||
* @param codearg error-code
|
||||
* @param msgarg the error description
|
||||
* @param code the error-code
|
||||
* @param args... libfmt-style format string and parameters
|
||||
*/
|
||||
Error(Code codearg, const std::string& msgarg) : code_{codearg}, what_{msgarg} {}
|
||||
Error(Code codearg, std::string&& msgarg) : code_{codearg}, what_{std::move(msgarg)} {}
|
||||
template<typename...T>
|
||||
Error(Code code, fmt::format_string<T...> frm, T&&... args):
|
||||
code_{code},
|
||||
what_{fmt::format(frm, std::forward<T>(args)...)} {}
|
||||
|
||||
/**
|
||||
* Build an error from an error-code and a format string
|
||||
* Construct an error
|
||||
*
|
||||
* @param code error-code
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
* @param code the error-code
|
||||
* @param gerr a GError (or {}); the error is _consumed_ by this function
|
||||
* @param args... libfmt-style format string and parameters
|
||||
*/
|
||||
__attribute__((format(printf, 3, 0))) Error(Code codearg, const char* frm, ...)
|
||||
: code_{codearg}
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
Error(Error&& rhs) = default;
|
||||
Error(const Error& rhs) = default;
|
||||
template<typename...T>
|
||||
Error(Code code, GError **gerr, fmt::format_string<T...> frm, T&&... args):
|
||||
code_{code},
|
||||
what_{fmt::format(frm, std::forward<T>(args)...) +
|
||||
fmt::format(": {}", (gerr && *gerr) ? (*gerr)->message :
|
||||
"something went wrong")}
|
||||
{ g_clear_error(gerr); }
|
||||
|
||||
/**
|
||||
* Build an error from a GError an error-code and a format string
|
||||
*
|
||||
* @param code error-code
|
||||
* @param gerr a GError or {}, which is consumed
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
*/
|
||||
__attribute__((format(printf, 4, 0)))
|
||||
Error(Code codearg, GError** err, const char* frm, ...)
|
||||
: code_{codearg}
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
|
||||
if (err && *err)
|
||||
what_ += format(": %s", (*err)->message);
|
||||
else
|
||||
what_ += ": something went wrong";
|
||||
|
||||
g_clear_error(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* DTOR
|
||||
*
|
||||
*/
|
||||
virtual ~Error() override = default;
|
||||
|
||||
/**
|
||||
* Get the descriptive message.
|
||||
* Get the descriptive message for this error.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
@ -192,8 +162,8 @@ private:
|
|||
return error_domain;
|
||||
}
|
||||
|
||||
const Code code_;
|
||||
std::string what_;
|
||||
const Code code_;
|
||||
const std::string what_;
|
||||
};
|
||||
|
||||
} // namespace Mu
|
||||
|
|
|
@ -51,15 +51,13 @@ maybe_open_logfile()
|
|||
|
||||
const auto logdir{to_string_gchar(g_path_get_dirname(MuLogPath.c_str()))};
|
||||
if (g_mkdir_with_parents(logdir.c_str(), 0700) != 0) {
|
||||
std::cerr << "creating " << logdir << " failed:" << g_strerror(errno)
|
||||
<< std::endl;
|
||||
mu_printerrln("creating {} failed: {}", logdir, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
MuStream.open(MuLogPath, std::ios::out | std::ios::app);
|
||||
if (!MuStream.is_open()) {
|
||||
std::cerr << "opening " << MuLogPath << " failed:" << g_strerror(errno)
|
||||
<< std::endl;
|
||||
mu_printerrln("opening {} failed: {}", MuLogPath, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -86,8 +84,7 @@ maybe_rotate_logfile()
|
|||
MuStream.close();
|
||||
|
||||
if (g_rename(MuLogPath.c_str(), old.c_str()) != 0)
|
||||
std::cerr << "failed to rename " << MuLogPath << " -> " << old.c_str() << ": "
|
||||
<< g_strerror(errno) << std::endl;
|
||||
mu_printerrln("failed to rename {} -> {}: {}", MuLogPath, old, g_strerror(errno));
|
||||
|
||||
return maybe_open_logfile();
|
||||
}
|
||||
|
@ -197,7 +194,7 @@ static void
|
|||
test_logger_threads(void)
|
||||
{
|
||||
const auto testpath{test_random_tmpdir() + "/test.log"};
|
||||
g_message("log-file: %s", testpath.c_str());
|
||||
mu_message("log-file: {}", testpath);
|
||||
|
||||
auto logger = Logger::make(testpath.c_str(), Logger::Options::File | Logger::Options::Debug);
|
||||
assert_valid_result(logger);
|
||||
|
@ -212,7 +209,7 @@ test_logger_threads(void)
|
|||
threads.emplace_back(
|
||||
std::thread([n,&running]{
|
||||
while (running) {
|
||||
g_debug("log message from thread <%d>", n);
|
||||
mu_debug("log message from thread <{}>", n);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -51,15 +51,15 @@ test_regex_replace()
|
|||
int
|
||||
main(int argc, char* argv[])
|
||||
try {
|
||||
mu_test_init(&argc, &argv);
|
||||
mu_test_init(&argc, &argv);
|
||||
|
||||
g_test_add_func("/regex/match", test_regex_match);
|
||||
g_test_add_func("/regex/replace", test_regex_replace);
|
||||
return g_test_run();
|
||||
g_test_add_func("/regex/match", test_regex_match);
|
||||
g_test_add_func("/regex/replace", test_regex_replace);
|
||||
return g_test_run();
|
||||
|
||||
} catch (const std::runtime_error& re) {
|
||||
std::cerr << re.what() << "\n";
|
||||
return 1;
|
||||
mu_printerrln("{}", re.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /*BUILD_TESTS*/
|
||||
|
|
|
@ -39,8 +39,6 @@ template <typename T> using Result = tl::expected<T, Error>;
|
|||
*/
|
||||
template <typename T>
|
||||
class Result<T>::expected
|
||||
// note: "class", not "typename";
|
||||
// https://stackoverflow.com/questions/46412754/class-name-injection-and-constructors
|
||||
Ok(T&& t)
|
||||
{
|
||||
return std::move(t);
|
||||
|
@ -83,55 +81,21 @@ Err(const Result<T>& res)
|
|||
return res.error();
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
static inline Result<void>
|
||||
Ok(const T& t)
|
||||
{
|
||||
if (t)
|
||||
return Ok();
|
||||
else
|
||||
return Err(t.error());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* convenience
|
||||
*/
|
||||
|
||||
static inline tl::unexpected<Error>
|
||||
Err(Error::Code errcode, std::string&& msg="")
|
||||
template <typename ...T>
|
||||
inline tl::unexpected<Error>
|
||||
Err(Error::Code code, fmt::format_string<T...> frm, T&&... args)
|
||||
{
|
||||
return Err(Error{errcode, std::move(msg)});
|
||||
return Err(Error{code, frm, std::forward<T>(args)...});
|
||||
}
|
||||
|
||||
__attribute__((format(printf, 2, 0)))
|
||||
static inline tl::unexpected<Error>
|
||||
Err(Error::Code errcode, const char* frm, ...)
|
||||
template <typename ...T>
|
||||
inline tl::unexpected<Error>
|
||||
Err(Error::Code code, GError **err, fmt::format_string<T...> frm, T&&... args)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
auto str{vformat(frm, args)};
|
||||
va_end(args);
|
||||
|
||||
return Err(errcode, std::move(str));
|
||||
}
|
||||
|
||||
__attribute__((format(printf, 3, 0)))
|
||||
static inline tl::unexpected<Error>
|
||||
Err(Error::Code errcode, GError **err, const char* frm, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
auto str{vformat(frm, args)};
|
||||
va_end(args);
|
||||
|
||||
if (err && *err)
|
||||
str += format(" (%s)", (*err)->message ? (*err)->message : "");
|
||||
g_clear_error(err);
|
||||
|
||||
return Err(errcode, std::move(str));
|
||||
return Err(Error{code, err, frm, std::forward<T>(args)...});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -517,7 +517,7 @@ try {
|
|||
return g_test_run();
|
||||
|
||||
} catch (const std::runtime_error& re) {
|
||||
std::cerr << re.what() << "\n";
|
||||
mu_printerrln("{}", re.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,20 +35,17 @@
|
|||
|
||||
using namespace Mu;
|
||||
|
||||
char*
|
||||
Mu::test_mu_common_get_random_tmpdir()
|
||||
std::string
|
||||
Mu::test_random_tmpdir()
|
||||
{
|
||||
char* dir;
|
||||
int res;
|
||||
auto&& dir = mu_format("{}{}mu-test-{}{}test-{:x}",
|
||||
g_get_tmp_dir(),
|
||||
G_DIR_SEPARATOR,
|
||||
getuid(),
|
||||
G_DIR_SEPARATOR,
|
||||
::random() * getpid() * ::time({}));
|
||||
|
||||
dir = g_strdup_printf("%s%cmu-test-%d%ctest-%x",
|
||||
g_get_tmp_dir(),
|
||||
G_DIR_SEPARATOR,
|
||||
getuid(),
|
||||
G_DIR_SEPARATOR,
|
||||
(int)random() * getpid() * (int)time(NULL));
|
||||
|
||||
res = g_mkdir_with_parents(dir, 0700);
|
||||
auto res = g_mkdir_with_parents(dir.c_str(), 0700);
|
||||
g_assert(res != -1);
|
||||
|
||||
return dir;
|
||||
|
@ -76,12 +73,12 @@ Mu::set_en_us_utf8_locale()
|
|||
setlocale(LC_ALL, "en_US.UTF-8");
|
||||
|
||||
if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) {
|
||||
g_print("Note: Unit tests require the en_US.utf8 locale. "
|
||||
"Ignoring test cases.\n");
|
||||
return FALSE;
|
||||
mu_println("Note: Unit tests require the en_US.utf8 locale. "
|
||||
"Ignoring test cases.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -108,8 +105,6 @@ Mu::mu_test_init(int *argc, char ***argv)
|
|||
(GLogFunc)black_hole, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
Mu::allow_warnings()
|
||||
{
|
||||
|
@ -128,10 +123,8 @@ Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete}
|
|||
throw Mu::Error(Error::Code::File, &err,
|
||||
"failed to create temporary directory");
|
||||
|
||||
path_ = tmpdir;
|
||||
g_free(tmpdir);
|
||||
|
||||
g_debug("created '%s'", path_.c_str());
|
||||
path_ = to_string_gchar(std::move(tmpdir));
|
||||
mu_debug("created '{}'", path_);
|
||||
}
|
||||
|
||||
Mu::TempDir::~TempDir()
|
||||
|
|
|
@ -28,11 +28,12 @@ namespace Mu {
|
|||
/**
|
||||
* get a dir name for a random temporary directory to do tests
|
||||
*
|
||||
* @return a random dir name, g_free when it's no longer needed
|
||||
* @return a random dir name
|
||||
*/
|
||||
char* test_mu_common_get_random_tmpdir(void);
|
||||
static inline std::string test_random_tmpdir() {
|
||||
return to_string_gchar(test_mu_common_get_random_tmpdir());
|
||||
std::string test_random_tmpdir(void);
|
||||
|
||||
inline gchar* test_mu_common_get_random_tmpdir() {
|
||||
return g_strdup(test_random_tmpdir().c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
** 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MU_UTILS_HH__
|
||||
#define __MU_UTILS_HH__
|
||||
#ifndef MU_UTILS_HH__
|
||||
#define MU_UTILS_HH__
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -37,8 +37,50 @@
|
|||
#include "mu-utils-format.hh"
|
||||
#include "mu-option.hh"
|
||||
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif /*FMT_HEADER_ONLY*/
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace Mu {
|
||||
|
||||
/*
|
||||
* Logging functions connect libfmt with the Glib logging system
|
||||
*/
|
||||
|
||||
template<typename...T>
|
||||
inline void mu_debug(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_DEBUG, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
template<typename...T>
|
||||
inline void mu_info(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_INFO, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
template<typename...T>
|
||||
inline void mu_message(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_MESSAGE, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
template<typename...T>
|
||||
inline void mu_warning(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_WARNING, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
template<typename...T>
|
||||
inline void mu_critical(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_CRITICAL, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
template<typename...T>
|
||||
inline void mu_error(fmt::format_string<T...> frm, T&&... args) noexcept {
|
||||
g_log("mu", G_LOG_LEVEL_ERROR, "%s",
|
||||
fmt::format(frm, std::forward<T>(args)...).c_str());
|
||||
}
|
||||
|
||||
using StringVec = std::vector<std::string>;
|
||||
|
||||
/**
|
||||
|
@ -517,4 +559,4 @@ private:
|
|||
|
||||
} // namespace Mu
|
||||
|
||||
#endif /* __MU_UTILS_HH__ */
|
||||
#endif /* MU_UTILS_HH__ */
|
||||
|
|
|
@ -44,32 +44,27 @@ static std::string DB_PATH2;
|
|||
static std::string
|
||||
make_database(const std::string& testdir)
|
||||
{
|
||||
char* tmpdir{test_mu_common_get_random_tmpdir()};
|
||||
auto&& tmpdir{test_random_tmpdir()};
|
||||
|
||||
/* use the env var rather than `--muhome` */
|
||||
|
||||
g_setenv("MUHOME", tmpdir, 1);
|
||||
const auto cmdline{format("/bin/sh -c '"
|
||||
"%s --quiet init --maildir=%s ; "
|
||||
"%s --quiet index'",
|
||||
MU_PROGRAM,
|
||||
testdir.c_str(),
|
||||
MU_PROGRAM)};
|
||||
g_setenv("MUHOME", tmpdir.c_str(), 1);
|
||||
const auto cmdline{mu_format(
|
||||
"/bin/sh -c '"
|
||||
"{} --quiet init --maildir={} ; "
|
||||
"{} --quiet index'",
|
||||
MU_PROGRAM, testdir, MU_PROGRAM)};
|
||||
|
||||
if (g_test_verbose())
|
||||
g_printerr("\n%s\n", cmdline.c_str());
|
||||
|
||||
g_assert(g_spawn_command_line_sync(cmdline.c_str(), NULL, NULL, NULL, NULL));
|
||||
auto xpath = g_strdup_printf("%s%c%s", tmpdir, G_DIR_SEPARATOR, "xapian");
|
||||
g_free(tmpdir);
|
||||
|
||||
auto xpath = mu_format("{}{}{}",
|
||||
tmpdir, G_DIR_SEPARATOR, "xapian");
|
||||
/* ensure MUHOME worked */
|
||||
g_assert_cmpuint(::access(xpath, F_OK), ==, 0);
|
||||
g_assert_cmpuint(::access(xpath.c_str(), F_OK), ==, 0);
|
||||
|
||||
std::string dbpath{xpath};
|
||||
g_free(xpath);
|
||||
|
||||
return dbpath;
|
||||
return xpath;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
Loading…
Reference in New Issue