Improve enum conversions (#4397)

* Simplify enum conversions

* Make stats and parse status enums compatible

* Simplify conversion

* Move `parse_status` and auxiliary functions to more a appropriate namespace

* More simplifications

* Use ADL for `to_stat_detail (...)` calls

* Update magic_enum version

* Customize enum ranges for reflection

* Add enum safeguards and test accordingly
This commit is contained in:
Piotr Wójcik 2024-01-26 18:35:11 +01:00 committed by GitHub
commit 9e2303222b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 192 additions and 263 deletions

View file

@ -526,7 +526,7 @@ add_subdirectory(submodules/rocksdb EXCLUDE_FROM_ALL)
include_directories(submodules/cpptoml/include)
# magic_enum
include_directories(submodules/magic_enum/include)
include_directories(submodules/magic_enum/include/magic_enum)
add_subdirectory(crypto/ed25519-donna)

View file

@ -7,17 +7,59 @@
TEST (enums, stat_type)
{
ASSERT_FALSE (nano::to_string (static_cast<nano::stat::type> (0)).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (static_cast<nano::stat::type> (0)) });
ASSERT_FALSE (nano::to_string (nano::stat::type::_last).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (nano::stat::type::_last) });
ASSERT_EQ (nano::to_string (nano::stat::type::_last), "_last");
}
TEST (enums, stat_detail)
{
ASSERT_FALSE (nano::to_string (static_cast<nano::stat::detail> (0)).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (static_cast<nano::stat::detail> (0)) });
ASSERT_FALSE (nano::to_string (nano::stat::detail::_last).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (nano::stat::detail::_last) });
ASSERT_EQ (nano::to_string (nano::stat::detail::_last), "_last");
}
TEST (enums, stat_dir)
{
ASSERT_FALSE (nano::to_string (static_cast<nano::stat::dir> (0)).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (static_cast<nano::stat::dir> (0)) });
ASSERT_FALSE (nano::to_string (nano::stat::dir::_last).empty ());
ASSERT_NO_THROW (std::string{ nano::to_string (nano::stat::dir::_last) });
ASSERT_EQ (nano::to_string (nano::stat::dir::_last), "_last");
}
TEST (enums, log_type)
{
ASSERT_FALSE (to_string (static_cast<nano::log::type> (0)).empty ());
ASSERT_NO_THROW (std::string{ to_string (static_cast<nano::log::type> (0)) });
ASSERT_FALSE (to_string (nano::log::type::_last).empty ());
ASSERT_NO_THROW (std::string{ to_string (nano::log::type::_last) });
ASSERT_EQ (to_string (nano::log::type::_last), "_last");
}
TEST (enums, log_detail)
{
ASSERT_FALSE (to_string (static_cast<nano::log::detail> (0)).empty ());
ASSERT_NO_THROW (std::string{ to_string (static_cast<nano::log::detail> (0)) });
ASSERT_FALSE (to_string (nano::log::detail::_last).empty ());
ASSERT_NO_THROW (std::string{ to_string (nano::log::detail::_last) });
ASSERT_EQ (to_string (nano::log::detail::_last), "_last");
}
TEST (enums, log_category)
{
ASSERT_FALSE (to_string (static_cast<nano::log::type> (0)).empty ());
ASSERT_NO_THROW (std::string{ to_string (static_cast<nano::log::type> (0)) });
ASSERT_FALSE (to_string (nano::log::type::_last).empty ());
ASSERT_NO_THROW (std::string{ to_string (nano::log::type::_last) });
ASSERT_EQ (to_string (nano::log::type::_last), "_last");
}

View file

@ -51,7 +51,7 @@ auto message_deserializer_success_checker (message_type & message_original) -> v
ASSERT_EQ (*deserialized_bytes, *original_bytes);
});
// This is a sanity test, to ensure the successful deserialization case passes.
ASSERT_EQ (message_deserializer->status, nano::transport::message_deserializer::parse_status::success);
ASSERT_EQ (message_deserializer->status, nano::transport::parse_status::success);
}
TEST (message_deserializer, exact_confirm_ack)

View file

