Support parsing ATA Capabilities from JSON.

This commit is contained in:
Alexander Shaduri
2024-04-19 11:47:48 +04:00
parent 50d605b5b4
commit 6da27f7e86
13 changed files with 538 additions and 50 deletions
+24 -12
View File
@@ -248,6 +248,29 @@ class AtaStorageProperty {
[[nodiscard]] static std::string get_readable_section_name(Section s);
using ValueVariantType = std::variant<
std::monostate, ///< None
std::string, ///< Value (if it's a string)
int64_t, ///< Value (if it's an integer)
bool, ///< Value (if it's bool)
std::chrono::seconds, ///< Value in seconds (if it's time interval)
AtaStorageCapability, ///< Value (if it's a capability)
AtaStorageAttribute, ///< Value (if it's an attribute)
AtaStorageStatistic, ///< Value (if it's a statistic from devstat)
AtaStorageErrorBlock, ///< Value (if it's a error block)
AtaStorageSelftestEntry ///< Value (if it's a self-test entry)
>;
/// Constructor
AtaStorageProperty() = default;
/// Constructor
AtaStorageProperty(Section section_, ValueVariantType value_)
: section(section_), value(std::move(value_))
{ }
/// Get displayable value type name
[[nodiscard]] std::string get_storable_value_type_name() const;
@@ -297,18 +320,7 @@ class AtaStorageProperty {
std::string reported_value; ///< String representation of the value as reported
std::string readable_value; ///< User-friendly readable representation of value. if empty, use the other members.
std::variant<
std::monostate, ///< None
std::string, ///< Value (if it's a string)
int64_t, ///< Value (if it's an integer)
bool, ///< Value (if it's bool)
std::chrono::seconds, ///< Value in seconds (if it's time interval)
AtaStorageCapability, ///< Value (if it's a capability)
AtaStorageAttribute, ///< Value (if it's an attribute)
AtaStorageStatistic, ///< Value (if it's a statistic from devstat)
AtaStorageErrorBlock, ///< Value (if it's a error block)
AtaStorageSelftestEntry ///< Value (if it's a self-test entry)
> value;
ValueVariantType value; ///< Stored value
WarningLevel warning_level = WarningLevel::None; ///< Warning severity for this property
std::string warning_reason; // Warning reason (displayable)
+1 -1
View File
@@ -1613,7 +1613,7 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA
|| auto_set(p, "ata_smart_data/self_test/status/_group", "Status of the last self-test run.")
|| auto_set(p, "ata_smart_data/offline_data_collection/_group", "Drive properties related to Offline Data Collection and self-tests.")
|| auto_set(p, "ata_smart_data/capabilities/_group", "Drive properties related to SMART handling.")
|| auto_set(p, "ata_smart_data/capabilities/error_logging_supported", "Drive properties related to error logging.")
|| auto_set(p, "ata_smart_data/capabilities/error_logging_supported/_group", "Drive properties related to error logging.")
|| auto_set(p, "ata_sct_capabilities/_group", "Drive properties related to temperature information.");
break;
+1 -1
View File
@@ -274,7 +274,7 @@ hz::ExpectedVoid<SelfTestError> SelfTest::update(const std::shared_ptr<CommandEx
for (const auto& e : property_repo.get_properties()) {
if (e.section != AtaStorageProperty::Section::Internal
|| !e.is_value_type<AtaStorageSelftestEntry>() || e.get_value<AtaStorageSelftestEntry>().test_num != 0
|| e.generic_name != "ata_smart_data/self_test/status/passed")
|| e.generic_name != "ata_smart_data/self_test/status/_merged")
continue;
p = e;
}
+333 -5
View File
@@ -9,15 +9,23 @@ Copyright:
/// \weakgroup applib
/// @{
#include <format>
#include "smartctl_json_ata_parser.h"
#include <cstdint>
#include <format>
#include <tuple>
#include <vector>
#include <chrono>
#include "json/json.hpp"
#include "ata_storage_property.h"
#include "hz/debug.h"
#include "hz/string_algo.h"
// #include "smartctl_version_parser.h"
#include "hz/format_unit.h"
#include "hz/error_container.h"
#include "hz/string_num.h"
#include "smartctl_json_parser_helpers.h"
@@ -103,8 +111,24 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse(std::string_v
add_property(merged_property);
add_property(full_property);
// Info must be supported.
auto info_parse_status = parse_section_info(json_root_node);
auto health_parse_status = parse_section_health(json_root_node);
if (!info_parse_status) {
return info_parse_status;
}
// Ignore parse errors here, they are not critical.
[[maybe_unused]] auto health_parse_status = parse_section_health(json_root_node);
[[maybe_unused]] auto capabilities_parse_status = parse_section_capabilities(json_root_node);
[[maybe_unused]] auto attributes_parse_status = parse_section_attributes(json_root_node);
[[maybe_unused]] auto directory_log_parse_status = parse_section_directory_log(json_root_node);
[[maybe_unused]] auto error_log_parse_status = parse_section_error_log(json_root_node);
[[maybe_unused]] auto selftest_log_parse_status = parse_section_selftest_log(json_root_node);
[[maybe_unused]] auto selective_selftest_log_parse_status = parse_section_selective_selftest_log(json_root_node);
[[maybe_unused]] auto scttemp_log_parse_status = parse_section_scttemp_log(json_root_node);
[[maybe_unused]] auto scterc_log_parse_status = parse_section_scterc_log(json_root_node);
[[maybe_unused]] auto devstat_parse_status = parse_section_devstat(json_root_node);
[[maybe_unused]] auto sataphy_parse_status = parse_section_sataphy(json_root_node);
return {};
}
@@ -115,7 +139,10 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
{
using namespace SmartctlJsonParserHelpers;
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> info_keys = {
// This is very similar to Basic Parser, but the Basic Parser supports different drive types, while this
// one is only for ATA.
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> json_keys = {
{"model_family", _("Model Family"), string_formatter()},
{"model_name", _("Device Model"), string_formatter()},
{"serial_number", _("Serial Number"), string_formatter()},
@@ -216,16 +243,21 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
{"ata_security/string", _("ATA Security"), string_formatter()},
};
for (const auto& [key, displayable_name, retrieval_func] : info_keys) {
bool any_found = false;
for (const auto& [key, displayable_name, retrieval_func] : json_keys) {
DBG_ASSERT(retrieval_func != nullptr);
auto p = retrieval_func(json_root_node, key, displayable_name);
if (p.has_value()) { // ignore if not found
p->section = AtaStorageProperty::Section::Info;
add_property(p.value());
any_found = true;
}
}
if (!any_found) {
return hz::Unexpected(SmartctlParserError::KeyNotFound, "No keys info found in JSON data.");
}
return {};
}
@@ -254,4 +286,300 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_healt
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_capabilities(const nlohmann::json& json_root_node)
{
using namespace SmartctlJsonParserHelpers;
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> json_keys = {
// // FIXME Remove?
// {"ata_smart_data/capabilities/_group", _("SMART Capabilities"),
// conditional_formatter("ata_smart_data/capabilities/values",
// AtaStorageProperty{} )},
//
// // FIXME Remove?
// {"ata_smart_data/capabilities/error_logging_supported/_group", _("Error Logging Capabilities"),
// conditional_formatter("ata_smart_data/capabilities/error_logging_supported",
// AtaStorageProperty{} )},
//
{"ata_smart_data/offline_data_collection/status/_auto_enabled", _("Automatic offline data collection status"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<int64_t>(root_node, "ata_smart_data/offline_data_collection/status/value");
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = static_cast<bool>(value_val.value() & 0x80); // taken from ataprint.cpp
p.readable_value = p.get_value<bool>() ? _("Enabled") : _("Disabled");
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
// Last self-test status
{"ata_smart_data/offline_data_collection/status/value/_decoded", "Last offline data collection status",
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<uint8_t>(root_node, "ata_smart_data/offline_data_collection/status/value");
if (value_val.has_value()) {
std::string status_str;
switch (value_val.value() & 0x7f) {
// Data from smartmontools/ataprint.cpp
case 0x00: status_str = _("Never started"); break;
case 0x02: status_str = _("Completed without error"); break;
case 0x03: status_str = (value_val.value() == 0x03 ? _("In progress") : _("In reserved state")); break;
case 0x04: status_str = _("Suspended by an interrupting command from host"); break;
case 0x05: status_str = _("Aborted by an interrupting command from host"); break;
case 0x06: status_str = _("Aborted by the device with a fatal error"); break;
default: status_str = ((value_val.value() & 0x7f) > 0x40 ? _("In vendor-specific state") : _("In reserved state")); break;
}
AtaStorageProperty p;
p.section = AtaStorageProperty::Section::Capabilities;
p.set_name(key, key, displayable_name);
p.value = status_str;
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/offline_data_collection/completion_seconds", _("Time to complete offline data collection"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<int64_t>(root_node, key);
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = std::chrono::seconds(value_val.value());
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/self_test/status/value/_decoded", _("Self-test execution status"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
// Testing:
// "status": {
// "value": 249,
// "string": "in progress, 90% remaining",
// "remaining_percent": 90
// },
// Not testing:
// "status": {
// "value": 0,
// "string": "completed without error",
// "passed": true
// },
AtaStorageSelftestEntry::Status status = AtaStorageSelftestEntry::Status::Unknown;
auto value_val = get_node_data<uint8_t>(root_node, "ata_smart_data/self_test/status/value");
if (value_val.has_value()) {
switch (value_val.value() >> 4) {
// Data from smartmontools/ataprint.cpp
case 0x0: status = AtaStorageSelftestEntry::Status::CompletedNoError; break;
case 0x1: status = AtaStorageSelftestEntry::Status::AbortedByHost; break;
case 0x2: status = AtaStorageSelftestEntry::Status::Interrupted; break;
case 0x3: status = AtaStorageSelftestEntry::Status::FatalOrUnknown; break;
case 0x4: status = AtaStorageSelftestEntry::Status::ComplUnknownFailure; break;
case 0x5: status = AtaStorageSelftestEntry::Status::ComplElectricalFailure; break;
case 0x6: status = AtaStorageSelftestEntry::Status::ComplServoFailure; break;
case 0x7: status = AtaStorageSelftestEntry::Status::ComplReadFailure; break;
case 0x8: status = AtaStorageSelftestEntry::Status::ComplHandlingDamage; break;
// Special case
case 0xf: status = AtaStorageSelftestEntry::Status::InProgress; break;
default: status = AtaStorageSelftestEntry::Status::Reserved; break;
}
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = AtaStorageSelftestEntry::get_readable_status_name(status);
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/self_test/status/remaining_percent", _("Self-test remaining percentage"),
custom_string_formatter<int64_t>([](int64_t value)
{
return std::format("{} %", value);
})
},
{"ata_smart_data/capabilities/self_tests_supported", _("Self-tests supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/exec_offline_immediate_supported", _("Offline immediate test supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/offline_is_aborted_upon_new_cmd", _("Abort offline collection on new command"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/offline_surface_scan_supported", _("Offline surface scan supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/conveyance_self_test_supported", _("Conveyance self-test supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/selective_self_test_supported", _("Selective self-test supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/self_test/polling_minutes/short", _("Short self-test status recommended polling time"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<int64_t>(root_node, key);
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = std::chrono::minutes(value_val.value());
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/self_test/polling_minutes/extended", _("Extended self-test status recommended polling time"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<int64_t>(root_node, key);
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = std::chrono::minutes(value_val.value());
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/self_test/polling_minutes/conveyance", _("Conveyance self-test status recommended polling time"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_data<int64_t>(root_node, key);
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = std::chrono::minutes(value_val.value());
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_smart_data/capabilities/attribute_autosave_enabled", _("Saves SMART data before entering power-saving mode"), bool_formatter(_("Enabled"), _("Disabled"))},
{"ata_smart_data/capabilities/error_logging_supported", _("Error logging supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_smart_data/capabilities/gp_logging_supported", _("General purpose logging supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_sct_capabilities/_supported", _("SCT capabilities supported"),
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto value_val = get_node_exists(root_node, "ata_sct_capabilities");
if (value_val.has_value()) {
AtaStorageProperty p;
p.set_name(key, key, displayable_name);
p.value = value_val.value();
return p;
}
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
}
},
{"ata_sct_capabilities/error_recovery_control_supported", _("SCT error recovery control supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_sct_capabilities/feature_control_supported", _("SCT feature control supported"), bool_formatter(_("Yes"), _("No"))},
{"ata_sct_capabilities/data_table_supported", _("SCT data table supported"), bool_formatter(_("Yes"), _("No"))},
};
for (const auto& [key, displayable_name, retrieval_func] : json_keys) {
DBG_ASSERT(retrieval_func != nullptr);
auto p = retrieval_func(json_root_node, key, displayable_name);
if (p.has_value()) { // ignore if not found
p->section = AtaStorageProperty::Section::Capabilities;
add_property(p.value());
}
}
return {};
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_attributes(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_directory_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_error_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_selftest_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_selective_selftest_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_scttemp_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_scterc_log(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_devstat(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_sataphy(const nlohmann::json& json_root_node)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_internal_capabilities(AtaStorageProperty& cap_prop)
{
return hz::ExpectedVoid<SmartctlParserError>();
}
/// @}
+39 -1
View File
@@ -12,10 +12,14 @@ Copyright:
#ifndef SMARTCTL_JSON_ATA_PARSER_H
#define SMARTCTL_JSON_ATA_PARSER_H
#include "smartctl_parser.h"
#include <string>
#include <string_view>
#include "json/json.hpp"
#include "smartctl_parser.h"
#include "hz/error_container.h"
@@ -36,6 +40,40 @@ class SmartctlJsonAtaParser : public SmartctlParser {
/// Parse the health section (root node), filling in the properties
hz::ExpectedVoid<SmartctlParserError> parse_section_health(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_capabilities(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_attributes(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_directory_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_error_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_selftest_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_selective_selftest_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_scttemp_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_scterc_log(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_devstat(const nlohmann::json& json_root_node);
/// Parse a section from json data
hz::ExpectedVoid<SmartctlParserError> parse_section_sataphy(const nlohmann::json& json_root_node);
/// Check the capabilities for internal properties we can use.
hz::ExpectedVoid<SmartctlParserError> parse_section_internal_capabilities(AtaStorageProperty& cap_prop);
};
+13 -5
View File
@@ -9,22 +9,30 @@ Copyright:
/// \weakgroup applib
/// @{
#include "smartctl_json_basic_parser.h"
// #include "local_glibmm.h"
//#include <clocale> // localeconv
//#include <cstdint>
//#include <utility>
#include <cstdint>
#include <format>
#include <string_view>
#include <tuple>
#include <vector>
// #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/string_algo.h" // string_*
#include "hz/string_num.h" // string_is_numeric, number_to_string
//#include "hz/debug.h" // debug_*
#include "json/json.hpp"
#include "hz/error_container.h"
//#include "app_pcrecpp.h"
//#include "smartctl_text_ata_parser.h"
//#include "ata_storage_property_descr.h"
#include "ata_storage_property.h"
#include "smartctl_json_parser_helpers.h"
#include "smartctl_parser_types.h"
//#include "smartctl_version_parser.h"
#include "smartctl_json_basic_parser.h"
+2
View File
@@ -15,6 +15,8 @@ Copyright:
//#include <string>
//#include <vector>
#include "json/json.hpp"
#include "smartctl_parser.h"
+81 -12
View File
@@ -12,12 +12,17 @@ Copyright:
#ifndef SMARTCTL_JSON_PARSER_HELPERS_H
#define SMARTCTL_JSON_PARSER_HELPERS_H
#include <cstddef>
#include <format>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include "json/json.hpp"
#include "hz/debug.h"
#include "hz/string_algo.h"
#include "smartctl_parser_types.h"
#include "smartctl_version_parser.h"
#include "hz/format_unit.h"
#include "hz/error_container.h"
@@ -38,10 +43,9 @@ enum class SmartctlJsonParserError {
namespace SmartctlJsonParserHelpers {
/// Get json node data. The path is slash-separated string.
/// \throws std::runtime_error If not found or one of the paths is not an object
template<typename T>
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, const std::string& path)
/// Get node from json data. The path is slash-separated string.
[[nodiscard]] inline hz::ExpectedValue<nlohmann::json, SmartctlJsonParserError>
get_node(const nlohmann::json& root, std::string_view path)
{
using namespace std::literals;
@@ -63,13 +67,7 @@ template<typename T>
if (auto iter = curr->find(comp_name); iter != curr->end()) { // path component exists
const auto& jval = iter.value();
if (comp_index + 1 == components.size()) { // it's the "value" component
try {
return jval.get<T>(); // may throw json::type_error
}
catch (nlohmann::json::type_error& ex) {
return hz::Unexpected(SmartctlJsonParserError::TypeError,
std::format("Cannot get node data \"{}\", component \"{}\" has wrong type: {}.", path, comp_name, ex.what()));
}
return jval;
}
// continue to the next component
curr = &jval;
@@ -85,10 +83,32 @@ template<typename T>
/// Get json node data. The path is slash-separated string.
/// \return SmartctlJsonParserError on error.
template<typename T>
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, std::string_view path)
{
auto node_result = get_node(root, path);
if (!node_result) {
return hz::UnexpectedFrom(node_result);
}
try {
return node_result.value().get<T>(); // may throw json::type_error
}
catch (nlohmann::json::type_error& ex) {
return hz::Unexpected(SmartctlJsonParserError::TypeError,
std::format("Cannot get node data \"{}\", component has wrong type: {}.", path, ex.what()));
}
}
/// Get json node data. The path is slash-separated string.
/// If the data is not is found, the default value is returned.
template<typename T>
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, const std::string& path, const T& default_value)
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, std::string_view path, const T& default_value)
{
auto expected_data = get_node_data<T>(root, path);
@@ -110,6 +130,31 @@ template<typename T>
/// Check if json node exists. The path is slash-separated string.
[[nodiscard]] inline hz::ExpectedValue<bool, SmartctlJsonParserError>
get_node_exists(const nlohmann::json& root, std::string_view path)
{
auto node_result = get_node(root, path);
if (node_result.has_value()) {
return true;
}
switch (node_result.error().data()) {
case SmartctlJsonParserError::PathNotFound:
return false;
case SmartctlJsonParserError::UnexpectedObjectInPath:
case SmartctlJsonParserError::EmptyPath:
case SmartctlJsonParserError::InternalError:
case SmartctlJsonParserError::TypeError:
break;
}
return hz::UnexpectedFrom(node_result);
}
/// A signature for a property retrieval function.
using PropertyRetrievalFunc = std::function<
auto(const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
@@ -137,6 +182,30 @@ inline auto string_formatter()
/// Return a lambda which returns a return_property if conditional_path exists.
/// If the path doesn't exist, an error is returned.
inline auto conditional_formatter(const std::string_view conditional_path, AtaStorageProperty return_property)
{
return [conditional_path, return_property](const nlohmann::json& root_node, const std::string& key, [[maybe_unused]] const std::string& displayable_name) mutable
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
{
auto node_exists_result = get_node_exists(root_node, conditional_path);
if (!node_exists_result.has_value()) {
return hz::Unexpected(SmartctlParserError::DataError, node_exists_result.error().message());
}
if (node_exists_result.value()) {
return_property.generic_name = key;
return_property.displayable_name = displayable_name;
return return_property;
}
return hz::Unexpected(SmartctlParserError::InternalError, std::format("Error getting key {} from JSON data.", key));
};
}
/// Return a lambda which retrieves a key value as a bool (formatted according to parameters), and sets it as a property.
inline auto bool_formatter(const std::string_view& true_str, const std::string_view& false_str)
{
-1
View File
@@ -13,7 +13,6 @@ Copyright:
#define SMARTCTL_PARSER_H
#include <string_view>
#include <vector>
#include <memory>
#include "ata_storage_property.h"
+10 -4
View File
@@ -9,20 +9,26 @@ Copyright:
/// \weakgroup applib
/// @{
#include "smartctl_text_ata_parser.h"
// #include "local_glibmm.h"
#include <clocale> // localeconv
#include <cstdint>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
#include "ata_storage_property.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_text_ata_parser.h"
#include "ata_storage_property_descr.h"
//#include "ata_storage_property_descr.h"
// #include "warning_colors.h"
#include "smartctl_parser_types.h"
#include "smartctl_version_parser.h"
#include "smartctl_text_parser_helper.h"
@@ -1042,7 +1048,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_
cap_prop.generic_name = "ata_smart_data/capabilities/_group";
} else if (re_error_log_cap_group.PartialMatch(cap_prop.reported_name)) {
cap_prop.generic_name = "ata_smart_data/capabilities/error_logging_supported";
cap_prop.generic_name = "ata_smart_data/capabilities/error_logging_supported/_group";
} else if (re_sct_cap_group.PartialMatch(cap_prop.reported_name)) {
cap_prop.generic_name = "ata_sct_capabilities/_group";
@@ -1059,7 +1065,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_
AtaStorageProperty p;
p.section = AtaStorageProperty::Section::Internal;
p.set_name("ata_smart_data/self_test/status/passed");
p.set_name("ata_smart_data/self_test/status/_merged");
AtaStorageSelftestEntry sse;
sse.test_num = 0;
+11 -7
View File
@@ -9,30 +9,34 @@ Copyright:
/// \weakgroup applib
/// @{
#include "smartctl_text_basic_parser.h"
// #include "local_glibmm.h"
//#include <clocale> // localeconv
//#include <cstdint>
//#include <utility>
#include <cstdint>
#include <string>
#include <string_view>
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
#include "ata_storage_property.h"
#include "hz/string_algo.h" // string_*
//#include "hz/string_num.h" // string_is_numeric, number_to_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_parser_types.h"
#include "smartctl_version_parser.h"
#include "smartctl_text_basic_parser.h"
#include "hz/string_num.h"
#include "smartctl_text_parser_helper.h"
hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string_view smartctl_output)
{
// perform any2unix
std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output));
const std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output));
if (output.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n");
@@ -192,7 +196,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string
std::string size;
if (app_pcre_match("/^User Capacity:[ \\t]*(.*)$/mi", output, &size)) {
int64_t bytes = 0;
std::string readable_size = SmartctlTextParserHelper::parse_byte_size(size, bytes, false);
const std::string readable_size = SmartctlTextParserHelper::parse_byte_size(size, bytes, false);
AtaStorageProperty p;
p.set_name("User Capacity", "user_capacity/bytes", "Capacity");
p.reported_value = size;
+1
View File
@@ -6,6 +6,7 @@ Copyright:
#ifndef STORAGE_PROPERTY_REPOSITORY_H
#define STORAGE_PROPERTY_REPOSITORY_H
#include <string>
#include <vector>
#include "ata_storage_property.h"
+22 -1
View File
@@ -15,6 +15,7 @@ Copyright:
#include <source_location>
#include <string>
#include <tl/expected.hpp>
#include <utility>
// #include <stacktrace>
@@ -94,14 +95,25 @@ class ErrorContainer {
/// ExpectedValue is a wrapper around tl::expected that uses ErrorContainer as the error type.
template<typename ValueType, typename ErrorType>
using ExpectedValue = tl::expected<ValueType, ErrorContainer<ErrorType>>;
/// ExpectedVoid is a wrapper around tl::expected that uses ErrorContainer as the error type and void as value.
template<typename ErrorType>
using ExpectedVoid = tl::expected<void, ErrorContainer<ErrorType>>;
/// Unexpected creates an unexpected value with an ErrorContainer as error.
template<typename ErrorContainerWithData>
auto UnexpectedFromContainer(const ErrorContainerWithData& container)
{
return tl::unexpected(container);
}
/// Unexpected creates an unexpected value with an ErrorContainer.
template<typename ErrorData>
auto Unexpected(ErrorData&& data, std::string error_message,
const std::source_location& loc = std::source_location::current()
@@ -109,13 +121,22 @@ auto Unexpected(ErrorData&& data, std::string error_message,
)
{
return tl::unexpected(ErrorContainer<ErrorData>(std::forward<ErrorData>(data),
std::move(error_message),
std::move(error_message),
loc
// trace
));
}
/// UnexpectedFrom creates an unexpected value from ExpectedValue or ExpectedVoid
/// containing an error.
template<typename ExpectedValueT>
auto UnexpectedFrom(ExpectedValueT unexpected_value)
{
return tl::unexpected(unexpected_value.error());
}
/*
std::ostream& operator<< (std::ostream& os, const std::source_location& location)