Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/util.cpp
Show First 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | UniValue JSONRPCTransactionError(TransactionError terr, | ||||
if (err_string.length() > 0) { | if (err_string.length() > 0) { | ||||
return JSONRPCError(RPCErrorFromTransactionError(terr), err_string); | return JSONRPCError(RPCErrorFromTransactionError(terr), err_string); | ||||
} else { | } else { | ||||
return JSONRPCError(RPCErrorFromTransactionError(terr), | return JSONRPCError(RPCErrorFromTransactionError(terr), | ||||
TransactionErrorString(terr)); | TransactionErrorString(terr)); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* A pair of strings that can be aligned (through padding) with other Sections | |||||
* later on | |||||
*/ | |||||
struct Section { | struct Section { | ||||
Section(const std::string &left, const std::string &right) | Section(const std::string &left, const std::string &right) | ||||
: m_left{left}, m_right{right} {} | : m_left{left}, m_right{right} {} | ||||
const std::string m_left; | const std::string m_left; | ||||
const std::string m_right; | const std::string m_right; | ||||
}; | }; | ||||
/** | |||||
* Keeps track of RPCArgs by transforming them into sections for the purpose | |||||
* of serializing everything to a single string | |||||
*/ | |||||
struct Sections { | struct Sections { | ||||
std::vector<Section> m_sections; | std::vector<Section> m_sections; | ||||
size_t m_max_pad{0}; | size_t m_max_pad{0}; | ||||
void PushSection(const Section &s) { | void PushSection(const Section &s) { | ||||
m_max_pad = std::max(m_max_pad, s.m_left.size()); | m_max_pad = std::max(m_max_pad, s.m_left.size()); | ||||
m_sections.push_back(s); | m_sections.push_back(s); | ||||
} | } | ||||
/** | |||||
* Serializing RPCArgs depends on the outer type. Only arrays and | |||||
* dictionaries can be nested in json. The top-level outer type is "named | |||||
* arguments", a mix between a dictionary and arrays. | |||||
*/ | |||||
enum class OuterType { | enum class OuterType { | ||||
ARR, | ARR, | ||||
OBJ, | OBJ, | ||||
// Only set on first recursion | // Only set on first recursion | ||||
NAMED_ARG, | NAMED_ARG, | ||||
}; | }; | ||||
/** | |||||
* Recursive helper to translate an RPCArg into sections | |||||
*/ | |||||
void Push(const RPCArg &arg, const size_t current_indent = 5, | void Push(const RPCArg &arg, const size_t current_indent = 5, | ||||
const OuterType outer_type = OuterType::NAMED_ARG) { | const OuterType outer_type = OuterType::NAMED_ARG) { | ||||
const auto indent = std::string(current_indent, ' '); | const auto indent = std::string(current_indent, ' '); | ||||
const auto indent_next = std::string(current_indent + 2, ' '); | const auto indent_next = std::string(current_indent + 2, ' '); | ||||
// Dictionary keys must have a name | |||||
const bool push_name{outer_type == OuterType::OBJ}; | |||||
switch (arg.m_type) { | switch (arg.m_type) { | ||||
case RPCArg::Type::STR_HEX: | case RPCArg::Type::STR_HEX: | ||||
case RPCArg::Type::STR: | case RPCArg::Type::STR: | ||||
case RPCArg::Type::NUM: | case RPCArg::Type::NUM: | ||||
case RPCArg::Type::AMOUNT: | case RPCArg::Type::AMOUNT: | ||||
case RPCArg::Type::BOOL: { | case RPCArg::Type::BOOL: { | ||||
// Nothing more to do for non-recursive types on first recursion | // Nothing more to do for non-recursive types on first recursion | ||||
if (outer_type == OuterType::NAMED_ARG) { | if (outer_type == OuterType::NAMED_ARG) { | ||||
return; | return; | ||||
} | } | ||||
auto left = indent; | auto left = indent; | ||||
if (arg.m_type_str.size() != 0 && | if (arg.m_type_str.size() != 0 && push_name) { | ||||
outer_type == OuterType::OBJ) { | |||||
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0); | left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0); | ||||
} else { | } else { | ||||
left += outer_type == OuterType::OBJ | left += push_name ? arg.ToStringObj(/* oneline */ false) | ||||
? arg.ToStringObj(/* oneline */ false) | |||||
: arg.ToString(/* oneline */ false); | : arg.ToString(/* oneline */ false); | ||||
} | } | ||||
left += ","; | left += ","; | ||||
PushSection({left, arg.ToDescriptionString()}); | PushSection({left, arg.ToDescriptionString()}); | ||||
break; | break; | ||||
} | } | ||||
case RPCArg::Type::OBJ: | case RPCArg::Type::OBJ: | ||||
case RPCArg::Type::OBJ_USER_KEYS: { | case RPCArg::Type::OBJ_USER_KEYS: { | ||||
const auto right = outer_type == OuterType::NAMED_ARG | const auto right = outer_type == OuterType::NAMED_ARG | ||||
? "" | ? "" | ||||
: arg.ToDescriptionString(); | : arg.ToDescriptionString(); | ||||
PushSection({indent + "{", right}); | PushSection({indent + | ||||
(push_name ? "\"" + arg.m_name + "\": " : "") + | |||||
"{", | |||||
right}); | |||||
for (const auto &arg_inner : arg.m_inner) { | for (const auto &arg_inner : arg.m_inner) { | ||||
Push(arg_inner, current_indent + 2, OuterType::OBJ); | Push(arg_inner, current_indent + 2, OuterType::OBJ); | ||||
} | } | ||||
if (arg.m_type != RPCArg::Type::OBJ) { | if (arg.m_type != RPCArg::Type::OBJ) { | ||||
PushSection({indent_next + "...", ""}); | PushSection({indent_next + "...", ""}); | ||||
} | } | ||||
PushSection( | PushSection( | ||||
{indent + "}" + | {indent + "}" + | ||||
(outer_type != OuterType::NAMED_ARG ? "," : ""), | (outer_type != OuterType::NAMED_ARG ? "," : ""), | ||||
""}); | ""}); | ||||
break; | break; | ||||
} | } | ||||
case RPCArg::Type::ARR: { | case RPCArg::Type::ARR: { | ||||
auto left = indent; | auto left = indent; | ||||
left += outer_type == OuterType::OBJ | left += push_name ? "\"" + arg.m_name + "\": " : ""; | ||||
? "\"" + arg.m_name + "\": " | |||||
: ""; | |||||
left += "["; | left += "["; | ||||
const auto right = outer_type == OuterType::NAMED_ARG | const auto right = outer_type == OuterType::NAMED_ARG | ||||
? "" | ? "" | ||||
: arg.ToDescriptionString(); | : arg.ToDescriptionString(); | ||||
PushSection({left, right}); | PushSection({left, right}); | ||||
for (const auto &arg_inner : arg.m_inner) { | for (const auto &arg_inner : arg.m_inner) { | ||||
Push(arg_inner, current_indent + 2, OuterType::ARR); | Push(arg_inner, current_indent + 2, OuterType::ARR); | ||||
} | } | ||||
PushSection({indent_next + "...", ""}); | PushSection({indent_next + "...", ""}); | ||||
PushSection( | PushSection( | ||||
{indent + "]" + | {indent + "]" + | ||||
(outer_type != OuterType::NAMED_ARG ? "," : ""), | (outer_type != OuterType::NAMED_ARG ? "," : ""), | ||||
""}); | ""}); | ||||
break; | break; | ||||
} | } | ||||
// no default case, so the compiler can warn about missing cases | // no default case, so the compiler can warn about missing cases | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Concatenate all sections with proper padding | |||||
*/ | |||||
std::string ToString() const { | std::string ToString() const { | ||||
std::string ret; | std::string ret; | ||||
const size_t pad = m_max_pad + 4; | const size_t pad = m_max_pad + 4; | ||||
for (const auto &s : m_sections) { | for (const auto &s : m_sections) { | ||||
if (s.m_right.empty()) { | if (s.m_right.empty()) { | ||||
ret += s.m_left; | ret += s.m_left; | ||||
ret += "\n"; | ret += "\n"; | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 279 Lines • Show Last 20 Lines |