diff --git a/lib/utils/mu-regex.hh b/lib/utils/mu-regex.hh new file mode 100644 index 00000000..5a93b195 --- /dev/null +++ b/lib/utils/mu-regex.hh @@ -0,0 +1,133 @@ +/* +** Copyright (C) 2022 Dirk-Jan C. Binnema +** +** 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. +** +*/ +#ifndef MU_REGEX_HH__ +#define MU_REGEX_HH__ + +#include +#include + +namespace Mu { +/** + * RAII wrapper around a GRegex which in itself is a wrapper around PCRE. We use + * PCRE rather than std::regex because it is much faster. + */ +struct Regex { + /** + * Trivial constructor + * + * @return + */ + Regex() noexcept: rx_{} {} + + + /** + * Construct a new Regex object + * + * @param ptrn PRRE regular expression pattern + * @param cflags compile flags + * @param mflags match flags + * + * @return a Regex object or an error. + */ + static Result make(const std::string& ptrn, + GRegexCompileFlags cflags = G_REGEX_DEFAULT, + GRegexMatchFlags mflags = G_REGEX_MATCH_DEFAULT) noexcept try { + return Regex(ptrn.c_str(), cflags, mflags); + } catch (const Error& err) { + return Err(err); + } + + /** + * DTOR + */ + ~Regex() { + if (rx_) + g_regex_unref(rx_); + } + + /** + * Cast to the the underlying GRegex* + * + * @return a GRegex* + */ + operator const GRegex*() const noexcept { return rx_; } + + /** + * Doe this object contain a valid GRegex*? + * + * @return true or false + */ + operator bool() const noexcept { return !!rx_; } + + + // No need for a move CTOR, copying is cheap (GRegex is refcounted) + + /** + * Copy CTOR + * + * @param other some other Regex + */ + Regex(const Regex& other) noexcept { *this = other; } + + /** + * operator= + * + * @param other copy some other object to this one + * + * @return *this + */ + Regex& operator=(const Regex& other) noexcept { + if (this != &other) { + auto oldrx = rx_; + rx_ = other.rx_ ? g_regex_ref(other.rx_): nullptr; + if (oldrx) + g_regex_unref(oldrx); + } + return *this; + } + + /** + * Does this regexp match the given string? An unset Regex matches + * nothing. + * + * @param str + * @param mflags + * + * @return true or false + */ + bool matches(const std::string& str, + GRegexMatchFlags mflags=G_REGEX_MATCH_DEFAULT) const noexcept { + return rx_ ? g_regex_match(rx_, str.c_str(), mflags, {}) : false; + } + +private: + Regex(const char *ptrn, GRegexCompileFlags cflags, GRegexMatchFlags mflags) { + GError *err{}; + rx_ = g_regex_new(ptrn, cflags, mflags, &err); + if (!rx_) + throw Err(Error::Code::InvalidArgument, + "invalid regexp: '%s'", ptrn); + } + GRegex *rx_{}; +}; + +} // namespace Mu + + +#endif /* MU_REGEX_HH__ */