mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'ximinez/lending-XLS-66-2' into ximinez/lending-XLS-66-ongoing
This commit is contained in:
@@ -1,445 +0,0 @@
|
||||
#ifndef XRPL_JSON_OBJECT_H_INCLUDED
|
||||
#define XRPL_JSON_OBJECT_H_INCLUDED
|
||||
|
||||
#include <xrpl/json/Writer.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Json {
|
||||
|
||||
/**
|
||||
Collection is a base class for Array and Object, classes which provide the
|
||||
facade of JSON collections for the O(1) JSON writer, while still using no
|
||||
heap memory and only a very small amount of stack.
|
||||
|
||||
From http://json.org, JSON has two types of collection: array, and object.
|
||||
Everything else is a *scalar* - a number, a string, a boolean, the special
|
||||
value null, or a legacy Json::Value.
|
||||
|
||||
Collections must write JSON "as-it-goes" in order to get the strong
|
||||
performance guarantees. This puts restrictions upon API users:
|
||||
|
||||
1. Only one collection can be open for change at any one time.
|
||||
|
||||
This condition is enforced automatically and a std::logic_error thrown if
|
||||
it is violated.
|
||||
|
||||
2. A tag may only be used once in an Object.
|
||||
|
||||
Some objects have many tags, so this condition might be a little
|
||||
expensive. Enforcement of this condition is turned on in debug builds and
|
||||
a std::logic_error is thrown when the tag is added for a second time.
|
||||
|
||||
Code samples:
|
||||
|
||||
Writer writer;
|
||||
|
||||
// An empty object.
|
||||
{
|
||||
Object::Root (writer);
|
||||
}
|
||||
// Outputs {}
|
||||
|
||||
// An object with one scalar value.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
write["hello"] = "world";
|
||||
}
|
||||
// Outputs {"hello":"world"}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)["hello"] = "world";
|
||||
}
|
||||
// Output is the same.
|
||||
|
||||
// Add several scalars, with chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.set ("hello", "world")
|
||||
.set ("flag", false)
|
||||
.set ("x", 42);
|
||||
}
|
||||
// Outputs {"hello":"world","flag":false,"x":42}
|
||||
|
||||
// Add an array.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
{
|
||||
auto array = root.setArray ("hands");
|
||||
array.append ("left");
|
||||
array.append ("right");
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":["left", "right"]}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.setArray ("hands")
|
||||
.append ("left")
|
||||
.append ("right");
|
||||
}
|
||||
// Output is the same.
|
||||
|
||||
// Add an object.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
{
|
||||
auto object = root.setObject ("hands");
|
||||
object["left"] = false;
|
||||
object["right"] = true;
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":{"left":false,"right":true}}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.setObject ("hands")
|
||||
.set ("left", false)
|
||||
.set ("right", true);
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":{"left":false,"right":true}}
|
||||
|
||||
|
||||
Typical ways to make mistakes and get a std::logic_error:
|
||||
|
||||
Writer writer;
|
||||
Object::Root root (writer);
|
||||
|
||||
// Repeat a tag.
|
||||
{
|
||||
root ["hello"] = "world";
|
||||
root ["hello"] = "there"; // THROWS! in a debug build.
|
||||
}
|
||||
|
||||
// Open a subcollection, then set something else.
|
||||
{
|
||||
auto object = root.setObject ("foo");
|
||||
root ["hello"] = "world"; // THROWS!
|
||||
}
|
||||
|
||||
// Open two subcollections at a time.
|
||||
{
|
||||
auto object = root.setObject ("foo");
|
||||
auto array = root.setArray ("bar"); // THROWS!!
|
||||
}
|
||||
|
||||
For more examples, check the unit tests.
|
||||
*/
|
||||
|
||||
class Collection
|
||||
{
|
||||
public:
|
||||
Collection(Collection&& c) noexcept;
|
||||
Collection&
|
||||
operator=(Collection&& c) noexcept;
|
||||
Collection() = delete;
|
||||
|
||||
~Collection();
|
||||
|
||||
protected:
|
||||
// A null parent means "no parent at all".
|
||||
// Writers cannot be null.
|
||||
Collection(Collection* parent, Writer*);
|
||||
void
|
||||
checkWritable(std::string const& label);
|
||||
|
||||
Collection* parent_;
|
||||
Writer* writer_;
|
||||
bool enabled_;
|
||||
};
|
||||
|
||||
class Array;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Represents a JSON object being written to a Writer. */
|
||||
class Object : protected Collection
|
||||
{
|
||||
public:
|
||||
/** Object::Root is the only Collection that has a public constructor. */
|
||||
class Root;
|
||||
|
||||
/** Set a scalar value in the Object for a key.
|
||||
|
||||
A JSON scalar is a single value - a number, string, boolean, nullptr or
|
||||
a Json::Value.
|
||||
|
||||
`set()` throws an exception if this object is disabled (which means that
|
||||
one of its children is enabled).
|
||||
|
||||
In a debug build, `set()` also throws an exception if the key has
|
||||
already been set() before.
|
||||
|
||||
An operator[] is provided to allow writing `object["key"] = scalar;`.
|
||||
*/
|
||||
template <typename Scalar>
|
||||
void
|
||||
set(std::string const& key, Scalar const&);
|
||||
|
||||
void
|
||||
set(std::string const& key, Json::Value const&);
|
||||
|
||||
// Detail class and method used to implement operator[].
|
||||
class Proxy;
|
||||
|
||||
Proxy
|
||||
operator[](std::string const& key);
|
||||
Proxy
|
||||
operator[](Json::StaticString const& key);
|
||||
|
||||
/** Make a new Object at a key and return it.
|
||||
|
||||
This Object is disabled until that sub-object is destroyed.
|
||||
Throws an exception if this Object was already disabled.
|
||||
*/
|
||||
Object
|
||||
setObject(std::string const& key);
|
||||
|
||||
/** Make a new Array at a key and return it.
|
||||
|
||||
This Object is disabled until that sub-array is destroyed.
|
||||
Throws an exception if this Object was already disabled.
|
||||
*/
|
||||
Array
|
||||
setArray(std::string const& key);
|
||||
|
||||
protected:
|
||||
friend class Array;
|
||||
Object(Collection* parent, Writer* w) : Collection(parent, w)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Object::Root : public Object
|
||||
{
|
||||
public:
|
||||
/** Each Object::Root must be constructed with its own unique Writer. */
|
||||
Root(Writer&);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Represents a JSON array being written to a Writer. */
|
||||
class Array : private Collection
|
||||
{
|
||||
public:
|
||||
/** Append a scalar to the Arrary.
|
||||
|
||||
Throws an exception if this array is disabled (which means that one of
|
||||
its sub-collections is enabled).
|
||||
*/
|
||||
template <typename Scalar>
|
||||
void
|
||||
append(Scalar const&);
|
||||
|
||||
/**
|
||||
Appends a Json::Value to an array.
|
||||
Throws an exception if this Array was disabled.
|
||||
*/
|
||||
void
|
||||
append(Json::Value const&);
|
||||
|
||||
/** Append a new Object and return it.
|
||||
|
||||
This Array is disabled until that sub-object is destroyed.
|
||||
Throws an exception if this Array was disabled.
|
||||
*/
|
||||
Object
|
||||
appendObject();
|
||||
|
||||
/** Append a new Array and return it.
|
||||
|
||||
This Array is disabled until that sub-array is destroyed.
|
||||
Throws an exception if this Array was already disabled.
|
||||
*/
|
||||
Array
|
||||
appendArray();
|
||||
|
||||
protected:
|
||||
friend class Object;
|
||||
Array(Collection* parent, Writer* w) : Collection(parent, w)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Generic accessor functions to allow Json::Value and Collection to
|
||||
// interoperate.
|
||||
|
||||
/** Add a new subarray at a named key in a Json object. */
|
||||
Json::Value&
|
||||
setArray(Json::Value&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subarray at a named key in a Json object. */
|
||||
Array
|
||||
setArray(Object&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subobject at a named key in a Json object. */
|
||||
Json::Value&
|
||||
addObject(Json::Value&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subobject at a named key in a Json object. */
|
||||
Object
|
||||
addObject(Object&, Json::StaticString const& key);
|
||||
|
||||
/** Append a new subarray to a Json array. */
|
||||
Json::Value&
|
||||
appendArray(Json::Value&);
|
||||
|
||||
/** Append a new subarray to a Json array. */
|
||||
Array
|
||||
appendArray(Array&);
|
||||
|
||||
/** Append a new subobject to a Json object. */
|
||||
Json::Value&
|
||||
appendObject(Json::Value&);
|
||||
|
||||
/** Append a new subobject to a Json object. */
|
||||
Object
|
||||
appendObject(Array&);
|
||||
|
||||
/** Copy all the keys and values from one object into another. */
|
||||
void
|
||||
copyFrom(Json::Value& to, Json::Value const& from);
|
||||
|
||||
/** Copy all the keys and values from one object into another. */
|
||||
void
|
||||
copyFrom(Object& to, Json::Value const& from);
|
||||
|
||||
/** An Object that contains its own Writer. */
|
||||
class WriterObject
|
||||
{
|
||||
public:
|
||||
WriterObject(Output const& output)
|
||||
: writer_(std::make_unique<Writer>(output))
|
||||
, object_(std::make_unique<Object::Root>(*writer_))
|
||||
{
|
||||
}
|
||||
|
||||
WriterObject(WriterObject&& other) = default;
|
||||
|
||||
Object*
|
||||
operator->()
|
||||
{
|
||||
return object_.get();
|
||||
}
|
||||
|
||||
Object&
|
||||
operator*()
|
||||
{
|
||||
return *object_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Writer> writer_;
|
||||
std::unique_ptr<Object::Root> object_;
|
||||
};
|
||||
|
||||
WriterObject
|
||||
stringWriterObject(std::string&);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation details.
|
||||
|
||||
// Detail class for Object::operator[].
|
||||
class Object::Proxy
|
||||
{
|
||||
private:
|
||||
Object& object_;
|
||||
std::string const key_;
|
||||
|
||||
public:
|
||||
Proxy(Object& object, std::string const& key);
|
||||
|
||||
template <class T>
|
||||
void
|
||||
operator=(T const& t)
|
||||
{
|
||||
object_.set(key_, t);
|
||||
// Note: This function shouldn't return *this, because it's a trap.
|
||||
//
|
||||
// In Json::Value, foo[jss::key] returns a reference to a
|
||||
// mutable Json::Value contained _inside_ foo. But in the case of
|
||||
// Json::Object, where we write once only, there isn't any such
|
||||
// reference that can be returned. Returning *this would return an
|
||||
// object "a level higher" than in Json::Value, leading to obscure bugs,
|
||||
// particularly in generic code.
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
Array::append(Scalar const& value)
|
||||
{
|
||||
checkWritable("append");
|
||||
if (writer_)
|
||||
writer_->append(value);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
Object::set(std::string const& key, Scalar const& value)
|
||||
{
|
||||
checkWritable("set");
|
||||
if (writer_)
|
||||
writer_->set(key, value);
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
setArray(Json::Value& json, Json::StaticString const& key)
|
||||
{
|
||||
return (json[key] = Json::arrayValue);
|
||||
}
|
||||
|
||||
inline Array
|
||||
setArray(Object& json, Json::StaticString const& key)
|
||||
{
|
||||
return json.setArray(std::string(key));
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
addObject(Json::Value& json, Json::StaticString const& key)
|
||||
{
|
||||
return (json[key] = Json::objectValue);
|
||||
}
|
||||
|
||||
inline Object
|
||||
addObject(Object& object, Json::StaticString const& key)
|
||||
{
|
||||
return object.setObject(std::string(key));
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
appendArray(Json::Value& json)
|
||||
{
|
||||
return json.append(Json::arrayValue);
|
||||
}
|
||||
|
||||
inline Array
|
||||
appendArray(Array& json)
|
||||
{
|
||||
return json.appendArray();
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
appendObject(Json::Value& json)
|
||||
{
|
||||
return json.append(Json::objectValue);
|
||||
}
|
||||
|
||||
inline Object
|
||||
appendObject(Array& json)
|
||||
{
|
||||
return json.appendObject();
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif
|
||||
@@ -58,14 +58,14 @@ static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
|
||||
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
|
||||
static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion);
|
||||
|
||||
template <class JsonObject>
|
||||
void
|
||||
setVersion(JsonObject& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
inline void
|
||||
setVersion(Json::Value& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
apiVersion != apiInvalidVersion,
|
||||
"xrpl::RPC::setVersion : input is valid");
|
||||
auto& retObj = addObject(parent, jss::version);
|
||||
|
||||
auto& retObj = parent[jss::version] = Json::objectValue;
|
||||
|
||||
if (apiVersion == apiVersionIfUnspecified)
|
||||
{
|
||||
|
||||
@@ -209,33 +209,11 @@ get_error_info(error_code_i code);
|
||||
|
||||
/** Add or update the json update to reflect the error code. */
|
||||
/** @{ */
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(error_code_i code, JsonValue& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = info.message;
|
||||
}
|
||||
inject_error(error_code_i code, Json::Value& json);
|
||||
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(int code, JsonValue& json)
|
||||
{
|
||||
inject_error(error_code_i(code), json);
|
||||
}
|
||||
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(error_code_i code, std::string const& message, JsonValue& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = message;
|
||||
}
|
||||
|
||||
inject_error(error_code_i code, std::string const& message, Json::Value& json);
|
||||
/** @} */
|
||||
|
||||
/** Returns a new json object that reflects the error code. */
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace xrpl {
|
||||
bool
|
||||
isRpcError(Json::Value jvResult);
|
||||
Json::Value
|
||||
rpcError(int iError);
|
||||
rpcError(error_code_i iError);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/Object.h>
|
||||
#include <xrpl/json/Output.h>
|
||||
#include <xrpl/json/Writer.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace Json {
|
||||
|
||||
Collection::Collection(Collection* parent, Writer* writer)
|
||||
: parent_(parent), writer_(writer), enabled_(true)
|
||||
{
|
||||
checkWritable("Collection::Collection()");
|
||||
if (parent_)
|
||||
{
|
||||
check(parent_->enabled_, "Parent not enabled in constructor");
|
||||
parent_->enabled_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
Collection::~Collection()
|
||||
{
|
||||
if (writer_)
|
||||
writer_->finish();
|
||||
if (parent_)
|
||||
parent_->enabled_ = true;
|
||||
}
|
||||
|
||||
Collection&
|
||||
Collection::operator=(Collection&& that) noexcept
|
||||
{
|
||||
parent_ = that.parent_;
|
||||
writer_ = that.writer_;
|
||||
enabled_ = that.enabled_;
|
||||
|
||||
that.parent_ = nullptr;
|
||||
that.writer_ = nullptr;
|
||||
that.enabled_ = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Collection::Collection(Collection&& that) noexcept
|
||||
{
|
||||
*this = std::move(that);
|
||||
}
|
||||
|
||||
void
|
||||
Collection::checkWritable(std::string const& label)
|
||||
{
|
||||
if (!enabled_)
|
||||
xrpl::Throw<std::logic_error>(label + ": not enabled");
|
||||
if (!writer_)
|
||||
xrpl::Throw<std::logic_error>(label + ": not writable");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Object::Root::Root(Writer& w) : Object(nullptr, &w)
|
||||
{
|
||||
writer_->startRoot(Writer::object);
|
||||
}
|
||||
|
||||
Object
|
||||
Object::setObject(std::string const& key)
|
||||
{
|
||||
checkWritable("Object::setObject");
|
||||
if (writer_)
|
||||
writer_->startSet(Writer::object, key);
|
||||
return Object(this, writer_);
|
||||
}
|
||||
|
||||
Array
|
||||
Object::setArray(std::string const& key)
|
||||
{
|
||||
checkWritable("Object::setArray");
|
||||
if (writer_)
|
||||
writer_->startSet(Writer::array, key);
|
||||
return Array(this, writer_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Object
|
||||
Array::appendObject()
|
||||
{
|
||||
checkWritable("Array::appendObject");
|
||||
if (writer_)
|
||||
writer_->startAppend(Writer::object);
|
||||
return Object(this, writer_);
|
||||
}
|
||||
|
||||
Array
|
||||
Array::appendArray()
|
||||
{
|
||||
checkWritable("Array::makeArray");
|
||||
if (writer_)
|
||||
writer_->startAppend(Writer::array);
|
||||
return Array(this, writer_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Object::Proxy::Proxy(Object& object, std::string const& key)
|
||||
: object_(object), key_(key)
|
||||
{
|
||||
}
|
||||
|
||||
Object::Proxy
|
||||
Object::operator[](std::string const& key)
|
||||
{
|
||||
return Proxy(*this, key);
|
||||
}
|
||||
|
||||
Object::Proxy
|
||||
Object::operator[](Json::StaticString const& key)
|
||||
{
|
||||
return Proxy(*this, std::string(key));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Array::append(Json::Value const& v)
|
||||
{
|
||||
auto t = v.type();
|
||||
switch (t)
|
||||
{
|
||||
case Json::nullValue:
|
||||
return append(nullptr);
|
||||
case Json::intValue:
|
||||
return append(v.asInt());
|
||||
case Json::uintValue:
|
||||
return append(v.asUInt());
|
||||
case Json::realValue:
|
||||
return append(v.asDouble());
|
||||
case Json::stringValue:
|
||||
return append(v.asString());
|
||||
case Json::booleanValue:
|
||||
return append(v.asBool());
|
||||
|
||||
case Json::objectValue: {
|
||||
auto object = appendObject();
|
||||
copyFrom(object, v);
|
||||
return;
|
||||
}
|
||||
|
||||
case Json::arrayValue: {
|
||||
auto array = appendArray();
|
||||
for (auto& item : v)
|
||||
array.append(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Json::Array::append : invalid type"); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
void
|
||||
Object::set(std::string const& k, Json::Value const& v)
|
||||
{
|
||||
auto t = v.type();
|
||||
switch (t)
|
||||
{
|
||||
case Json::nullValue:
|
||||
return set(k, nullptr);
|
||||
case Json::intValue:
|
||||
return set(k, v.asInt());
|
||||
case Json::uintValue:
|
||||
return set(k, v.asUInt());
|
||||
case Json::realValue:
|
||||
return set(k, v.asDouble());
|
||||
case Json::stringValue:
|
||||
return set(k, v.asString());
|
||||
case Json::booleanValue:
|
||||
return set(k, v.asBool());
|
||||
|
||||
case Json::objectValue: {
|
||||
auto object = setObject(k);
|
||||
copyFrom(object, v);
|
||||
return;
|
||||
}
|
||||
|
||||
case Json::arrayValue: {
|
||||
auto array = setArray(k);
|
||||
for (auto& item : v)
|
||||
array.append(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Json::Object::set : invalid type"); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
doCopyFrom(Object& to, Json::Value const& from)
|
||||
{
|
||||
XRPL_ASSERT(from.isObjectOrNull(), "Json::doCopyFrom : valid input type");
|
||||
auto members = from.getMemberNames();
|
||||
for (auto& m : members)
|
||||
to[m] = from[m];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
copyFrom(Json::Value& to, Json::Value const& from)
|
||||
{
|
||||
if (!to) // Short circuit this very common case.
|
||||
to = from;
|
||||
else
|
||||
doCopyFrom(to, from);
|
||||
}
|
||||
|
||||
void
|
||||
copyFrom(Object& to, Json::Value const& from)
|
||||
{
|
||||
doCopyFrom(to, from);
|
||||
}
|
||||
|
||||
WriterObject
|
||||
stringWriterObject(std::string& s)
|
||||
{
|
||||
return WriterObject(stringOutput(s));
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
@@ -17,7 +17,7 @@ namespace BuildInfo {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "3.1.0-b0"
|
||||
char const* const versionString = "3.2.0-b0"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
|
||||
@@ -160,6 +160,24 @@ constexpr ErrorInfo unknownError;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
inject_error(error_code_i code, Json::Value& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = info.message;
|
||||
}
|
||||
|
||||
void
|
||||
inject_error(error_code_i code, std::string const& message, Json::Value& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = message;
|
||||
}
|
||||
|
||||
ErrorInfo const&
|
||||
get_error_info(error_code_i code)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ struct RPCErr;
|
||||
|
||||
// VFALCO NOTE Deprecated function
|
||||
Json::Value
|
||||
rpcError(int iError)
|
||||
rpcError(error_code_i iError)
|
||||
{
|
||||
Json::Value jvResult(Json::objectValue);
|
||||
RPC::inject_error(iError, jvResult);
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
#include <test/json/TestOutputSuite.h>
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/json/Object.h>
|
||||
|
||||
namespace Json {
|
||||
|
||||
class JsonObject_test : public xrpl::test::TestOutputSuite
|
||||
{
|
||||
void
|
||||
setup(std::string const& testName)
|
||||
{
|
||||
testcase(testName);
|
||||
output_.clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<WriterObject> writerObject_;
|
||||
|
||||
Object&
|
||||
makeRoot()
|
||||
{
|
||||
writerObject_ =
|
||||
std::make_unique<WriterObject>(stringWriterObject(output_));
|
||||
return **writerObject_;
|
||||
}
|
||||
|
||||
void
|
||||
expectResult(std::string const& expected)
|
||||
{
|
||||
writerObject_.reset();
|
||||
TestOutputSuite::expectResult(expected);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
testTrivial()
|
||||
{
|
||||
setup("trivial");
|
||||
|
||||
{
|
||||
auto& root = makeRoot();
|
||||
(void)root;
|
||||
}
|
||||
expectResult("{}");
|
||||
}
|
||||
|
||||
void
|
||||
testSimple()
|
||||
{
|
||||
setup("simple");
|
||||
{
|
||||
auto& root = makeRoot();
|
||||
root["hello"] = "world";
|
||||
root["skidoo"] = 23;
|
||||
root["awake"] = false;
|
||||
root["temperature"] = 98.6;
|
||||
}
|
||||
|
||||
expectResult(
|
||||
"{\"hello\":\"world\","
|
||||
"\"skidoo\":23,"
|
||||
"\"awake\":false,"
|
||||
"\"temperature\":98.6}");
|
||||
}
|
||||
|
||||
void
|
||||
testOneSub()
|
||||
{
|
||||
setup("oneSub");
|
||||
{
|
||||
auto& root = makeRoot();
|
||||
root.setArray("ar");
|
||||
}
|
||||
expectResult("{\"ar\":[]}");
|
||||
}
|
||||
|
||||
void
|
||||
testSubs()
|
||||
{
|
||||
setup("subs");
|
||||
{
|
||||
auto& root = makeRoot();
|
||||
|
||||
{
|
||||
// Add an array with three entries.
|
||||
auto array = root.setArray("ar");
|
||||
array.append(23);
|
||||
array.append(false);
|
||||
array.append(23.5);
|
||||
}
|
||||
|
||||
{
|
||||
// Add an object with one entry.
|
||||
auto obj = root.setObject("obj");
|
||||
obj["hello"] = "world";
|
||||
}
|
||||
|
||||
{
|
||||
// Add another object with two entries.
|
||||
Json::Value value;
|
||||
value["h"] = "w";
|
||||
value["f"] = false;
|
||||
root["obj2"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Json::Value has an unstable order...
|
||||
auto case1 =
|
||||
"{\"ar\":[23,false,23.5],"
|
||||
"\"obj\":{\"hello\":\"world\"},"
|
||||
"\"obj2\":{\"h\":\"w\",\"f\":false}}";
|
||||
auto case2 =
|
||||
"{\"ar\":[23,false,23.5],"
|
||||
"\"obj\":{\"hello\":\"world\"},"
|
||||
"\"obj2\":{\"f\":false,\"h\":\"w\"}}";
|
||||
writerObject_.reset();
|
||||
BEAST_EXPECT(output_ == case1 || output_ == case2);
|
||||
}
|
||||
|
||||
void
|
||||
testSubsShort()
|
||||
{
|
||||
setup("subsShort");
|
||||
|
||||
{
|
||||
auto& root = makeRoot();
|
||||
|
||||
{
|
||||
// Add an array with three entries.
|
||||
auto array = root.setArray("ar");
|
||||
array.append(23);
|
||||
array.append(false);
|
||||
array.append(23.5);
|
||||
}
|
||||
|
||||
// Add an object with one entry.
|
||||
root.setObject("obj")["hello"] = "world";
|
||||
|
||||
{
|
||||
// Add another object with two entries.
|
||||
auto object = root.setObject("obj2");
|
||||
object.set("h", "w");
|
||||
object.set("f", false);
|
||||
}
|
||||
}
|
||||
expectResult(
|
||||
"{\"ar\":[23,false,23.5],"
|
||||
"\"obj\":{\"hello\":\"world\"},"
|
||||
"\"obj2\":{\"h\":\"w\",\"f\":false}}");
|
||||
}
|
||||
|
||||
void
|
||||
testFailureObject()
|
||||
{
|
||||
{
|
||||
setup("object failure assign");
|
||||
auto& root = makeRoot();
|
||||
auto obj = root.setObject("o1");
|
||||
expectException([&]() { root["fail"] = "complete"; });
|
||||
}
|
||||
{
|
||||
setup("object failure object");
|
||||
auto& root = makeRoot();
|
||||
auto obj = root.setObject("o1");
|
||||
expectException([&]() { root.setObject("o2"); });
|
||||
}
|
||||
{
|
||||
setup("object failure Array");
|
||||
auto& root = makeRoot();
|
||||
auto obj = root.setArray("o1");
|
||||
expectException([&]() { root.setArray("o2"); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testFailureArray()
|
||||
{
|
||||
{
|
||||
setup("array failure append");
|
||||
auto& root = makeRoot();
|
||||
auto array = root.setArray("array");
|
||||
auto subarray = array.appendArray();
|
||||
auto fail = [&]() { array.append("fail"); };
|
||||
expectException(fail);
|
||||
}
|
||||
{
|
||||
setup("array failure appendArray");
|
||||
auto& root = makeRoot();
|
||||
auto array = root.setArray("array");
|
||||
auto subarray = array.appendArray();
|
||||
auto fail = [&]() { array.appendArray(); };
|
||||
expectException(fail);
|
||||
}
|
||||
{
|
||||
setup("array failure appendObject");
|
||||
auto& root = makeRoot();
|
||||
auto array = root.setArray("array");
|
||||
auto subarray = array.appendArray();
|
||||
auto fail = [&]() { array.appendObject(); };
|
||||
expectException(fail);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testKeyFailure()
|
||||
{
|
||||
setup("repeating keys");
|
||||
auto& root = makeRoot();
|
||||
root.set("foo", "bar");
|
||||
root.set("baz", 0);
|
||||
// setting key again throws in !NDEBUG builds
|
||||
auto set_again = [&]() { root.set("foo", "bar"); };
|
||||
#ifdef NDEBUG
|
||||
set_again();
|
||||
pass();
|
||||
#else
|
||||
expectException(set_again);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testTrivial();
|
||||
testSimple();
|
||||
|
||||
testOneSub();
|
||||
testSubs();
|
||||
testSubsShort();
|
||||
|
||||
testFailureObject();
|
||||
testFailureArray();
|
||||
testKeyFailure();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(JsonObject, json, xrpl);
|
||||
|
||||
} // namespace Json
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <xrpld/rpc/RPCCall.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/json/Object.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -83,7 +82,7 @@ cmdToJSONRPC(
|
||||
// If paramsObj is not empty, put it in a [params] array.
|
||||
if (paramsObj.begin() != paramsObj.end())
|
||||
{
|
||||
auto& paramsArray = Json::setArray(jv, jss::params);
|
||||
auto& paramsArray = jv[jss::params] = Json::arrayValue;
|
||||
paramsArray.append(paramsObj);
|
||||
}
|
||||
if (paramsObj.isMember(jss::jsonrpc))
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/json/Object.h>
|
||||
#include <xrpl/protocol/serialize.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -42,10 +41,9 @@ struct LedgerFill
|
||||
std::optional<NetClock::time_point> closeTime;
|
||||
};
|
||||
|
||||
/** Given a Ledger and options, fill a Json::Object or Json::Value with a
|
||||
/** Given a Ledger and options, fill a Json::Value with a
|
||||
description of the ledger.
|
||||
*/
|
||||
|
||||
void
|
||||
addJson(Json::Value&, LedgerFill const&);
|
||||
|
||||
@@ -53,6 +51,10 @@ addJson(Json::Value&, LedgerFill const&);
|
||||
Json::Value
|
||||
getJson(LedgerFill const&);
|
||||
|
||||
/** Copy all the keys and values from one object into another. */
|
||||
void
|
||||
copyFrom(Json::Value& to, Json::Value const& from);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,10 +32,9 @@ isBinary(LedgerFill const& fill)
|
||||
return fill.options & LedgerFill::binary;
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJson(
|
||||
Object& json,
|
||||
Json::Value& json,
|
||||
bool closed,
|
||||
LedgerHeader const& info,
|
||||
bool bFull,
|
||||
@@ -78,9 +77,8 @@ fillJson(
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJsonBinary(Object& json, bool closed, LedgerHeader const& info)
|
||||
fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info)
|
||||
{
|
||||
if (!closed)
|
||||
json[jss::closed] = false;
|
||||
@@ -207,11 +205,10 @@ fillJsonTx(
|
||||
return txJson;
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJsonTx(Object& json, LedgerFill const& fill)
|
||||
fillJsonTx(Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& txns = setArray(json, jss::transactions);
|
||||
auto& txns = json[jss::transactions] = Json::arrayValue;
|
||||
auto bBinary = isBinary(fill);
|
||||
auto bExpanded = isExpanded(fill);
|
||||
|
||||
@@ -238,12 +235,11 @@ fillJsonTx(Object& json, LedgerFill const& fill)
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJsonState(Object& json, LedgerFill const& fill)
|
||||
fillJsonState(Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
auto& ledger = fill.ledger;
|
||||
auto&& array = Json::setArray(json, jss::accountState);
|
||||
auto& array = json[jss::accountState] = Json::arrayValue;
|
||||
auto expanded = isExpanded(fill);
|
||||
auto binary = isBinary(fill);
|
||||
|
||||
@@ -251,7 +247,7 @@ fillJsonState(Object& json, LedgerFill const& fill)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
auto&& obj = appendObject(array);
|
||||
auto& obj = array.append(Json::objectValue);
|
||||
obj[jss::hash] = to_string(sle->key());
|
||||
obj[jss::tx_blob] = serializeHex(*sle);
|
||||
}
|
||||
@@ -262,17 +258,16 @@ fillJsonState(Object& json, LedgerFill const& fill)
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJsonQueue(Object& json, LedgerFill const& fill)
|
||||
fillJsonQueue(Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& queueData = Json::setArray(json, jss::queue_data);
|
||||
auto& queueData = json[jss::queue_data] = Json::arrayValue;
|
||||
auto bBinary = isBinary(fill);
|
||||
auto bExpanded = isExpanded(fill);
|
||||
|
||||
for (auto const& tx : fill.txQueue)
|
||||
{
|
||||
auto&& txJson = appendObject(queueData);
|
||||
auto& txJson = queueData.append(Json::objectValue);
|
||||
txJson[jss::fee_level] = to_string(tx.feeLevel);
|
||||
if (tx.lastValid)
|
||||
txJson[jss::LastLedgerSequence] = *tx.lastValid;
|
||||
@@ -297,9 +292,8 @@ fillJsonQueue(Object& json, LedgerFill const& fill)
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
fillJson(Object& json, LedgerFill const& fill)
|
||||
fillJson(Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
// TODO: what happens if bBinary and bExtracted are both set?
|
||||
// Is there a way to report this back?
|
||||
@@ -327,7 +321,7 @@ fillJson(Object& json, LedgerFill const& fill)
|
||||
void
|
||||
addJson(Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& object = Json::addObject(json, jss::ledger);
|
||||
auto& object = json[jss::ledger] = Json::objectValue;
|
||||
fillJson(object, fill);
|
||||
|
||||
if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty())
|
||||
@@ -342,4 +336,20 @@ getJson(LedgerFill const& fill)
|
||||
return json;
|
||||
}
|
||||
|
||||
void
|
||||
copyFrom(Json::Value& to, Json::Value const& from)
|
||||
{
|
||||
if (!to) // Short circuit this very common case.
|
||||
to = from;
|
||||
else
|
||||
{
|
||||
// TODO: figure out if there is a way to remove this clause
|
||||
// or check that it does/needs to do a deep copy
|
||||
XRPL_ASSERT(from.isObjectOrNull(), "copyFrom : invalid input type");
|
||||
auto const members = from.getMemberNames();
|
||||
for (auto const& m : members)
|
||||
to[m] = from[m];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -94,9 +94,8 @@ public:
|
||||
|
||||
/** Apply the Status to a JsonObject
|
||||
*/
|
||||
template <class Object>
|
||||
void
|
||||
inject(Object& object) const
|
||||
inject(Json::Value& object) const
|
||||
{
|
||||
if (auto ec = toErrorCode())
|
||||
{
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <xrpld/app/main/Application.h>
|
||||
|
||||
#include <xrpl/json/Object.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
Json::Value
|
||||
|
||||
@@ -75,6 +75,43 @@ LedgerHandler::check()
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
void
|
||||
LedgerHandler::writeResult(Json::Value& value)
|
||||
{
|
||||
if (ledger_)
|
||||
{
|
||||
copyFrom(value, result_);
|
||||
addJson(value, {*ledger_, &context_, options_, queueTxs_});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& master = context_.app.getLedgerMaster();
|
||||
{
|
||||
auto& closed = value[jss::closed] = Json::objectValue;
|
||||
addJson(closed, {*master.getClosedLedger(), &context_, 0});
|
||||
}
|
||||
{
|
||||
auto& open = value[jss::open] = Json::objectValue;
|
||||
addJson(open, {*master.getCurrentLedger(), &context_, 0});
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value warnings{Json::arrayValue};
|
||||
if (context_.params.isMember(jss::type))
|
||||
{
|
||||
Json::Value& w = warnings.append(Json::objectValue);
|
||||
w[jss::id] = warnRPC_FIELDS_DEPRECATED;
|
||||
w[jss::message] =
|
||||
"Some fields from your request are deprecated. Please check the "
|
||||
"documentation at "
|
||||
"https://xrpl.org/docs/references/http-websocket-apis/ "
|
||||
"and update your request. Field `type` is deprecated.";
|
||||
}
|
||||
|
||||
if (warnings.size())
|
||||
value[jss::warnings] = std::move(warnings);
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
|
||||
#include <xrpl/json/Object.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -37,9 +36,8 @@ public:
|
||||
Status
|
||||
check();
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
writeResult(Object&);
|
||||
writeResult(Json::Value&);
|
||||
|
||||
static constexpr char name[] = "ledger";
|
||||
|
||||
@@ -59,49 +57,6 @@ private:
|
||||
int options_ = 0;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Implementation.
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
LedgerHandler::writeResult(Object& value)
|
||||
{
|
||||
if (ledger_)
|
||||
{
|
||||
Json::copyFrom(value, result_);
|
||||
addJson(value, {*ledger_, &context_, options_, queueTxs_});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& master = context_.app.getLedgerMaster();
|
||||
{
|
||||
auto&& closed = Json::addObject(value, jss::closed);
|
||||
addJson(closed, {*master.getClosedLedger(), &context_, 0});
|
||||
}
|
||||
{
|
||||
auto&& open = Json::addObject(value, jss::open);
|
||||
addJson(open, {*master.getCurrentLedger(), &context_, 0});
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value warnings{Json::arrayValue};
|
||||
if (context_.params.isMember(jss::type))
|
||||
{
|
||||
Json::Value& w = warnings.append(Json::objectValue);
|
||||
w[jss::id] = warnRPC_FIELDS_DEPRECATED;
|
||||
w[jss::message] =
|
||||
"Some fields from your request are deprecated. Please check the "
|
||||
"documentation at "
|
||||
"https://xrpl.org/docs/references/http-websocket-apis/ "
|
||||
"and update your request. Field `type` is deprecated.";
|
||||
}
|
||||
|
||||
if (warnings.size())
|
||||
value[jss::warnings] = std::move(warnings);
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace xrpl
|
||||
|
||||
|
||||
@@ -20,9 +20,8 @@ public:
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
writeResult(Object& obj)
|
||||
writeResult(Json::Value& obj)
|
||||
{
|
||||
setVersion(obj, apiVersion_, betaEnabled_);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user