Initial work on JSON parser.

This commit is contained in:
Alexander Shaduri
2024-03-19 16:16:36 +04:00
parent ae5a327116
commit 9889f8792f
22 changed files with 311 additions and 136 deletions
+2
View File
@@ -17,6 +17,7 @@
# misc-no-recursion is too noisy.
# modernize-raw-string-literal is very noisy.
# modernize-use-trailing-return-type is contrary to our style.
# performance-enum-size is too noisy.
# readability-avoid-unconditional-preprocessor-if is triggered on platform-specific code
# readability-convert-member-functions-to-static is triggered for many callbacks.
# readability-function-cognitive-complexity needed by UI constructors.
@@ -63,6 +64,7 @@ Checks: >
-objc-*,
openmp-*,
performance-*,
-performance-enum-size,
portability-*,
readability-*,
-readability-avoid-unconditional-preprocessor-if,
+4
View File
@@ -34,6 +34,8 @@ target_sources(applib PRIVATE
smartctl_parser.h
smartctl_json_ata_parser.cpp
smartctl_json_ata_parser.h
smartctl_json_basic_parser.cpp
smartctl_json_basic_parser.h
smartctl_json_parser_helpers.h
smartctl_executor.cpp
smartctl_executor_gui.h
@@ -41,6 +43,8 @@ target_sources(applib PRIVATE
smartctl_parser_types.h
smartctl_text_ata_parser.cpp
smartctl_text_ata_parser.h
smartctl_text_basic_parser.cpp
smartctl_text_basic_parser.h
smartctl_text_parser_helper.cpp
smartctl_text_parser_helper.h
smartctl_version_parser.cpp
@@ -42,8 +42,8 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
SmartctlAtaTextParser parser;
if (const auto parse_status = parser.parse_full(contents); !parse_status.has_value()) {
SmartctlTextAtaParser parser;
if (const auto parse_status = parser.parse(contents); !parse_status.has_value()) {
debug_out_error("app", "Cannot parse file contents: " << parse_status.error().message() << "\n");
return EXIT_FAILURE;
}
+2 -2
View File
@@ -243,10 +243,10 @@ std::string SelfTest::update(const std::shared_ptr<CommandExecutor>& smartctl_ex
return error_msg;
const AtaStorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
auto parser = SmartctlParser::create(SmartctlParserType::Text);
auto parser = SmartctlParser::create(SmartctlParserType::TextAta);
DBG_ASSERT_RETURN(parser, "Cannot create parser");
auto parse_status = parser->parse_full(output);
auto parse_status = parser->parse(output);
if (!parse_status) {
return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message());
}
+7 -9
View File
@@ -80,20 +80,18 @@ _custom/smart_enabled
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_full(const std::string& json_data_full)
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse(std::string_view smartctl_output)
{
using namespace SmartctlJsonParserHelpers;
this->set_data_full(json_data_full);
if (hz::string_trim_copy(json_data_full).empty()) {
if (hz::string_trim_copy(smartctl_output).empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n");
return hz::Unexpected(SmartctlParserError::EmptyInput, "Smartctl data is empty.");
}
nlohmann::json json_root_node;
try {
json_root_node = nlohmann::json::parse(json_data_full);
json_root_node = nlohmann::json::parse(smartctl_output);
} catch (const nlohmann::json::parse_error& e) {
debug_out_warn("app", DBG_FUNC_MSG << "Error parsing smartctl output as JSON: " << e.what() << "\n");
return hz::Unexpected(SmartctlParserError::SyntaxError, std::string("Invalid JSON data: ") + e.what());
@@ -112,7 +110,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_full(const st
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_version(const nlohmann::json& json_root_node)
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_version(const nlohmann::json& json_root_node)
{
using namespace SmartctlJsonParserHelpers;
@@ -155,7 +153,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_version(const
p.section = AtaStorageProperty::Section::info; // add to info section
add_property(p);
}
if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::Json, smartctl_version)) {
if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::JsonAta, smartctl_version)) {
debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n");
return hz::Unexpected(SmartctlParserError::IncompatibleVersion, "Incompatible smartctl version.");
}
@@ -165,7 +163,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_version(const
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_section_info(const nlohmann::json& json_root_node)
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(const nlohmann::json& json_root_node)
{
using namespace SmartctlJsonParserHelpers;
@@ -285,7 +283,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_section_info(
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaJsonParser::parse_section_health(const nlohmann::json& json_root_node)
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_health(const nlohmann::json& json_root_node)
{
using namespace SmartctlJsonParserHelpers;
+3 -3
View File
@@ -20,14 +20,14 @@ Copyright:
/// Smartctl (S)ATA JSON output parser
class SmartctlAtaJsonParser : public SmartctlParser {
class SmartctlJsonAtaParser : public SmartctlParser {
public:
// Defaulted, used by make_unique.
SmartctlAtaJsonParser() = default;
SmartctlJsonAtaParser() = default;
// Overridden
hz::ExpectedVoid<SmartctlParserError> parse_full(const std::string& json_data_full) override;
hz::ExpectedVoid<SmartctlParserError> parse(std::string_view smartctl_output) override;
private:
+44
View File
@@ -0,0 +1,44 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
// #include "local_glibmm.h"
//#include <clocale> // localeconv
//#include <cstdint>
//#include <utility>
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
//#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_text_ata_parser.h"
//#include "ata_storage_property_descr.h"
// #include "warning_colors.h"
//#include "smartctl_version_parser.h"
#include "smartctl_json_basic_parser.h"
// Parse full "smartctl -x" output
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonBasicParser::parse(std::string_view smartctl_output)
{
return {};
}
/// @}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SMARTCTL_JSON_BASIC_PARSER_H
#define SMARTCTL_JSON_BASIC_PARSER_H
//#include <string>
//#include <vector>
#include "smartctl_parser.h"
/// Parse info output, regardless of device type
class SmartctlJsonBasicParser : public SmartctlParser {
public:
// Defaulted, used by make_unique.
SmartctlJsonBasicParser() = default;
// Overridden
hz::ExpectedVoid<SmartctlParserError> parse(std::string_view smartctl_output) override;
};
#endif
/// @}
+21 -26
View File
@@ -15,36 +15,45 @@ Copyright:
#include "smartctl_parser.h"
#include "smartctl_text_ata_parser.h"
#include "smartctl_json_ata_parser.h"
#include "ata_storage_property_descr.h"
#include "warning_colors.h"
#include "smartctl_json_basic_parser.h"
#include "smartctl_text_basic_parser.h"
//#include "ata_storage_property_descr.h"
std::unique_ptr<SmartctlParser> SmartctlParser::create(SmartctlParserType type)
{
switch(type) {
case SmartctlParserType::Json:
return std::make_unique<SmartctlAtaJsonParser>();
case SmartctlParserType::Text:
return std::make_unique<SmartctlAtaTextParser>();
case SmartctlParserType::JsonBasic:
return std::make_unique<SmartctlJsonBasicParser>();
break;
case SmartctlParserType::JsonAta:
return std::make_unique<SmartctlJsonAtaParser>();
break;
case SmartctlParserType::TextBasic:
return std::make_unique<SmartctlTextBasicParser>();
break;
case SmartctlParserType::TextAta:
return std::make_unique<SmartctlTextAtaParser>();
break;
}
return nullptr;
}
hz::ExpectedValue<SmartctlParserType, SmartctlParserError> SmartctlParser::detect_output_type(const std::string& output)
hz::ExpectedValue<SmartctlParserFormat, SmartctlParserError> SmartctlParser::detect_output_format(std::string_view smartctl_output)
{
// Look for the first non-whitespace symbol
auto first_symbol = std::find_if(output.begin(), output.end(), [&](char c) {
const auto* first_symbol = std::find_if(smartctl_output.begin(), smartctl_output.end(), [&](char c) {
return !std::isspace(c, std::locale::classic());
});
if (first_symbol != output.end()) {
if (first_symbol != smartctl_output.end()) {
if (*first_symbol == '{') {
return SmartctlParserType::Json;
return SmartctlParserFormat::Json;
}
if (output.rfind("smartctl", static_cast<std::size_t>(first_symbol - output.begin())) == 0) {
return SmartctlParserType::Text;
if (smartctl_output.rfind("smartctl", static_cast<std::size_t>(first_symbol - smartctl_output.begin())) == 0) {
return SmartctlParserFormat::Text;
}
return hz::Unexpected(SmartctlParserError::UnsupportedFormat, "Unsupported format while trying to detect smartctl output format.");
}
@@ -53,13 +62,6 @@ hz::ExpectedValue<SmartctlParserType, SmartctlParserError> SmartctlParser::detec
std::string SmartctlParser::get_data_full() const
{
return data_full_;
}
const std::vector<AtaStorageProperty>& SmartctlParser::get_properties() const
{
return properties_;
@@ -76,13 +78,6 @@ void SmartctlParser::add_property(AtaStorageProperty p)
void SmartctlParser::set_data_full(const std::string& s)
{
data_full_ = s;
}
/// @}
+3 -10
View File
@@ -12,7 +12,7 @@ Copyright:
#ifndef SMARTCTL_PARSER_H
#define SMARTCTL_PARSER_H
#include <string>
#include <string_view>
#include <vector>
#include <memory>
@@ -71,16 +71,13 @@ class SmartctlParser {
/// Parse full "smartctl -x" output.
/// Note: Once parsed, this function cannot be called again.
virtual hz::ExpectedVoid<SmartctlParserError> parse_full(const std::string& full) = 0;
virtual hz::ExpectedVoid<SmartctlParserError> parse(std::string_view smartctl_output) = 0;
/// Detect smartctl output type (text, json).
[[nodiscard]] static hz::ExpectedValue<SmartctlParserType, SmartctlParserError> detect_output_type(const std::string& output);
[[nodiscard]] static hz::ExpectedValue<SmartctlParserFormat, SmartctlParserError> detect_output_format(std::string_view smartctl_output);
/// Get "full" data, as passed to parse_full().
[[nodiscard]] std::string get_data_full() const;
/// Get parse result properties
[[nodiscard]] const std::vector<AtaStorageProperty>& get_properties() const;
@@ -90,14 +87,10 @@ class SmartctlParser {
/// Add a property into property list, look up and set its description
void add_property(AtaStorageProperty p);
/// Set "full" data ("smartctl -x" output), json or text.
void set_data_full(const std::string& s);
private:
std::vector<AtaStorageProperty> properties_; ///< Parsed data properties
std::string data_full_; ///< full data, filled by parse_full()
};
+15 -5
View File
@@ -19,8 +19,10 @@ Copyright:
enum class SmartctlParserType {
Json,
Text,
JsonBasic, ///< Info only
JsonAta,
TextBasic, ///< Info only
TextAta,
};
@@ -32,13 +34,15 @@ struct SmartctlParserTypeExt
SmartctlParserTypeExt,
Glib::ustring>
{
static constexpr inline SmartctlParserType default_value = SmartctlParserType::Json;
static constexpr inline SmartctlParserType default_value = SmartctlParserType::JsonAta;
static std::unordered_map<EnumType, std::pair<std::string, Glib::ustring>> build_enum_map()
{
return {
{SmartctlParserType::Json, {"json", _("JSON")}},
{SmartctlParserType::Text, {"text", _("Text")}},
{SmartctlParserType::JsonBasic, {"json_basic", _("JSON Basic")}},
{SmartctlParserType::JsonAta, {"json_ata", _("JSON ATA")}},
{SmartctlParserType::TextBasic, {"text_basic", _("Text Basic")}},
{SmartctlParserType::TextAta, {"text_ata", _("Text ATA")}},
};
}
@@ -46,6 +50,12 @@ struct SmartctlParserTypeExt
enum class SmartctlParserFormat {
Json,
Text,
};
enum class SmartctlParserSettingType {
Auto,
+22 -25
View File
@@ -69,15 +69,12 @@ namespace {
// Parse full "smartctl -x" output
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_full(const std::string& full)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse(std::string_view smartctl_output)
{
this->set_data_full(full);
// -------------------- Fix the output, so it doesn't interfere with proper parsing
// perform any2unix
std::string s = hz::string_trim_copy(hz::string_any_to_unix_copy(full));
std::string s = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output));
if (s.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n");
@@ -204,7 +201,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_full(const st
// version info
std::string version, version_full;
if (!SmartctlVersionParser::parse_version(s, version, version_full)) {
if (!SmartctlVersionParser::parse_version_text(s, version, version_full)) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n");
return hz::Unexpected(SmartctlParserError::NoVersion, "Cannot extract smartctl version information.");
}
@@ -226,7 +223,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_full(const st
add_property(p);
}
if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::Text, version)) {
if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::TextAta, version)) {
debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n");
return hz::Unexpected(SmartctlParserError::IncompatibleVersion, "Incompatible smartctl version.");
}
@@ -269,7 +266,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_full(const st
// Parse the section part (with "=== .... ===" header) - info or data sections.
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section(const std::string& header, const std::string& body)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section(const std::string& header, const std::string& body)
{
if (app_pcre_match("/START OF INFORMATION SECTION/mi", header)) {
return parse_section_info(body);
@@ -311,7 +308,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section(const
// ------------------------------------------------ INFO SECTION
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_info(const std::string& body)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_info(const std::string& body)
{
this->set_data_section_info(body);
@@ -423,7 +420,7 @@ http://knowledge.seagate.com/articles/en_US/FAQ/213891en
// Parse a component (one line) of the info section
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_info_property(AtaStorageProperty& p)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_info_property(AtaStorageProperty& p)
{
// ---- Info
if (p.section != AtaStorageProperty::Section::info) {
@@ -613,7 +610,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_info_
// Parse the Data section (without "===" header)
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data(const std::string& body)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data(const std::string& body)
{
this->set_data_section_data(body);
@@ -764,7 +761,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data(
// -------------------- Health
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_health(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_health(const std::string& sub)
{
// Health section data (--info and --get=all):
/*
@@ -807,7 +804,7 @@ Device is: In smartctl database [for details use: -P show]
// -------------------- Capabilities
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_capabilities(const std::string& sub_initial)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_capabilities(const std::string& sub_initial)
{
// Capabilities section data:
/*
@@ -987,7 +984,7 @@ SCT capabilities: (0x003d) SCT Status supported.
// Check the capabilities for internal properties we can use.
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_internal_capabilities(AtaStorageProperty& cap_prop)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_internal_capabilities(AtaStorageProperty& cap_prop)
{
// Some special capabilities we're interested in.
@@ -1244,7 +1241,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_
// -------------------- Attributes
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_attributes(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_attributes(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -1465,7 +1462,7 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_directory_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_directory_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -1517,7 +1514,7 @@ Address Access R/W Size Description
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_error_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_error_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -1714,7 +1711,7 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours)
// -------------------- Selftest Log
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_selftest_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_selftest_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -1893,7 +1890,7 @@ Num Test_Description Status Remaining LifeTime(hours) LBA
// -------------------- Selective Selftest Log
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -1950,7 +1947,7 @@ If Selective self-test is pending on power-up, resume after 0 minute delay.
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_scttemp_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_scttemp_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -2040,7 +2037,7 @@ Index Estimated Time Temperature Celsius
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_scterc_log(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_scterc_log(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -2089,7 +2086,7 @@ SCT Error Recovery Control:
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_devstat(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_devstat(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -2260,7 +2257,7 @@ Page Offset Size Value Description
hz::ExpectedVoid<SmartctlParserError> SmartctlAtaTextParser::parse_section_data_subsection_sataphy(const std::string& sub)
hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_subsection_sataphy(const std::string& sub)
{
AtaStorageProperty pt; // template for easy copying
pt.section = AtaStorageProperty::Section::data;
@@ -2318,14 +2315,14 @@ ID Size Value Description
void SmartctlAtaTextParser::set_data_section_info(std::string s)
void SmartctlTextAtaParser::set_data_section_info(std::string s)
{
data_section_info_ = std::move(s);
}
void SmartctlAtaTextParser::set_data_section_data(std::string s)
void SmartctlTextAtaParser::set_data_section_data(std::string s)
{
data_section_data_ = std::move(s);
}
+4 -4
View File
@@ -20,16 +20,16 @@ Copyright:
/// Smartctl (S)ATA text output parser.
/// Note: ALL parse_* functions (except parse_full() and parse_version())
/// Note: ALL parse_* functions (except parse())
/// expect data in unix-newline format!
class SmartctlAtaTextParser : public SmartctlParser {
class SmartctlTextAtaParser : public SmartctlParser {
public:
// Defaulted, used by make_unique.
SmartctlAtaTextParser() = default;
SmartctlTextAtaParser() = default;
// Overridden
hz::ExpectedVoid<SmartctlParserError> parse_full(const std::string& full) override;
hz::ExpectedVoid<SmartctlParserError> parse(std::string_view smartctl_output) override;
protected:
+43
View File
@@ -0,0 +1,43 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
// #include "local_glibmm.h"
//#include <clocale> // localeconv
//#include <cstdint>
//#include <utility>
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
//#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_text_ata_parser.h"
//#include "ata_storage_property_descr.h"
// #include "warning_colors.h"
//#include "smartctl_version_parser.h"
#include "smartctl_text_basic_parser.h"
hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string_view smartctl_output)
{
return {};
}
/// @}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SMARTCTL_TEXT_BASIC_PARSER_H
#define SMARTCTL_TEXT_BASIC_PARSER_H
//#include <string>
//#include <vector>
#include "smartctl_parser.h"
/// Parse info output, regardless of device type
class SmartctlTextBasicParser : public SmartctlParser {
public:
// Defaulted, used by make_unique.
SmartctlTextBasicParser() = default;
// Overridden
hz::ExpectedVoid<SmartctlParserError> parse(std::string_view smartctl_output) override;
};
#endif
/// @}
+9 -8
View File
@@ -21,7 +21,7 @@ Copyright:
bool SmartctlVersionParser::parse_version(const std::string& s, std::string& version_only, std::string& version_full)
bool SmartctlVersionParser::parse_version_text(const std::string& s, std::string& version_only, std::string& version_full)
{
// e.g.
// "smartctl version 5.37"
@@ -57,9 +57,11 @@ bool SmartctlVersionParser::check_parsed_version(SmartctlParserType parser_type,
{
if (auto numeric_version = get_numeric_version(version_only); numeric_version.has_value()) {
switch(parser_type) {
case SmartctlParserType::Json:
case SmartctlParserType::JsonBasic:
case SmartctlParserType::JsonAta:
return numeric_version.value() >= minimum_req_json_version;
case SmartctlParserType::Text:
case SmartctlParserType::TextBasic:
case SmartctlParserType::TextAta:
return numeric_version.value() >= minimum_req_text_version;
}
}
@@ -70,11 +72,10 @@ bool SmartctlVersionParser::check_parsed_version(SmartctlParserType parser_type,
std::optional<SmartctlParserType> SmartctlVersionParser::detect_supported_parser_type(const std::string& version_only)
{
if (check_parsed_version(SmartctlParserType::Json, version_only)) {
return SmartctlParserType::Json;
}
if (check_parsed_version(SmartctlParserType::Text, version_only)) {
return SmartctlParserType::Text;
for (auto type : SmartctlParserTypeExt::getAllValues()) {
if (check_parsed_version(type, version_only)) {
return type;
}
}
return std::nullopt;
}
+3 -3
View File
@@ -28,15 +28,15 @@ class SmartctlVersionParser {
/// 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 s "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);
static bool parse_version_text(const std::string& s, 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().
/// \param version_only A string similar to "7.2", as parsed by parse_version_text().
/// \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);
+19 -13
View File
@@ -146,7 +146,7 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
}
std::string version, version_full;
if (!SmartctlVersionParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all?
if (!SmartctlVersionParser::parse_version_text(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).
@@ -174,10 +174,10 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
smart_enabled_ = false;
} else {
// Note: We don't use SmartctlAtaTextParser here, because this information
// Note: We don't use SmartctlTextAtaParser here, because this information
// may be in some other format. If this information is valid, only then it's
// passed to SmartctlAtaTextParser.
// Compared to SmartctlAtaTextParser, this one is much looser.
// passed to SmartctlTextAtaParser.
// Compared to SmartctlTextAtaParser, this one is much looser.
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this
@@ -241,10 +241,10 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
}
auto parser = SmartctlParser::create(SmartctlParserType::Text);
auto parser = SmartctlParser::create(SmartctlParserType::TextAta);
DBG_ASSERT_RETURN(parser, "Cannot create parser");
if (parser->parse_full(this->info_output_)) { // try to parse it
if (parser->parse(this->info_output_)) { // try to parse it
this->set_properties(StoragePropertyProcessor::process_properties(parser->get_properties(), disk_type)); // copy to our drive, overwriting old data
}
}
@@ -317,24 +317,30 @@ std::string StorageDevice::parse_data()
disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
}
auto parser_type = SmartctlParser::detect_output_type(this->full_output_);
auto parser_format = SmartctlParser::detect_output_format(this->full_output_);
if (!parser_type.has_value()) {
return parser_type.error().message();
if (!parser_format.has_value()) {
return parser_format.error().message();
}
auto parser = SmartctlParser::create(parser_type.value());
// TODO Choose format according to device type
SmartctlParserType parser_type = SmartctlParserType::TextAta;
if (parser_format == SmartctlParserFormat::Json) {
parser_type = SmartctlParserType::JsonAta;
}
auto parser = SmartctlParser::create(parser_type);
DBG_ASSERT_RETURN(parser, "Cannot create parser");
// Try to parse it (parse only, set the properties after basic parsing).
const auto parse_status = parser->parse_full(this->full_output_);
const auto parse_status = parser->parse(this->full_output_);
if (parse_status.has_value()) {
// refresh basic info too
this->info_output_ = parser->get_data_full(); // put data including version information
this->info_output_ = this->full_output_; // put data including version information
// note: this will clear the non-basic properties!
// this will parse some info that is already parsed by SmartctlAtaTextParser::parse_full(),
// this will parse some info that is already parsed by SmartctlAtaTextParser::parse(),
// but this one sets the StorageDevice class members, not properties.
this->parse_basic_data(false, false); // don't emit signal, we're not complete yet.
+7 -7
View File
@@ -21,21 +21,21 @@ Copyright:
TEST_CASE("SmartctlFormatDetection", "[app][parser]")
{
REQUIRE(SmartctlParser::detect_output_type({}).error().data() == SmartctlParserError::EmptyInput);
REQUIRE(SmartctlParser::detect_output_format({}).error().data() == SmartctlParserError::EmptyInput);
REQUIRE(SmartctlParser::detect_output_type("smart").error().data() == SmartctlParserError::UnsupportedFormat);
REQUIRE(SmartctlParser::detect_output_format("smart").error().data() == SmartctlParserError::UnsupportedFormat);
REQUIRE(SmartctlParser::detect_output_type("{ }").value() == SmartctlParserType::Json);
REQUIRE(SmartctlParser::detect_output_format("{ }").value() == SmartctlParserFormat::Json);
REQUIRE(SmartctlParser::detect_output_type(" \n { } ").value() == SmartctlParserType::Json);
REQUIRE(SmartctlParser::detect_output_format(" \n { } ").value() == SmartctlParserFormat::Json);
REQUIRE(SmartctlParser::detect_output_type("smartctl").value() == SmartctlParserType::Text);
REQUIRE(SmartctlParser::detect_output_format("smartctl").value() == SmartctlParserFormat::Text);
REQUIRE(SmartctlParser::detect_output_type(
REQUIRE(SmartctlParser::detect_output_format(
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
)").value() == SmartctlParserType::Text);
)").value() == SmartctlParserFormat::Text);
}
@@ -24,31 +24,31 @@ 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);
SmartctlVersionParser::parse_version_text("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);
SmartctlVersionParser::parse_version_text("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);
SmartctlVersionParser::parse_version_text("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);
SmartctlVersionParser::parse_version_text("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);
SmartctlVersionParser::parse_version_text("smartctl version 5.0-49", version_only, version_full);
REQUIRE(version_only == "5.0-49");
REQUIRE(version_full == "5.0-49");
}
@@ -70,7 +70,7 @@ 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);
SmartctlVersionParser::parse_version_text(output, version_only, version_full);
REQUIRE(version_only == "7.2");
REQUIRE(version_full == "7.2 2020-12-30 r5155");
}
@@ -93,7 +93,7 @@ smartmontools build with: C++11, GCC 7.5.0
smartmontools configure arguments: [no arguments given]
)";
SmartctlVersionParser::parse_version(output, version_only, version_full);
SmartctlVersionParser::parse_version_text(output, version_only, version_full);
REQUIRE(version_only == "7.3");
REQUIRE(version_full == "7.3");
}
+1 -1
View File
@@ -118,7 +118,7 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder>
}
std::string version, version_full;
if (!SmartctlVersionParser::parse_version(output, version, version_full)) {
if (!SmartctlVersionParser::parse_version_text(output, version, version_full)) {
error_msg = _("Smartctl returned invalid output.");
break;
}
+11 -11
View File
@@ -236,7 +236,7 @@ inline bool string_trim(std::string& s, const std::string& trim_chars = " \t\r\n
/// Trim a string s from both sides (not modifying s), returning the changed string.
/// Trimming removes all trim_chars that occur on either side of the string s.
inline std::string string_trim_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
inline std::string string_trim_copy(std::string_view s, const std::string& trim_chars = " \t\r\n")
{
std::string ret(s);
string_trim(ret, trim_chars);
@@ -268,7 +268,7 @@ inline bool string_trim_left(std::string& s, const std::string& trim_chars = " \
/// Trim a string s from the left (not modifying s), returning the changed string.
/// Trimming removes all trim_chars that occur on the left side of the string s.
inline std::string string_trim_left_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
inline std::string string_trim_left_copy(std::string_view s, const std::string& trim_chars = " \t\r\n")
{
std::string ret(s);
string_trim_left(ret, trim_chars);
@@ -297,7 +297,7 @@ inline bool string_trim_right(std::string& s, const std::string& trim_chars = "
/// Trim a string s from the right (not modifying s), returning the changed string.
/// Trimming removes all trim_chars that occur on the right side of the string s.
inline std::string string_trim_right_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
inline std::string string_trim_right_copy(std::string_view s, const std::string& trim_chars = " \t\r\n")
{
std::string ret(s);
string_trim_right(ret, trim_chars);
@@ -756,7 +756,7 @@ std::string string_replace_array_copy(const std::string& s,
/// Check whether a string begins with another string
inline bool string_begins_with(const std::string& str, const std::string& substr)
inline bool string_begins_with(std::string_view str, const std::string& substr)
{
if (str.length() >= substr.length()) {
return (str.compare(0, substr.length(), substr) == 0);
@@ -767,7 +767,7 @@ inline bool string_begins_with(const std::string& str, const std::string& substr
/// Check whether a string begins with a character
inline bool string_begins_with(const std::string& str, char ch)
inline bool string_begins_with(std::string_view str, char ch)
{
return !str.empty() && str[0] == ch;
}
@@ -775,7 +775,7 @@ inline bool string_begins_with(const std::string& str, char ch)
/// Check whether a string ends with another string
inline bool string_ends_with(const std::string& str, const std::string& substr)
inline bool string_ends_with(std::string_view str, const std::string& substr)
{
if (str.length() >= substr.length()) {
return (str.compare(str.length() - substr.length(), substr.length(), substr) == 0);
@@ -786,7 +786,7 @@ inline bool string_ends_with(const std::string& str, const std::string& substr)
/// Check whether a string ends with a character
inline bool string_ends_with(const std::string& str, char ch)
inline bool string_ends_with(std::string_view str, char ch)
{
return !str.empty() && str[str.size() - 1] == ch;
}
@@ -810,7 +810,7 @@ inline bool string_any_to_unix(std::string& s)
/// Auto-detect and convert mac/dos/unix newline formats in s to unix format.
/// Returns the result string.
inline std::string string_any_to_unix_copy(const std::string& s)
inline std::string string_any_to_unix_copy(std::string_view s)
{
std::string ret(s);
string_any_to_unix(ret);
@@ -831,7 +831,7 @@ inline bool string_any_to_dos(std::string& s)
/// Auto-detect and convert mac/dos/unix newline formats in s to dos format.
/// Returns the result string.
inline std::string string_any_to_dos_copy(const std::string& s)
inline std::string string_any_to_dos_copy(std::string_view s)
{
std::string ret(s);
string_any_to_dos(ret);
@@ -853,7 +853,7 @@ inline std::string::size_type string_to_lower(std::string& s)
/// Convert s to lowercase, not modifying s, returning the changed string.
inline std::string string_to_lower_copy(const std::string& s)
inline std::string string_to_lower_copy(std::string_view s)
{
std::string ret(s);
string_to_lower(ret);
@@ -875,7 +875,7 @@ inline std::string::size_type string_to_upper(std::string& s)
/// Convert s to uppercase, not modifying s, returning the changed string.
inline std::string string_to_upper_copy(const std::string& s)
inline std::string string_to_upper_copy(std::string_view s)
{
std::string ret(s);
string_to_upper(ret);