@ -796,16 +796,16 @@ TEST (network, duplicate_detection)
auto & node1 = *system.add_node (node_flags);
nano::publish publish{ nano::dev::network_params.network, nano::dev::genesis };
ASSERT_EQ (0, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish));
ASSERT_EQ (0, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message));
// Publish duplicate detection through TCP
auto tcp_channel = node0.network.tcp_channels.find_node_id (node1.get_node_id ());
ASSERT_NE (nullptr, tcp_channel);
ASSERT_EQ (0, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish));
ASSERT_EQ (0, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message));
tcp_channel->send (publish);
ASSERT_TIMELY_EQ (2s, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish), 0);
ASSERT_TIMELY_EQ (2s, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message), 0);
tcp_channel->send (publish);
ASSERT_TIMELY_EQ (2s, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish), 1);
ASSERT_TIMELY_EQ (2s, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message), 1);
}
TEST (network, duplicate_revert_publish)

View file

@ -1,6 +1,3 @@
#define MAGIC_ENUM_RANGE_MIN 0
#define MAGIC_ENUM_RANGE_MAX 256
#include <nano/lib/logging_enums.hpp>
#include <nano/lib/utility.hpp>
@ -24,12 +21,7 @@ std::string_view nano::log::to_string (nano::log::level level)
const std::vector<nano::log::level> & nano::log::all_levels ()
{
static std::vector<nano::log::level> all = [] () {
std::vector<nano::log::level> result;
for (auto const & lvl : magic_enum::enum_values<nano::log::level> ())
{
result.push_back (lvl);
}
return result;
return nano::util::enum_values<nano::log::level> ();
}();
return all;
}
@ -37,19 +29,14 @@ const std::vector<nano::log::level> & nano::log::all_levels ()
const std::vector<nano::log::type> & nano::log::all_types ()
{
static std::vector<nano::log::type> all = [] () {
std::vector<nano::log::type> result;
for (auto const & lvl : magic_enum::enum_values<nano::log::type> ())
{
result.push_back (lvl);
}
return result;
return nano::util::enum_values<nano::log::type> ();
}();
return all;
}
nano::log::level nano::log::parse_level (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::level> (name);
auto value = nano::util::parse_enum<nano::log::level> (name);
if (value.has_value ())
{
return value.value ();
@ -66,7 +53,7 @@ nano::log::level nano::log::parse_level (std::string_view name)
nano::log::type nano::log::parse_type (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::type> (name);
auto value = nano::util::parse_enum<nano::log::type> (name);
if (value.has_value ())
{
return value.value ();
@ -79,7 +66,7 @@ nano::log::type nano::log::parse_type (std::string_view name)
nano::log::detail nano::log::parse_detail (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::detail> (name);
auto value = nano::util::parse_enum<nano::log::detail> (name);
if (value.has_value ())
{
return value.value ();

View file

@ -4,6 +4,8 @@
#include <string_view>
#include <vector>
#include <magic_enum.hpp>
namespace nano::log
{
enum class level
@ -74,6 +76,8 @@ enum class type
bootstrap,
bootstrap_lazy,
bootstrap_legacy,
_last // Must be the last enum
};
enum class detail
@ -110,6 +114,7 @@ enum class detail
requesting_account_or_head,
requesting_pending,
_last // Must be the last enum
};
// TODO: Additionally categorize logs by categories which can be enabled/disabled independently
@ -119,6 +124,8 @@ enum class category
work_generation,
// ...
_last // Must be the last enum
};
}
@ -140,3 +147,19 @@ nano::log::detail parse_detail (std::string_view);
std::vector<nano::log::level> const & all_levels ();
std::vector<nano::log::type> const & all_types ();
}
// Ensure that the enum_range is large enough to hold all values (including future ones)
template <>
struct magic_enum::customize::enum_range<nano::log::type>
{
static constexpr int min = 0;
static constexpr int max = 128;
};
// Ensure that the enum_range is large enough to hold all values (including future ones)
template <>
struct magic_enum::customize::enum_range<nano::log::detail>
{
static constexpr int min = 0;
static constexpr int max = 512;
};

View file

@ -1,8 +1,5 @@
#include <nano/lib/stats_enums.hpp>
#define MAGIC_ENUM_RANGE_MIN 0
#define MAGIC_ENUM_RANGE_MAX 256
#include <magic_enum.hpp>
std::string_view nano::to_string (nano::stat::type type)

View file

@ -3,6 +3,8 @@
#include <cstdint>
#include <string_view>
#include <magic_enum.hpp>
namespace nano::stat
{
/** Primary statistics type */
@ -67,6 +69,8 @@ enum class detail : uint8_t
broadcast,
cleanup,
top,
none,
success,
// processing queue
queue,
@ -172,7 +176,7 @@ enum class detail : uint8_t
invalid_frontier_req_message,
invalid_asc_pull_req_message,
invalid_asc_pull_ack_message,
message_too_big,
message_size_too_big,
outdated_version,
// tcp
@ -210,7 +214,7 @@ enum class detail : uint8_t
requests_unknown,
// duplicate
duplicate_publish,
duplicate_publish_message,
// telemetry
invalid_signature,
@ -317,3 +321,19 @@ std::string_view to_string (stat::type);
std::string_view to_string (stat::detail);
std::string_view to_string (stat::dir);
}
// Ensure that the enum_range is large enough to hold all values (including future ones)
template <>
struct magic_enum::customize::enum_range<nano::stat::type>
{
static constexpr int min = 0;
static constexpr int max = 128;
};
// Ensure that the enum_range is large enough to hold all values (including future ones)
template <>
struct magic_enum::customize::enum_range<nano::stat::detail>
{
static constexpr int min = 0;
static constexpr int max = 512;
};

View file

@ -14,6 +14,7 @@
#include <sstream>
#include <vector>
#include <magic_enum.hpp>
#include <magic_enum_containers.hpp>
namespace boost
@ -262,4 +263,38 @@ std::string to_str (T const & val)
{
return boost::lexical_cast<std::string> (val);
}
/**
* Same as `magic_enum::enum_values (...)` but ignores reserved values (starting with underscore)
*/
template <class E>
std::vector<E> enum_values ()
{
std::vector<E> result;
for (auto const & [val, name] : magic_enum::enum_entries<E> ())
{
if (!name.starts_with ('_'))
{
result.push_back (val);
}
}
return result;
}
/**
* Same as `magic_enum::enum_cast (...)` but ignores reserved values (starting with underscore).
* Case insensitive.
*/
template <class E>
std::optional<E> parse_enum (std::string_view name)
{
if (name.starts_with ('_'))
{
return std::nullopt;
}
else
{
return magic_enum::enum_cast<E> (name, magic_enum::case_insensitive);
}
}
}

View file

@ -324,7 +324,7 @@ void nano::active_transactions::cleanup_election (nano::unique_lock<nano::mutex>
lock_a.unlock ();
node.stats.inc (completion_type (*election), nano::to_stat_detail (election->behavior ()));
node.stats.inc (completion_type (*election), to_stat_detail (election->behavior ()));
vacancy_update ();
@ -461,7 +461,7 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
{
cache->fill (result.election);
}
node.stats.inc (nano::stat::type::active_started, nano::to_stat_detail (election_behavior_a));
node.stats.inc (nano::stat::type::active_started, to_stat_detail (election_behavior_a));
node.observers.active_started.notify (hash);
vacancy_update ();
}

View file

@ -5,6 +5,8 @@
#include <boost/format.hpp>
#include <magic_enum.hpp>
using namespace std::chrono;
std::chrono::milliseconds nano::election::base_latency () const
@ -693,24 +695,9 @@ std::vector<nano::vote_with_weight_info> nano::election::votes_with_weight () co
nano::stat::detail nano::to_stat_detail (nano::election_behavior behavior)
{
switch (behavior)
{
case nano::election_behavior::normal:
{
return nano::stat::detail::normal;
}
case nano::election_behavior::hinted:
{
return nano::stat::detail::hinted;
}
case nano::election_behavior::optimistic:
{
return nano::stat::detail::optimistic;
}
}
debug_assert (false, "unknown election behavior");
return {};
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (behavior));
debug_assert (value);
return value.value_or (nano::stat::detail{});
}
nano::election_behavior nano::election::behavior () const

