From 0621841912683f6de7ce6c7893f70a002ed64ffd Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 22 Jul 2026 17:07:18 -0400 Subject: [PATCH] make TER changes --- include/xrpl/protocol/TER.h | 129 ++++++++++++++++++++++++++++++--- src/libxrpl/protocol/TER.cpp | 6 +- src/test/protocol/TER_test.cpp | 40 ++++++++++ 3 files changed, 160 insertions(+), 15 deletions(-) diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 730d021254..891bfd5962 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -643,44 +643,149 @@ class CanCvtToTER : public std::true_type { }; -// TER allows all of the subsets. -using TER = TERSubset; +// TERRaw allows all of the subsets (the plain numeric code, no reason string). +using TERRaw = TERSubset; + +//------------------------------------------------------------------------------ +// Forward declaration required by TER constructors below (TER.cpp provides the +// definition). The full public declaration is repeated after the struct. +std::string +transHuman(TERRaw code); + +//------------------------------------------------------------------------------ +// TER: a transaction result code paired with a human-readable reason string. +// +// When no reason is supplied the standard description from transHuman() is used +// automatically, so existing "return tesSUCCESS;" and "return tecNO_DST;" +// call sites continue to compile and gain a default reason for free. +// +// To attach a more specific reason at a particular return site write: +// return {tecNO_DST, "destination account has been deleted"}; +// +struct TER +{ + TERRaw code; + std::string reason; + + // Default-construct to tesSUCCESS with its standard description. + TER() : code(tesSUCCESS), reason(transHuman(TERRaw{tesSUCCESS})) + { + } + + TER(TER const&) = default; + TER(TER&&) = default; + TER& + operator=(TER const&) = default; + TER& + operator=(TER&&) = default; + + // --- construction from TERRaw --- + + // Without explicit reason: populate from transHuman. + TER(TERRaw c) : code(c), reason(transHuman(c)) // NOLINT(google-explicit-constructor) + { + } + + // With explicit reason. + TER(TERRaw c, std::string r) : code(c), reason(std::move(r)) + { + } + + // --- construction from any TE*codes enum or NotTEC --- + + // Without explicit reason: populate from transHuman. + template + TER(T c) // NOLINT(google-explicit-constructor) + requires(CanCvtToTER>>::value) + : code(c), reason(transHuman(TERRaw{c})) + { + } + + // With explicit reason. + template + TER(T c, std::string r) + requires(CanCvtToTER>>::value) + : code(c), reason(std::move(r)) + { + } + + // Build from a raw integer (used by transCode). + static TER + fromInt(int from) + { + return TER(TERRaw::fromInt(from)); + } + + // True when the code is anything other than tesSUCCESS. + explicit + operator bool() const + { + return static_cast(code); + } + + // Allow assignment to json::Objects without casting. + operator json::Value() const // NOLINT(google-explicit-constructor) + { + return json::Value{TERtoInt(code)}; + } + + // Allow implicit conversion to TERRaw for backward compatibility with + // functions that now take TERRaw (like transResultInfo, transToken, etc). + operator TERRaw() const // NOLINT(google-explicit-constructor) + { + return code; + } + + friend std::ostream& + operator<<(std::ostream& os, TER const& rhs) + { + return os << TERtoInt(rhs.code); + } +}; + +// Expose the underlying integer so the existing template comparison operators +// (operator==, !=, <, <=, >, >=) continue to work with TER values. +inline TERUnderlyingType +TERtoInt(TER const& v) +{ + return TERtoInt(v.code); +} //------------------------------------------------------------------------------ inline bool -isTelLocal(TER x) noexcept +isTelLocal(TER const& x) noexcept { return (x >= telLOCAL_ERROR && x < temMALFORMED); } inline bool -isTemMalformed(TER x) noexcept +isTemMalformed(TER const& x) noexcept { return (x >= temMALFORMED && x < tefFAILURE); } inline bool -isTefFailure(TER x) noexcept +isTefFailure(TER const& x) noexcept { return (x >= tefFAILURE && x < terRETRY); } inline bool -isTerRetry(TER x) noexcept +isTerRetry(TER const& x) noexcept { return (x >= terRETRY && x < tesSUCCESS); } inline bool -isTesSuccess(TER x) noexcept +isTesSuccess(TER const& x) noexcept { - // Makes use of TERSubset::operator bool() + // Makes use of TER::operator bool() return !x; } inline bool -isTecClaim(TER x) noexcept +isTecClaim(TER const& x) noexcept { return (x >= tecCLAIM); } @@ -689,13 +794,13 @@ std::unordered_map transCode(std::string const& token); diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index c2167d58ce..611ae17cc0 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -233,7 +233,7 @@ transResults() } bool -transResultInfo(TER code, std::string& token, std::string& text) +transResultInfo(TERRaw code, std::string& token, std::string& text) { auto& results = transResults(); @@ -248,7 +248,7 @@ transResultInfo(TER code, std::string& token, std::string& text) } std::string -transToken(TER code) +transToken(TERRaw code) { std::string token; std::string text; @@ -257,7 +257,7 @@ transToken(TER code) } std::string -transHuman(TER code) +transHuman(TERRaw code) { std::string token; std::string text; diff --git a/src/test/protocol/TER_test.cpp b/src/test/protocol/TER_test.cpp index 77f12ff2b7..e7c18f1f2c 100644 --- a/src/test/protocol/TER_test.cpp +++ b/src/test/protocol/TER_test.cpp @@ -235,12 +235,52 @@ struct TER_test : public beast::unit_test::Suite testIterate(kTers, *this); } + void + testReason() + { + testcase("Reason"); + + // 1. Implicit conversion from enum uses transHuman + { + TER t = tecNO_DST; + BEAST_EXPECT(t.code == tecNO_DST); + BEAST_EXPECT(t.reason == transHuman(TERRaw{tecNO_DST})); + } + + // 2. Explicit reason + { + TER t = {tecNO_DST, "custom reason"}; + BEAST_EXPECT(t.code == tecNO_DST); + BEAST_EXPECT(t.reason == "custom reason"); + } + + // 3. Default constructor uses tesSUCCESS description + { + TER t; + BEAST_EXPECT(t.code == tesSUCCESS); + BEAST_EXPECT(t.reason == transHuman(TERRaw{tesSUCCESS})); + } + + // 4. Copy and move + { + TER t1 = {tecPATH_DRY, "reason 1"}; + TER t2 = t1; + BEAST_EXPECT(t2.code == tecPATH_DRY); + BEAST_EXPECT(t2.reason == "reason 1"); + + TER t3 = std::move(t1); + BEAST_EXPECT(t3.code == tecPATH_DRY); + BEAST_EXPECT(t3.reason == "reason 1"); + } + } + void run() override { testTransResultInfo(); testConversion(); testComparison(); + testReason(); } };