refactor: Make getLedgerIndex return std::expected instead of throwing (#2788)

Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
Co-authored-by: Sergey Kuznetsov <kuzzz99@gmail.com>
This commit is contained in:
emrearıyürek
2025-11-20 18:46:15 +01:00
committed by GitHub
parent e3170203de
commit 2cd8226a11
30 changed files with 290 additions and 89 deletions

View File

@@ -23,10 +23,13 @@
#include <boost/json.hpp>
#include <boost/json/object.hpp>
#include <xrpl/beast/core/LexicalCast.h>
#include <algorithm>
#include <cctype>
#include <charconv>
#include <concepts>
#include <expected>
#include <stdexcept>
#include <string>
@@ -96,12 +99,11 @@ removeSecret(boost::json::object const& object)
*
* @tparam Type The type to cast to
* @param value The JSON value to cast
* @return Value casted to the requested type
* @throws logic_error if the underlying number is neither int64 nor uint64
* @return Value casted to the requested type or an error message
*/
template <std::integral Type>
Type
integralValueAs(boost::json::value const& value)
std::expected<Type, std::string>
tryIntegralValueAs(boost::json::value const& value)
{
if (value.is_uint64())
return static_cast<Type>(value.as_uint64());
@@ -109,29 +111,49 @@ integralValueAs(boost::json::value const& value)
if (value.is_int64())
return static_cast<Type>(value.as_int64());
throw std::logic_error("Value neither uint64 nor int64");
return std::unexpected("Value neither uint64 nor int64");
}
/**
* @brief Detects the type of number stored in value and casts it back to the requested Type.
* @note This conversion can possibly cause wrapping around or UB. Use with caution.
*
* @tparam Type The type to cast to
* @param value The JSON value to cast
* @return Value casted to the requested type
* @throws logic_error if the underlying number is neither int64 nor uint64
*/
template <std::integral Type>
Type
integralValueAs(boost::json::value const& value)
{
auto expectedResult = tryIntegralValueAs<Type>(value);
if (expectedResult.has_value())
return *expectedResult;
throw std::logic_error(std::move(expectedResult).error());
}
/**
* @brief Extracts ledger index from a JSON value which can be either a number or a string.
*
* @param value The JSON value to extract ledger index from
* @return An optional containing the ledger index if it is a number; std::nullopt otherwise
* @throws logic_error comes from integralValueAs if the underlying number is neither int64 nor uint64
* @throws std::invalid_argument or std::out_of_range if the string cannot be converted to a number
* @return The extracted ledger index or an error message
*/
[[nodiscard]] inline std::optional<uint32_t>
[[nodiscard]] inline std::expected<uint32_t, std::string>
getLedgerIndex(boost::json::value const& value)
{
std::optional<uint32_t> ledgerIndex;
if (not value.is_string()) {
ledgerIndex = util::integralValueAs<uint32_t>(value);
return tryIntegralValueAs<uint32_t>(value);
} else if (value.as_string() != "validated") {
ledgerIndex = std::stoi(value.as_string().c_str());
uint32_t ledgerIndex{};
if (beast::lexicalCastChecked(ledgerIndex, value.as_string().c_str()))
return ledgerIndex;
else
return std::unexpected("Invalid ledger index string");
} else {
return std::unexpected("'validated' ledger index is requested");
}
return ledgerIndex;
}
} // namespace util