View file

@ -1995,80 +1995,12 @@ void nano::asc_pull_ack::frontiers_payload::deserialize (nano::stream & stream)
std::string_view nano::to_string (nano::message_type type)
{
switch (type)
{
case nano::message_type::invalid:
return "invalid";
case nano::message_type::not_a_type:
return "not_a_type";
case nano::message_type::keepalive:
return "keepalive";
case nano::message_type::publish:
return "publish";
case nano::message_type::confirm_req:
return "confirm_req";
case nano::message_type::confirm_ack:
return "confirm_ack";
case nano::message_type::bulk_pull:
return "bulk_pull";
case nano::message_type::bulk_push:
return "bulk_push";
case nano::message_type::frontier_req:
return "frontier_req";
case nano::message_type::node_id_handshake:
return "node_id_handshake";
case nano::message_type::bulk_pull_account:
return "bulk_pull_account";
case nano::message_type::telemetry_req:
return "telemetry_req";
case nano::message_type::telemetry_ack:
return "telemetry_ack";
case nano::message_type::asc_pull_req:
return "asc_pull_req";
case nano::message_type::asc_pull_ack:
return "asc_pull_ack";
// default case intentionally omitted to cause warnings for unhandled enums
}
return "n/a";
return magic_enum::enum_name (type);
}
nano::stat::detail nano::to_stat_detail (nano::message_type type)
{
switch (type)
{
case nano::message_type::invalid:
return nano::stat::detail::invalid;
case nano::message_type::not_a_type:
return nano::stat::detail::not_a_type;
case nano::message_type::keepalive:
return nano::stat::detail::keepalive;
case nano::message_type::publish:
return nano::stat::detail::publish;
case nano::message_type::confirm_req:
return nano::stat::detail::confirm_req;
case nano::message_type::confirm_ack:
return nano::stat::detail::confirm_ack;
case nano::message_type::bulk_pull:
return nano::stat::detail::bulk_pull;
case nano::message_type::bulk_push:
return nano::stat::detail::bulk_push;
case nano::message_type::frontier_req:
return nano::stat::detail::frontier_req;
case nano::message_type::node_id_handshake:
return nano::stat::detail::node_id_handshake;
case nano::message_type::bulk_pull_account:
return nano::stat::detail::bulk_pull_account;
case nano::message_type::telemetry_req:
return nano::stat::detail::telemetry_req;
case nano::message_type::telemetry_ack:
return nano::stat::detail::telemetry_ack;
case nano::message_type::asc_pull_req:
return nano::stat::detail::asc_pull_req;
case nano::message_type::asc_pull_ack:
return nano::stat::detail::asc_pull_ack;
// default case intentionally omitted to cause warnings for unhandled enums
}
debug_assert (false);
return {};
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (type));
debug_assert (value);
return value.value_or (nano::stat::detail{});
}

