mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 14:25:34 +00:00
Added initial work for selecting smartctl parser type.
Refactored smartctl version parsing, added tests.
This commit is contained in:
@@ -35,6 +35,9 @@ target_sources(applib PRIVATE
|
||||
smartctl_executor.cpp
|
||||
smartctl_executor_gui.h
|
||||
smartctl_executor.h
|
||||
smartctl_output_type.h
|
||||
smartctl_version_parser.cpp
|
||||
smartctl_version_parser.h
|
||||
storage_detector.cpp
|
||||
storage_detector.h
|
||||
storage_detector_helpers.h
|
||||
|
||||
@@ -23,6 +23,7 @@ Copyright:
|
||||
#include "smartctl_ata_text_parser.h"
|
||||
#include "ata_storage_property_descr.h"
|
||||
#include "warning_colors.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "build_config.h"
|
||||
|
||||
|
||||
@@ -208,7 +209,7 @@ bool SmartctlAtaTextParser::parse_full(const std::string& full, AtaStorageAttrib
|
||||
// version info
|
||||
|
||||
std::string version, version_full;
|
||||
if (!parse_version(s, version, version_full)) {
|
||||
if (!SmartctlVersionParser::parse_version(s, version, version_full)) {
|
||||
set_error_msg("Cannot extract smartctl version information.");
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n");
|
||||
return false;
|
||||
@@ -231,7 +232,7 @@ bool SmartctlAtaTextParser::parse_full(const std::string& full, AtaStorageAttrib
|
||||
add_property(p);
|
||||
}
|
||||
|
||||
if (!check_parsed_version(version, version_full)) {
|
||||
if (!SmartctlVersionParser::check_parsed_version(SmartctlOutputParserType::Text, version)) {
|
||||
set_error_msg("Incompatible smartctl version.");
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n");
|
||||
return false;
|
||||
@@ -275,48 +276,6 @@ bool SmartctlAtaTextParser::parse_full(const std::string& full, AtaStorageAttrib
|
||||
|
||||
|
||||
|
||||
// Supply output of "smartctl --version" here.
|
||||
// returns false on failure. Non-unix newlines in s are ok.
|
||||
bool SmartctlAtaTextParser::parse_version(const std::string& s, std::string& version, std::string& version_full)
|
||||
{
|
||||
// e.g.
|
||||
// "smartctl version 5.37"
|
||||
// "smartctl 5.39"
|
||||
// "smartctl 5.39 2009-06-03 20:10" (cvs versions)
|
||||
// "smartctl 5.39 2009-08-08 r2873" (svn versions)
|
||||
if (!app_pcre_match(R"(/^smartctl (?:version )?(([0-9][^ \t\n\r]+)(?: [0-9 r:-]+)?)/mi)", s, &version_full, &version)) {
|
||||
debug_out_error("app", DBG_FUNC_MSG << "No smartctl version information found in supplied string.\n");
|
||||
return false;
|
||||
}
|
||||
hz::string_trim(version_full);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// check that the version of smartctl output can be parsed with this parser.
|
||||
bool SmartctlAtaTextParser::check_parsed_version(const std::string& version_str, [[maybe_unused]] const std::string& version_full_str)
|
||||
{
|
||||
// tested with 5.1-xx versions (1 - 18), and 5.[20 - 38].
|
||||
// note: 5.1-11 (maybe others too) with scsi disk gives non-parsable output (why?).
|
||||
|
||||
// 5.0-24, 5.0-36, 5.0-49 tested with data only, from smartmontool site.
|
||||
// can't fully test 5.0-xx, they don't support sata and I have only sata.
|
||||
const double minimum_req_version = 5.0;
|
||||
|
||||
double version = 0;
|
||||
if (hz::string_is_numeric_nolocale<double>(version_str, version, false)) {
|
||||
if (version >= minimum_req_version)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// convert e.g. "1,000,204,886,016 bytes" to 1.00 TB [931.51 GiB, 1000204886016 bytes].
|
||||
// Note: this property is present since 5.33.
|
||||
std::string SmartctlAtaTextParser::parse_byte_size(const std::string& str, int64_t& bytes, bool extended)
|
||||
|
||||
@@ -28,16 +28,11 @@ class SmartctlAtaTextParser {
|
||||
/// Parse full "smartctl -x" output
|
||||
bool parse_full(const std::string& full, AtaStorageAttribute::DiskType disk_type);
|
||||
|
||||
|
||||
/// Supply any output of smartctl here, the smartctl version will be retrieved.
|
||||
static bool parse_version(const std::string& s, std::string& version, std::string& version_full);
|
||||
|
||||
|
||||
/// Check that the version of smartctl output can be parsed with this parser.
|
||||
static bool check_parsed_version(const std::string& version_str, const std::string& version_full_str);
|
||||
|
||||
|
||||
/// Convert e.g. "1,000,204,886,016 bytes" to 1.00 TiB [931.51 GB, 1000204886016 bytes]
|
||||
/// Convert e.g. "1,000,204,886,016 bytes" to "1.00 TiB [931.51 GB, 1000204886016 bytes]"
|
||||
/// \param str String to parse
|
||||
/// \param bytes Number of bytes
|
||||
/// \param extended Return size in other units as well
|
||||
/// \return Size as a displayable string
|
||||
static std::string parse_byte_size(const std::string& str, int64_t& bytes, bool extended);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2022 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#ifndef SMARTCTL_OUTPUT_TYPE_H
|
||||
#define SMARTCTL_OUTPUT_TYPE_H
|
||||
|
||||
#include "local_glibmm.h"
|
||||
|
||||
#include "hz/enum_helper.h"
|
||||
|
||||
|
||||
enum class SmartctlOutputParserType {
|
||||
Auto,
|
||||
Json,
|
||||
Text,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Helper structure for enum-related functions
|
||||
struct SmartctlOutputParserTypeExt
|
||||
: public hz::EnumHelper<
|
||||
SmartctlOutputParserType,
|
||||
SmartctlOutputParserTypeExt,
|
||||
Glib::ustring>
|
||||
{
|
||||
static constexpr inline SmartctlOutputParserType default_value = SmartctlOutputParserType::Auto;
|
||||
|
||||
static std::unordered_map<EnumType, std::pair<std::string, Glib::ustring>> build_enum_map()
|
||||
{
|
||||
return {
|
||||
{SmartctlOutputParserType::Auto, {"auto", _("Automatic")}},
|
||||
{SmartctlOutputParserType::Json, {"json", _("JSON")}},
|
||||
{SmartctlOutputParserType::Text, {"text", _("Text")}},
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2022 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
// #include "local_glibmm.h"
|
||||
|
||||
#include "hz/string_algo.h" // string_*
|
||||
#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
#include "hz/debug.h" // debug_*
|
||||
|
||||
#include "app_pcrecpp.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
|
||||
|
||||
|
||||
|
||||
bool SmartctlVersionParser::parse_version(const std::string& s, std::string& version_only, std::string& version_full)
|
||||
{
|
||||
// e.g.
|
||||
// "smartctl version 5.37"
|
||||
// "smartctl 5.39"
|
||||
// "smartctl 5.39 2009-06-03 20:10" (cvs versions)
|
||||
// "smartctl 5.39 2009-08-08 r2873" (svn versions)
|
||||
// "smartctl 7.3 (build date Feb 11 2022)" (git versions)
|
||||
if (!app_pcre_match(R"(/^smartctl (?:version )?(([0-9][^ \t\n\r]+)(?: [0-9 r:-]+)?)/mi)", s, &version_full, &version_only)) {
|
||||
debug_out_error("app", DBG_FUNC_MSG << "No smartctl version information found in supplied string.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
hz::string_trim(version_only);
|
||||
hz::string_trim(version_full);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::optional<double> SmartctlVersionParser::get_numeric_version(const std::string& version_only)
|
||||
{
|
||||
double numeric_version = 0;
|
||||
if (!hz::string_is_numeric_nolocale<double>(version_only, numeric_version, false)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return numeric_version;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool SmartctlVersionParser::check_parsed_version(SmartctlOutputParserType parser_type, const std::string& version_only)
|
||||
{
|
||||
if (auto numeric_version = get_numeric_version(version_only); numeric_version.has_value()) {
|
||||
switch(parser_type) {
|
||||
case SmartctlOutputParserType::Auto:
|
||||
return numeric_version.value() >= minimum_req_text_version || numeric_version.value() >= minimum_req_json_version;
|
||||
case SmartctlOutputParserType::Json:
|
||||
return numeric_version.value() >= minimum_req_json_version;
|
||||
case SmartctlOutputParserType::Text:
|
||||
return numeric_version.value() >= minimum_req_text_version;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::optional<SmartctlOutputParserType> SmartctlVersionParser::detect_supported_parser_type(const std::string& version_only)
|
||||
{
|
||||
if (check_parsed_version(SmartctlOutputParserType::Json, version_only)) {
|
||||
return SmartctlOutputParserType::Json;
|
||||
}
|
||||
if (check_parsed_version(SmartctlOutputParserType::Text, version_only)) {
|
||||
return SmartctlOutputParserType::Text;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -0,0 +1,74 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2022 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#ifndef SMARTCTL_VERSION_PARSER_H
|
||||
#define SMARTCTL_VERSION_PARSER_H
|
||||
|
||||
#include "local_glibmm.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "smartctl_output_type.h"
|
||||
|
||||
|
||||
|
||||
/// Smartctl version parser.
|
||||
class SmartctlVersionParser {
|
||||
public:
|
||||
|
||||
/// Supply any text (not JSON) output of smartctl here, the smartctl version will be retrieved.
|
||||
/// The text does not have to be in Unix newline format.
|
||||
/// \param command_output "smartctl -V" command output.
|
||||
/// \param[out] version_only A string similar to "7.2"
|
||||
/// \param[out] version_full A string similar to "smartctl 7.2 2020-12-30 r5155"
|
||||
/// \return false if the version could not be parsed.
|
||||
static bool parse_version(const std::string& command_output, std::string& version_only, std::string& version_full);
|
||||
|
||||
|
||||
/// Get numeric version as a double from a parsed version.
|
||||
/// \param version_only A string similar to "7.2", as parsed by parse_version().
|
||||
/// \return Numeric version as a double, e.g. 7.2. std::nullopt if the version could not be parsed.
|
||||
static std::optional<double> get_numeric_version(const std::string& version_only);
|
||||
|
||||
|
||||
/// Check that the version of smartctl output can be parsed with a parser.
|
||||
static bool check_parsed_version(SmartctlOutputParserType parser_type, const std::string& version_only);
|
||||
|
||||
|
||||
/// Detect smartctl parser type based on smartctl version
|
||||
static std::optional<SmartctlOutputParserType> detect_supported_parser_type(const std::string& version_only);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Text Parser:
|
||||
// Tested with 5.1-xx versions (1 - 18), and 5.[20 - 38].
|
||||
// Note: 5.1-11 (maybe others too) with scsi disk gives non-parsable output (why?).
|
||||
// 5.0-24, 5.0-36, 5.0-49 tested with data only, from smartmontools site.
|
||||
// Can't fully test 5.0-xx, they don't support sata, and I have only sata.
|
||||
static constexpr double minimum_req_text_version = 5.0;
|
||||
|
||||
// JSON in 7.2 is still experimental, but we have to have some cut-off point.
|
||||
static constexpr double minimum_req_json_version = 7.2;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -22,7 +22,7 @@ Copyright:
|
||||
#include "smartctl_ata_text_parser.h"
|
||||
#include "storage_settings.h"
|
||||
#include "smartctl_executor.h"
|
||||
|
||||
#include "smartctl_version_parser.h"
|
||||
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
|
||||
}
|
||||
|
||||
std::string version, version_full;
|
||||
if (!SmartctlAtaTextParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all?
|
||||
if (!SmartctlVersionParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all?
|
||||
return _("Cannot get smartctl version information.");
|
||||
|
||||
// Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
|
||||
|
||||
@@ -15,6 +15,7 @@ endif()
|
||||
add_library(applib_tests OBJECT)
|
||||
target_sources(applib_tests PRIVATE
|
||||
test_app_pcrecpp.cpp
|
||||
test_smartctl_version_parser.cpp
|
||||
)
|
||||
target_link_libraries(applib_tests PRIVATE
|
||||
applib
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/******************************************************************************
|
||||
License: BSD Zero Clause License
|
||||
Copyright:
|
||||
(C) 2022 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib_tests
|
||||
/// \weakgroup applib_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "applib/smartctl_version_parser.h"
|
||||
|
||||
|
||||
|
||||
TEST_CASE("SmartctlVersionParser", "[app][parser]")
|
||||
{
|
||||
std::string version_only, version_full;
|
||||
|
||||
SECTION("Parse with version keyword") {
|
||||
SmartctlVersionParser::parse_version("smartctl version 5.37", version_only, version_full);
|
||||
REQUIRE(version_only == "5.37");
|
||||
REQUIRE(version_full == "5.37");
|
||||
}
|
||||
|
||||
SECTION("Parse without version keyword") {
|
||||
SmartctlVersionParser::parse_version("smartctl 5.39", version_only, version_full);
|
||||
REQUIRE(version_only == "5.39");
|
||||
REQUIRE(version_full == "5.39");
|
||||
}
|
||||
|
||||
SECTION("Parse with date (CVS)") {
|
||||
SmartctlVersionParser::parse_version("smartctl 5.39 2009-06-03 20:10", version_only, version_full);
|
||||
REQUIRE(version_only == "5.39");
|
||||
REQUIRE(version_full == "5.39 2009-06-03 20:10");
|
||||
}
|
||||
|
||||
SECTION("Parse with date (SVN)") {
|
||||
SmartctlVersionParser::parse_version("smartctl 5.39 2009-08-08 r2873", version_only, version_full);
|
||||
REQUIRE(version_only == "5.39");
|
||||
REQUIRE(version_full == "5.39 2009-08-08 r2873");
|
||||
}
|
||||
|
||||
SECTION("Parse old 5.0") {
|
||||
SmartctlVersionParser::parse_version("smartctl version 5.0-49", version_only, version_full);
|
||||
REQUIRE(version_only == "5.0-49");
|
||||
REQUIRE(version_full == "5.0-49");
|
||||
}
|
||||
|
||||
SECTION("Parse full output (SVN)") {
|
||||
std::string output =
|
||||
R"(smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.3.18-lp152.66-default] (SUSE RPM)
|
||||
Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org
|
||||
|
||||
smartctl comes with ABSOLUTELY NO WARRANTY. This is free
|
||||
software, and you are welcome to redistribute it under
|
||||
the terms of the GNU General Public License; either
|
||||
version 2, or (at your option) any later version.
|
||||
See http://www.gnu.org for further details.
|
||||
|
||||
smartmontools release 7.2 dated 2020-12-30 at 16:48:30 UTC
|
||||
smartmontools SVN rev 5155 dated 2020-12-30 at 16:49:18
|
||||
smartmontools build host: x86_64-suse-linux-gnu
|
||||
smartmontools build with: C++14, GCC 7.5.0
|
||||
smartmontools configure arguments: '--host=x86_64-suse-linux-gnu' '--build=x86_64-suse-linux-gnu' '--program-prefix=' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/lib' '--localstatedir=/var' '--sharedstatedir=/var/lib' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--disable-dependency-tracking' '--docdir=/usr/share/doc/packages/smartmontools' '--with-selinux' '--with-libsystemd' '--with-systemdsystemunitdir=/usr/lib/systemd/system' '--with-savestates' '--with-attributelog' '--with-nvme-devicescan' 'build_alias=x86_64-suse-linux-gnu' 'host_alias=x86_64-suse-linux-gnu' 'CXXFLAGS=-O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -fPIE ' 'LDFLAGS=-pie' 'CFLAGS=-O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -fPIE' 'PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'
|
||||
)";
|
||||
SmartctlVersionParser::parse_version(output, version_only, version_full);
|
||||
REQUIRE(version_only == "7.2");
|
||||
REQUIRE(version_full == "7.2 2020-12-30 r5155");
|
||||
}
|
||||
|
||||
SECTION("Parse full output (git)") {
|
||||
std::string output =
|
||||
R"(smartctl 7.3 (build date Feb 11 2022) [x86_64-linux-5.3.18-lp152.66-default] (local build)
|
||||
Copyright (C) 2002-22, Bruce Allen, Christian Franke, www.smartmontools.org
|
||||
|
||||
smartctl comes with ABSOLUTELY NO WARRANTY. This is free
|
||||
software, and you are welcome to redistribute it under
|
||||
the terms of the GNU General Public License; either
|
||||
version 2, or (at your option) any later version.
|
||||
See https://www.gnu.org for further details.
|
||||
|
||||
smartmontools release 7.3 dated 2020-12-30 at 16:48:30 UTC
|
||||
smartmontools SVN rev is unknown
|
||||
smartmontools build host: x86_64-pc-linux-gnu
|
||||
smartmontools build with: C++11, GCC 7.5.0
|
||||
smartmontools configure arguments: [no arguments given]
|
||||
|
||||
)";
|
||||
SmartctlVersionParser::parse_version(output, version_only, version_full);
|
||||
REQUIRE(version_only == "7.3");
|
||||
REQUIRE(version_full == "7.3");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -21,13 +21,13 @@ Copyright:
|
||||
#include "hz/fs.h"
|
||||
#include "rconfig/rconfig.h"
|
||||
#include "applib/storage_detector.h"
|
||||
#include "applib/smartctl_ata_text_parser.h"
|
||||
#include "applib/gui_utils.h" // gui_show_error_dialog
|
||||
#include "applib/smartctl_executor.h" // get_smartctl_binary()
|
||||
#include "applib/smartctl_executor_gui.h"
|
||||
#include "applib/app_gtkmm_tools.h" // app_gtkmm_*
|
||||
#include "applib/warning_colors.h" // app_property_get_label_highlight_color
|
||||
#include "applib/app_pcrecpp.h" // app_pcre_match
|
||||
#include "applib/smartctl_version_parser.h"
|
||||
|
||||
#include "gsc_init.h" // app_quit()
|
||||
#include "gsc_about_dialog.h"
|
||||
@@ -118,7 +118,7 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder>
|
||||
}
|
||||
|
||||
std::string version, version_full;
|
||||
if (!SmartctlAtaTextParser::parse_version(output, version, version_full)) {
|
||||
if (!SmartctlVersionParser::parse_version(output, version, version_full)) {
|
||||
error_msg = _("Smartctl returned invalid output.");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ target_sources(hz INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bad_cast_exception.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data_file.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/debug.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/enum_helper.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/env_tools.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/error.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/error_holder.h
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2022 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup hz
|
||||
/// \weakgroup hz
|
||||
/// @{
|
||||
|
||||
#ifndef ENUM_HELPER_H
|
||||
#define ENUM_HELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
|
||||
namespace hz {
|
||||
|
||||
|
||||
/// Helper class for defining enum-related functions.
|
||||
/// In EnumExtClass it expects the following (accessible) members:
|
||||
/// - static inline EnumType default_value = ...;
|
||||
/// - static const std::unordered_map<EnumType, std::pair<std::string, DisplayableStringType>>& get_enum_static_map();
|
||||
template <typename Enum, typename EnumExtClass, typename DisplayableStringType>
|
||||
class EnumHelper {
|
||||
public:
|
||||
|
||||
using EnumType = Enum;
|
||||
using EnumMapType = std::unordered_map<EnumType, std::pair<std::string, DisplayableStringType>>;
|
||||
|
||||
|
||||
/// Return storable name of an enum member
|
||||
static std::string get_storable_name(EnumType enum_value)
|
||||
{
|
||||
const auto& m = get_enum_static_map();
|
||||
// Iterator: enum -> pair{storable, displayable}
|
||||
auto iter = m.find(enum_value);
|
||||
return iter != m.end() ? std::string(iter->second.first) : std::string();
|
||||
}
|
||||
|
||||
|
||||
/// Return an enum member by its storable name
|
||||
static EnumType get_by_storable_name(const std::string& storable_name,
|
||||
EnumType default_value = EnumExtClass::default_value)
|
||||
{
|
||||
const auto& m = get_storable_enum_static_map();
|
||||
// Iterator: storable_name -> enum
|
||||
auto iter = m.find(storable_name);
|
||||
return iter != m.end() ? iter->second : default_value;
|
||||
}
|
||||
|
||||
|
||||
/// Return displayable name of an enum member
|
||||
static DisplayableStringType get_displayable_name(EnumType enum_value)
|
||||
{
|
||||
const auto& m = get_enum_static_map();
|
||||
// Iterator: enum -> pair{storable, displayable}
|
||||
auto iter = m.find(enum_value);
|
||||
return iter != m.end() ? DisplayableStringType(iter->second.second) : DisplayableStringType();
|
||||
}
|
||||
|
||||
|
||||
/// Return all possible members of an enum
|
||||
static std::vector<EnumType> getAllValues()
|
||||
{
|
||||
static const auto v = build_enum_value_list();
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// Get a static map of storable names to enum values.
|
||||
static const EnumMapType& get_enum_static_map()
|
||||
{
|
||||
static const auto m = EnumExtClass::build_enum_map();
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/// Get a static map of storable names to enum values.
|
||||
static const std::unordered_map<std::string, EnumType>& get_storable_enum_static_map()
|
||||
{
|
||||
static const auto m = build_storable_enum_map();
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/// Build a map of storable names to enum values.
|
||||
static std::unordered_map<std::string, EnumType> build_storable_enum_map()
|
||||
{
|
||||
std::unordered_map<std::string, EnumType> m;
|
||||
for (const auto& [data, enum_value] : EnumExtClass::get_enum_static_map()) {
|
||||
m.emplace(data.first, enum_value);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/// Build a list of enum values from get_enum_static_map()
|
||||
static std::vector<EnumType> build_enum_value_list()
|
||||
{
|
||||
const auto& m = EnumExtClass::get_enum_static_map();
|
||||
std::vector<EnumType> v;
|
||||
v.reserve(m.size());
|
||||
for (const auto& p : m) {
|
||||
v.push_back(p.first);
|
||||
}
|
||||
std::sort(v.begin(), v.end());
|
||||
return v;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
Reference in New Issue
Block a user