View file

@ -468,7 +468,7 @@ private:
void nano::network::process_message (nano::message const & message, std::shared_ptr<nano::transport::channel> const & channel)
{
node.stats.inc (nano::stat::type::message, nano::to_stat_detail (message.header.type), nano::stat::dir::in);
node.stats.inc (nano::stat::type::message, to_stat_detail (message.header.type), nano::stat::dir::in);
network_message_visitor visitor{ node, channel };
message.visit (visitor);

View file

@ -17,7 +17,7 @@ nano::transport::channel::channel (nano::node & node_a) :
void nano::transport::channel::send (nano::message & message_a, std::function<void (boost::system::error_code const &, std::size_t)> const & callback_a, nano::transport::buffer_drop_policy drop_policy_a, nano::transport::traffic_type traffic_type)
{
auto buffer (message_a.to_shared_const_buffer ());
auto detail = nano::to_stat_detail (message_a.header.type);
auto detail = to_stat_detail (message_a.header.type);
auto is_droppable_by_limiter = (drop_policy_a == nano::transport::buffer_drop_policy::limiter);
auto should_pass (node.outbound_limiter.should_pass (buffer.size (), to_bandwidth_limit_type (traffic_type)));
if (!is_droppable_by_limiter || should_pass)

View file

@ -78,7 +78,7 @@ void nano::transport::inproc::channel::send_buffer (nano::shared_const_buffer co
// process message
{
node.stats.inc (nano::stat::type::message, nano::to_stat_detail (message_a->header.type), nano::stat::dir::in);
node.stats.inc (nano::stat::type::message, to_stat_detail (message_a->header.type), nano::stat::dir::in);
// create an inbound message visitor class to handle incoming messages
message_visitor_inbound visitor{ destination.network.inbound, remote_channel };

View file

@ -382,78 +382,14 @@ std::unique_ptr<nano::asc_pull_ack> nano::transport::message_deserializer::deser
return {};
}
nano::stat::detail nano::to_stat_detail (nano::transport::message_deserializer::parse_status status)
nano::stat::detail nano::transport::to_stat_detail (nano::transport::parse_status status)
{
using parse_status = nano::transport::message_deserializer::parse_status;
// Keep additional `break` for readability
switch (status)
{
case parse_status::none:
case parse_status::success:
break;
case parse_status::insufficient_work:
return stat::detail::insufficient_work;
break;
case parse_status::invalid_header:
return stat::detail::invalid_header;
break;
case parse_status::invalid_message_type:
return stat::detail::invalid_message_type;
break;
case parse_status::invalid_keepalive_message:
return stat::detail::invalid_keepalive_message;
break;
case parse_status::invalid_publish_message:
return stat::detail::invalid_publish_message;
break;
case parse_status::invalid_confirm_req_message:
return stat::detail::invalid_confirm_req_message;
break;
case parse_status::invalid_confirm_ack_message:
return stat::detail::invalid_confirm_ack_message;
break;
case parse_status::invalid_node_id_handshake_message:
return stat::detail::invalid_node_id_handshake_message;
break;
case parse_status::invalid_telemetry_req_message:
return stat::detail::invalid_telemetry_req_message;
break;
case parse_status::invalid_telemetry_ack_message:
return stat::detail::invalid_telemetry_ack_message;
break;
case parse_status::invalid_bulk_pull_message:
return stat::detail::invalid_bulk_pull_message;
break;
case parse_status::invalid_bulk_pull_account_message:
return stat::detail::invalid_bulk_pull_account_message;
break;
case parse_status::invalid_frontier_req_message:
return stat::detail::invalid_frontier_req_message;
break;
case parse_status::invalid_asc_pull_req_message:
return stat::detail::invalid_asc_pull_req_message;
break;
case parse_status::invalid_asc_pull_ack_message:
return stat::detail::invalid_asc_pull_ack_message;
break;
case parse_status::invalid_network:
return stat::detail::invalid_network;
break;
case parse_status::outdated_version:
return stat::detail::outdated_version;
break;
case parse_status::duplicate_publish_message:
return stat::detail::duplicate_publish;
break;
case parse_status::message_size_too_big:
return stat::detail::message_too_big;
break;
}
return {};
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (status));
debug_assert (value);
return value.value_or (nano::stat::detail{});
}
std::string_view nano::to_string (nano::transport::message_deserializer::parse_status status)
std::string_view nano::transport::to_string (nano::transport::parse_status status)
{
return magic_enum::enum_name (status);
}

View file

@ -10,34 +10,34 @@ namespace nano
{
namespace transport
{
enum class parse_status
{
none,
success,
insufficient_work,
invalid_header,
invalid_message_type,
invalid_keepalive_message,
invalid_publish_message,
invalid_confirm_req_message,
invalid_confirm_ack_message,
invalid_node_id_handshake_message,
invalid_telemetry_req_message,
invalid_telemetry_ack_message,
invalid_bulk_pull_message,
invalid_bulk_pull_account_message,
invalid_frontier_req_message,
invalid_asc_pull_req_message,
invalid_asc_pull_ack_message,
invalid_network,
outdated_version,
duplicate_publish_message,
message_size_too_big,
};
class message_deserializer : public std::enable_shared_from_this<nano::transport::message_deserializer>
{
public:
enum class parse_status
{
none,
success,
insufficient_work,
invalid_header,
invalid_message_type,
invalid_keepalive_message,
invalid_publish_message,
invalid_confirm_req_message,
invalid_confirm_ack_message,
invalid_node_id_handshake_message,
invalid_telemetry_req_message,
invalid_telemetry_ack_message,
invalid_bulk_pull_message,
invalid_bulk_pull_account_message,
invalid_frontier_req_message,
invalid_asc_pull_req_message,
invalid_asc_pull_ack_message,
invalid_network,
outdated_version,
duplicate_publish_message,
message_size_too_big,
};
using callback_type = std::function<void (boost::system::error_code, std::unique_ptr<nano::message>)>;
parse_status status;
@ -90,8 +90,8 @@ namespace transport
nano::vote_uniquer & vote_uniquer_m;
read_query read_op;
};
}
nano::stat::detail to_stat_detail (nano::transport::message_deserializer::parse_status);
std::string_view to_string (nano::transport::message_deserializer::parse_status);
nano::stat::detail to_stat_detail (parse_status);
std::string_view to_string (parse_status);
}
}

View file

@ -372,10 +372,10 @@ void nano::transport::tcp_server::receive_message ()
if (ec)
{
// IO error or critical error when deserializing message
node->stats.inc (nano::stat::type::error, nano::to_stat_detail (this_l->message_deserializer->status));
node->stats.inc (nano::stat::type::error, to_stat_detail (this_l->message_deserializer->status));
node->logger.debug (nano::log::type::tcp_server, "Error reading message: {}, status: {} ({})",
ec.message (),
nano::to_string (this_l->message_deserializer->status),
to_string (this_l->message_deserializer->status),
nano::util::to_str (this_l->remote_endpoint));
this_l->stop ();
@ -402,18 +402,18 @@ void nano::transport::tcp_server::received_message (std::unique_ptr<nano::messag
else
{
// Error while deserializing message
debug_assert (message_deserializer->status != transport::message_deserializer::parse_status::success);
debug_assert (message_deserializer->status != transport::parse_status::success);
node->stats.inc (nano::stat::type::error, nano::to_stat_detail (message_deserializer->status));
if (message_deserializer->status == transport::message_deserializer::parse_status::duplicate_publish_message)
node->stats.inc (nano::stat::type::error, to_stat_detail (message_deserializer->status));
if (message_deserializer->status == transport::parse_status::duplicate_publish_message)
{
node->stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish);
node->stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message);
}
else
{
// Avoid too much noise about `duplicate_publish_message` errors
node->logger.debug (nano::log::type::tcp_server, "Error deserializing message: {} ({})",
nano::to_string (message_deserializer->status),
to_string (message_deserializer->status),
nano::util::to_str (remote_endpoint));
}
}
@ -431,7 +431,7 @@ bool nano::transport::tcp_server::process_message (std::unique_ptr<nano::message
{
return false;
}
node->stats.inc (nano::stat::type::tcp_server, nano::to_stat_detail (message->header.type), nano::stat::dir::in);
node->stats.inc (nano::stat::type::tcp_server, to_stat_detail (message->header.type), nano::stat::dir::in);
debug_assert (is_undefined_connection () || is_realtime_connection () || is_bootstrap_connection ());

View file

@ -498,37 +498,7 @@ void nano::generate_cache::enable_all ()
nano::stat::detail nano::to_stat_detail (nano::process_result process_result)
{
switch (process_result)
{
case process_result::progress:
return nano::stat::detail::progress;
case process_result::bad_signature:
return nano::stat::detail::bad_signature;
case process_result::old:
return nano::stat::detail::old;
case process_result::negative_spend:
return nano::stat::detail::negative_spend;
case process_result::fork:
return nano::stat::detail::fork;
case process_result::unreceivable:
return nano::stat::detail::unreceivable;
case process_result::gap_previous:
return nano::stat::detail::gap_previous;
case process_result::gap_source:
return nano::stat::detail::gap_source;
case process_result::gap_epoch_open_pending:
return nano::stat::detail::gap_epoch_open_pending;
case process_result::opened_burn_account:
return nano::stat::detail::opened_burn_account;
case process_result::balance_mismatch:
return nano::stat::detail::balance_mismatch;
case process_result::representative_mismatch:
return nano::stat::detail::representative_mismatch;
case process_result::block_position:
return nano::stat::detail::block_position;
case process_result::insufficient_work:
return nano::stat::detail::insufficient_work;
}
debug_assert (false && "There should be always a defined nano::stat::detail that is not _last");
return nano::stat::detail::_last;
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (process_result));
debug_assert (value);
return value.value_or (nano::stat::detail{});
}

@ -1 +1 @@
Subproject commit 533c9509ef77d0fedd8be47d16dc6310877e24cf
Subproject commit e55b9b54d5cf61f8e117cafb17846d7d742dd3b4