Commit 50984474 authored by Dominik Charousset's avatar Dominik Charousset

Add new API for interacting with JSON values

parent 7168be7a
...@@ -161,7 +161,9 @@ caf_add_component( ...@@ -161,7 +161,9 @@ caf_add_component(
src/ipv6_address.cpp src/ipv6_address.cpp
src/ipv6_endpoint.cpp src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp src/ipv6_subnet.cpp
src/json_object.cpp
src/json_reader.cpp src/json_reader.cpp
src/json_value.cpp
src/json_writer.cpp src/json_writer.cpp
src/load_inspector.cpp src/load_inspector.cpp
src/local_actor.cpp src/local_actor.cpp
...@@ -313,7 +315,10 @@ caf_add_component( ...@@ -313,7 +315,10 @@ caf_add_component(
ipv6_address ipv6_address
ipv6_endpoint ipv6_endpoint
ipv6_subnet ipv6_subnet
json_array
json_object
json_reader json_reader
json_value
json_writer json_writer
load_inspector load_inspector
logger logger
......
This diff is collapsed.
...@@ -55,20 +55,20 @@ public: ...@@ -55,20 +55,20 @@ public:
using other = allocator<U>; using other = allocator<U>;
}; };
explicit allocator(monotonic_buffer_resource* mbr) : mbr_(mbr) { constexpr explicit allocator(monotonic_buffer_resource* mbr) : mbr_(mbr) {
// nop // nop
} }
allocator() : mbr_(nullptr) { constexpr allocator() : mbr_(nullptr) {
// nop // nop
} }
allocator(const allocator&) = default; constexpr allocator(const allocator&) = default;
allocator& operator=(const allocator&) = default; constexpr allocator& operator=(const allocator&) = default;
template <class U> template <class U>
allocator(const allocator<U>& other) : mbr_(other.resource()) { constexpr allocator(const allocator<U>& other) : mbr_(other.resource()) {
// nop // nop
} }
......
...@@ -110,6 +110,11 @@ class ipv4_subnet; ...@@ -110,6 +110,11 @@ class ipv4_subnet;
class ipv6_address; class ipv6_address;
class ipv6_endpoint; class ipv6_endpoint;
class ipv6_subnet; class ipv6_subnet;
class json_array;
class json_object;
class json_reader;
class json_value;
class json_writer;
class local_actor; class local_actor;
class mailbox_element; class mailbox_element;
class message; class message;
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
#include "caf/json_reader.hpp"
#include "caf/json_value.hpp"
#include "caf/json_writer.hpp"
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/json_value.hpp"
#include <iterator>
#include <memory>
namespace caf {
/// Represents a JSON array.
class CAF_CORE_EXPORT json_array {
public:
// -- friends ----------------------------------------------------------------
friend class json_value;
// -- member types -----------------------------------------------------------
class const_iterator {
public:
using difference_type = ptrdiff_t;
using value_type = json_value;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
const_iterator(detail::json::value::array::const_iterator iter,
detail::json::storage* storage)
: iter_(iter), storage_(storage) {
// nop
}
const_iterator() noexcept : storage_(nullptr) {
// nop
}
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
json_value value() const noexcept {
return json_value{std::addressof(*iter_), storage_};
}
json_value operator*() const noexcept {
return value();
}
const_iterator& operator++() noexcept {
++iter_;
return *this;
}
const_iterator operator++(int) noexcept {
return {iter_++, storage_};
}
bool equal_to(const const_iterator& other) const noexcept {
return iter_ == other.iter_;
}
private:
detail::json::value::array::const_iterator iter_;
detail::json::storage* storage_;
};
// -- constructors, destructors, and assignment operators --------------------
json_array() noexcept : arr_(detail::json::empty_array()) {
// nop
}
json_array(json_array&&) noexcept = default;
json_array(const json_array&) noexcept = default;
json_array& operator=(json_array&&) noexcept = default;
json_array& operator=(const json_array&) noexcept = default;
// -- properties -------------------------------------------------------------
/// Checks whether the array has no members.
bool empty() const noexcept {
return arr_->empty();
}
/// Alias for @c empty.
bool is_empty() const noexcept {
return empty();
}
/// Returns the number of key-value pairs in this array.
size_t size() const noexcept {
return arr_->size();
}
const_iterator begin() const noexcept {
return {arr_->begin(), storage_.get()};
}
const_iterator end() const noexcept {
return {arr_->end(), storage_.get()};
}
// -- serialization ----------------------------------------------------------
template <class Inspector>
friend bool inspect(Inspector& inspector, json_array& arr) {
if constexpr (Inspector::is_loading) {
auto storage = make_counted<detail::json::storage>();
auto* internal_arr = detail::json::make_array(storage);
if (!detail::json::load(inspector, *internal_arr, storage))
return false;
arr = json_array{internal_arr, std::move(storage)};
return true;
} else {
return detail::json::save(inspector, *arr.arr_);
}
}
private:
json_array(const detail::json::value::array* obj,
detail::json::storage_ptr sptr) noexcept
: arr_(obj), storage_(sptr) {
// nop
}
const detail::json::value::array* arr_ = nullptr;
detail::json::storage_ptr storage_;
};
inline bool operator==(const json_array::const_iterator& lhs,
const json_array::const_iterator& rhs) noexcept {
return lhs.equal_to(rhs);
}
inline bool operator!=(const json_array::const_iterator& lhs,
const json_array::const_iterator& rhs) noexcept {
return !lhs.equal_to(rhs);
}
inline bool operator==(const json_array& lhs, const json_array& rhs) noexcept {
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
inline bool operator!=(const json_array& lhs, const json_array& rhs) noexcept {
return !(lhs == rhs);
}
} // namespace caf
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/json_value.hpp"
#include <iterator>
namespace caf {
/// Represents a JSON object.
class CAF_CORE_EXPORT json_object {
public:
// -- friends ----------------------------------------------------------------
friend class json_value;
// -- member types -----------------------------------------------------------
class const_iterator {
public:
using difference_type = ptrdiff_t;
using value_type = std::pair<std::string_view, json_value>;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
const_iterator(detail::json::value::object::const_iterator iter,
detail::json::storage* storage)
: iter_(iter), storage_(storage) {
// nop
}
const_iterator() noexcept = default;
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
std::string_view key() const noexcept {
return iter_->key;
}
json_value value() const noexcept {
return json_value{iter_->val, storage_};
}
value_type operator*() const noexcept {
return {key(), value()};
}
const_iterator& operator++() noexcept {
++iter_;
return *this;
}
const_iterator operator++(int) noexcept {
return {iter_++, storage_};
}
bool equal_to(const const_iterator& other) const noexcept {
return iter_ == other.iter_;
}
private:
detail::json::value::object::const_iterator iter_;
detail::json::storage* storage_ = nullptr;
};
// -- constructors, destructors, and assignment operators --------------------
json_object() noexcept : obj_(detail::json::empty_object()) {
// nop
}
json_object(json_object&&) noexcept = default;
json_object(const json_object&) noexcept = default;
json_object& operator=(json_object&&) noexcept = default;
json_object& operator=(const json_object&) noexcept = default;
// -- properties -------------------------------------------------------------
/// Checks whether the object has no members.
bool empty() const noexcept {
return !obj_ || obj_->empty();
}
/// Alias for @c empty.
bool is_empty() const noexcept {
return empty();
}
/// Returns the number of key-value pairs in this object.
size_t size() const noexcept {
return obj_ ? obj_->size() : 0u;
}
/// Returns the value for @p key or an @c undefined value if the object does
/// not contain a value for @p key.
json_value value(std::string_view key) const;
const_iterator begin() const noexcept {
return {obj_->begin(), storage_.get()};
}
const_iterator end() const noexcept {
return {obj_->end(), storage_.get()};
}
// -- serialization ----------------------------------------------------------
template <class Inspector>
friend bool inspect(Inspector& inspector, json_object& obj) {
if constexpr (Inspector::is_loading) {
auto storage = make_counted<detail::json::storage>();
auto* internal_obj = detail::json::make_object(storage);
if (!detail::json::load(inspector, *internal_obj, storage))
return false;
obj = json_object{internal_obj, std::move(storage)};
return true;
} else {
return detail::json::save(inspector, *obj.obj_);
}
}
private:
json_object(const detail::json::value::object* obj,
detail::json::storage_ptr sptr) noexcept
: obj_(obj), storage_(sptr) {
// nop
}
const detail::json::value::object* obj_ = nullptr;
detail::json::storage_ptr storage_;
};
inline bool operator==(const json_object::const_iterator& lhs,
const json_object::const_iterator& rhs) noexcept {
return lhs.equal_to(rhs);
}
inline bool operator!=(const json_object::const_iterator& lhs,
const json_object::const_iterator& rhs) noexcept {
return !lhs.equal_to(rhs);
}
inline bool operator==(const json_object& lhs,
const json_object& rhs) noexcept {
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
inline bool operator!=(const json_object& lhs,
const json_object& rhs) noexcept {
return !(lhs == rhs);
}
} // namespace caf
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/make_counted.hpp"
#include <string>
#include <string_view>
namespace caf {
/// Represents an immutable JSON value.
class CAF_CORE_EXPORT json_value {
public:
// -- constructors, destructors, and assignment operators --------------------
json_value() noexcept : val_(detail::json::null_value()) {
// nop
}
json_value(const detail::json::value* val,
detail::json::storage_ptr sptr) noexcept
: val_(val), storage_(sptr) {
// nop
}
json_value(json_value&&) noexcept = default;
json_value(const json_value&) noexcept = default;
json_value& operator=(json_value&&) noexcept = default;
json_value& operator=(const json_value&) noexcept = default;
// -- factories --------------------------------------------------------------
static json_value undefined() noexcept {
return json_value{detail::json::undefined_value(), nullptr};
}
// -- properties -------------------------------------------------------------
/// Checks whether the value is @c null.
bool is_null() const noexcept {
return val_->is_null();
}
/// Checks whether the value is undefined. This special state indicates that a
/// previous key lookup failed.
bool is_undefined() const noexcept {
return val_->is_undefined();
}
/// Checks whether the value is an @c int64_t.
bool is_integer() const noexcept {
return val_->is_integer();
}
/// Checks whether the value is a @c double.
bool is_double() const noexcept {
return val_->is_double();
}
/// Checks whether the value is a number, i.e., an @c int64_t or a @c double.
bool is_number() const noexcept {
return is_integer() || is_double();
}
/// Checks whether the value is a @c bool.
bool is_bool() const noexcept {
return val_->is_bool();
}
/// Checks whether the value is a JSON string (@c std::string_view).
bool is_string() const noexcept {
return val_->is_string();
}
/// Checks whether the value is an JSON array.
bool is_array() const noexcept {
return val_->is_array();
}
/// Checks whether the value is a JSON object.
bool is_object() const noexcept {
return val_->is_object();
}
// -- conversion -------------------------------------------------------------
int64_t to_integer(int64_t fallback = 0) const;
double to_double(double fallback = 0.0) const;
bool to_bool(bool fallback = false) const;
std::string_view to_string() const;
std::string_view to_string(std::string_view fallback) const;
json_array to_array() const;
json_array to_array(json_array fallback) const;
json_object to_object() const;
json_object to_object(json_object fallback) const;
// -- comparison -------------------------------------------------------------
bool equal_to(const json_value& other) const noexcept;
// -- parsing ----------------------------------------------------------------
/// Attempts to parse @p str as JSON input into a self-contained value.
static expected<json_value> parse(std::string_view str);
/// Attempts to parse @p str as JSON input into a value that avoids copies
/// where possible by pointing into @p str.
/// @warning The returned @ref json_value may hold pointers into @p str. Thus,
/// the input *must* outlive the @ref json_value and any other JSON
/// objects created from that value.
static expected<json_value> parse_shallow(std::string_view str);
/// Attempts to parse @p str as JSON input. Decodes JSON in place and points
/// back into the @p str for all strings in the JSON input.
/// @warning The returned @ref json_value may hold pointers into @p str. Thus,
/// the input *must* outlive the @ref json_value and any other JSON
/// objects created from that value.
static expected<json_value> parse_in_situ(std::string& str);
// -- serialization ----------------------------------------------------------
template <class Inspector>
friend bool inspect(Inspector& inspector, json_value& val) {
if constexpr (Inspector::is_loading) {
auto storage = make_counted<detail::json::storage>();
auto* internal_val = detail::json::make_value(storage);
if (!detail::json::load(inspector, *internal_val, storage))
return false;
val = json_value{internal_val, std::move(storage)};
return true;
} else {
return detail::json::save(inspector, *val.val_);
}
}
private:
const detail::json::value* val_;
detail::json::storage_ptr storage_;
};
inline bool operator==(const json_value& lhs, const json_value& rhs) {
return lhs.equal_to(rhs);
}
inline bool operator!=(const json_value& lhs, const json_value& rhs) {
return !(lhs == rhs);
}
} // namespace caf
...@@ -16,6 +16,8 @@ namespace caf { ...@@ -16,6 +16,8 @@ namespace caf {
/// Stores all information necessary for implementing an FSM-based parser. /// Stores all information necessary for implementing an FSM-based parser.
template <class Iterator, class Sentinel> template <class Iterator, class Sentinel>
struct parser_state { struct parser_state {
using iterator_type = Iterator;
/// Current position of the parser. /// Current position of the parser.
Iterator i; Iterator i;
...@@ -139,4 +141,7 @@ auto make_error(const parser_state<Iterator, Sentinel>& ps, Ts&&... xs) ...@@ -139,4 +141,7 @@ auto make_error(const parser_state<Iterator, Sentinel>& ps, Ts&&... xs)
/// Specialization for parsers operating on string views. /// Specialization for parsers operating on string views.
using string_parser_state = parser_state<std::string_view::iterator>; using string_parser_state = parser_state<std::string_view::iterator>;
/// Specialization for parsers operating on mutable character sequences.
using mutable_string_parser_state = parser_state<char*>;
} // namespace caf } // namespace caf
...@@ -405,7 +405,6 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0) ...@@ -405,7 +405,6 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID(core_module, (caf::cow_string)) CAF_ADD_TYPE_ID(core_module, (caf::cow_string))
CAF_ADD_TYPE_ID(core_module, (caf::cow_u16string)) CAF_ADD_TYPE_ID(core_module, (caf::cow_u16string))
CAF_ADD_TYPE_ID(core_module, (caf::cow_u32string)) CAF_ADD_TYPE_ID(core_module, (caf::cow_u32string))
CAF_ADD_TYPE_ID(core_module, (caf::dictionary<caf::config_value>) )
CAF_ADD_TYPE_ID(core_module, (caf::down_msg)) CAF_ADD_TYPE_ID(core_module, (caf::down_msg))
CAF_ADD_TYPE_ID(core_module, (caf::error)) CAF_ADD_TYPE_ID(core_module, (caf::error))
CAF_ADD_TYPE_ID(core_module, (caf::exit_msg)) CAF_ADD_TYPE_ID(core_module, (caf::exit_msg))
...@@ -419,6 +418,9 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0) ...@@ -419,6 +418,9 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID(core_module, (caf::ipv6_address)) CAF_ADD_TYPE_ID(core_module, (caf::ipv6_address))
CAF_ADD_TYPE_ID(core_module, (caf::ipv6_endpoint)) CAF_ADD_TYPE_ID(core_module, (caf::ipv6_endpoint))
CAF_ADD_TYPE_ID(core_module, (caf::ipv6_subnet)) CAF_ADD_TYPE_ID(core_module, (caf::ipv6_subnet))
CAF_ADD_TYPE_ID(core_module, (caf::json_array))
CAF_ADD_TYPE_ID(core_module, (caf::json_object))
CAF_ADD_TYPE_ID(core_module, (caf::json_value))
CAF_ADD_TYPE_ID(core_module, (caf::message)) CAF_ADD_TYPE_ID(core_module, (caf::message))
CAF_ADD_TYPE_ID(core_module, (caf::message_id)) CAF_ADD_TYPE_ID(core_module, (caf::message_id))
CAF_ADD_TYPE_ID(core_module, (caf::node_down_msg)) CAF_ADD_TYPE_ID(core_module, (caf::node_down_msg))
...@@ -426,6 +428,7 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0) ...@@ -426,6 +428,7 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID(core_module, (caf::none_t)) CAF_ADD_TYPE_ID(core_module, (caf::none_t))
CAF_ADD_TYPE_ID(core_module, (caf::pec)) CAF_ADD_TYPE_ID(core_module, (caf::pec))
CAF_ADD_TYPE_ID(core_module, (caf::sec)) CAF_ADD_TYPE_ID(core_module, (caf::sec))
CAF_ADD_TYPE_ID(core_module, (caf::settings))
CAF_ADD_TYPE_ID(core_module, (caf::shared_action_ptr)) CAF_ADD_TYPE_ID(core_module, (caf::shared_action_ptr))
CAF_ADD_TYPE_ID(core_module, (caf::stream)) CAF_ADD_TYPE_ID(core_module, (caf::stream))
CAF_ADD_TYPE_ID(core_module, (caf::stream_abort_msg)) CAF_ADD_TYPE_ID(core_module, (caf::stream_abort_msg))
......
This diff is collapsed.
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
#include "caf/ipv6_address.hpp" #include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.hpp" #include "caf/ipv6_endpoint.hpp"
#include "caf/ipv6_subnet.hpp" #include "caf/ipv6_subnet.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
#include "caf/json_value.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/message_id.hpp" #include "caf/message_id.hpp"
#include "caf/node_id.hpp" #include "caf/node_id.hpp"
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_object.hpp"
namespace caf {
// -- properties ---------------------------------------------------------------
json_value json_object::value(std::string_view key) const {
auto pred = [key](const auto& member) { return member.key == key; };
auto i = std::find_if(obj_->begin(), obj_->end(), pred);
if (i != obj_->end()) {
return {i->val, storage_};
}
return json_value::undefined();
}
} // namespace caf
...@@ -145,7 +145,7 @@ json_reader::~json_reader() { ...@@ -145,7 +145,7 @@ json_reader::~json_reader() {
bool json_reader::load(std::string_view json_text) { bool json_reader::load(std::string_view json_text) {
reset(); reset();
string_parser_state ps{json_text.begin(), json_text.end()}; string_parser_state ps{json_text.begin(), json_text.end()};
root_ = detail::json::parse(ps, &buf_); root_ = detail::json::parse_shallow(ps, &buf_);
if (ps.code != pec::success) { if (ps.code != pec::success) {
set_error(make_error(ps)); set_error(make_error(ps));
st_ = nullptr; st_ = nullptr;
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_value.hpp"
#include "caf/expected.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
#include "caf/make_counted.hpp"
#include "caf/parser_state.hpp"
namespace caf {
// -- conversion ---------------------------------------------------------------
int64_t json_value::to_integer(int64_t fallback) const {
if (is_integer()) {
return std::get<int64_t>(val_->data);
}
if (is_double()) {
return static_cast<int64_t>(std::get<double>(val_->data));
}
return fallback;
}
double json_value::to_double(double fallback) const {
if (is_double()) {
return std::get<double>(val_->data);
}
if (is_integer()) {
return static_cast<double>(std::get<int64_t>(val_->data));
}
return fallback;
}
bool json_value::to_bool(bool fallback) const {
if (is_bool()) {
return std::get<bool>(val_->data);
}
return fallback;
}
std::string_view json_value::to_string() const {
return to_string(std::string_view{});
}
std::string_view json_value::to_string(std::string_view fallback) const {
if (is_string()) {
return std::get<std::string_view>(val_->data);
}
return fallback;
}
json_object json_value::to_object() const {
return to_object(json_object{});
}
json_object json_value::to_object(json_object fallback) const {
if (is_object()) {
return json_object{&std::get<detail::json::object>(val_->data), storage_};
}
return fallback;
}
json_array json_value::to_array() const {
return to_array(json_array{});
}
json_array json_value::to_array(json_array fallback) const {
if (is_array()) {
return json_array{&std::get<detail::json::array>(val_->data), storage_};
}
return fallback;
}
// -- comparison ---------------------------------------------------------------
bool json_value::equal_to(const json_value& other) const noexcept {
if (val_ == other.val_) {
return true;
}
if (val_ != nullptr && other.val_ != nullptr) {
return *val_ == *other.val_;
}
return false;
}
// -- parsing ------------------------------------------------------------------
expected<json_value> json_value::parse(std::string_view str) {
auto storage = make_counted<detail::json::storage>();
string_parser_state ps{str.begin(), str.end()};
auto root = detail::json::parse(ps, &storage->buf);
if (ps.code != pec::success) {
return {make_error(ps)};
} else {
return {json_value{root, std::move(storage)}};
}
}
expected<json_value> json_value::parse_shallow(std::string_view str) {
auto storage = make_counted<detail::json::storage>();
string_parser_state ps{str.begin(), str.end()};
auto root = detail::json::parse_shallow(ps, &storage->buf);
if (ps.code != pec::success) {
return {make_error(ps)};
} else {
return {json_value{root, std::move(storage)}};
}
}
expected<json_value> json_value::parse_in_situ(std::string& str) {
auto storage = make_counted<detail::json::storage>();
mutable_string_parser_state ps{str.data(), str.data() + str.size()};
auto root = detail::json::parse_in_situ(ps, &storage->buf);
if (ps.code != pec::success) {
return {make_error(ps)};
} else {
return {json_value{root, std::move(storage)}};
}
}
} // namespace caf
#pragma once #pragma once
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/cow_vector.hpp" #include "caf/cow_vector.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/result.hpp" #include "caf/result.hpp"
...@@ -13,6 +16,32 @@ ...@@ -13,6 +16,32 @@
#include <string> #include <string>
#include <utility> #include <utility>
// -- utility for testing serialization round-trips ----------------------------
template <class T>
T deep_copy(const T& val) {
using namespace std::literals;
caf::byte_buffer buf;
{
caf::binary_serializer sink{nullptr, buf};
if (!sink.apply(val)) {
auto msg = "serialization failed in deep_copy: "s;
msg += to_string(sink.get_error());
CAF_RAISE_ERROR(msg.c_str());
}
}
auto result = T{};
{
caf::binary_deserializer sink{nullptr, buf};
if (!sink.apply(result)) {
auto msg = "deserialization failed in deep_copy: "s;
msg += to_string(sink.get_error());
CAF_RAISE_ERROR(msg.c_str());
}
}
return result;
}
// -- forward declarations for all unit test suites ---------------------------- // -- forward declarations for all unit test suites ----------------------------
using float_actor = caf::typed_actor<caf::result<void>(float)>; using float_actor = caf::typed_actor<caf::result<void>(float)>;
......
...@@ -106,6 +106,12 @@ void stringify(std::string& str, size_t, detail::json::null_t) { ...@@ -106,6 +106,12 @@ void stringify(std::string& str, size_t, detail::json::null_t) {
str += "null"; str += "null";
} }
void stringify(std::string& str, size_t, detail::json::undefined_t) {
// The parser never emits undefined objects, but we still need to provide an
// overload for this type.
str += "null";
}
void stringify(std::string& str, size_t indent, const detail::json::array& xs) { void stringify(std::string& str, size_t indent, const detail::json::array& xs) {
if (xs.empty()) { if (xs.empty()) {
str += "[]"; str += "[]";
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_array
#include "caf/json_array.hpp"
#include "core-test.hpp"
#include "caf/json_array.hpp"
#include "caf/json_value.hpp"
using namespace caf;
using namespace std::literals;
TEST_CASE("default-constructed") {
auto arr = json_array{};
CHECK(arr.empty());
CHECK(arr.is_empty());
CHECK(arr.begin() == arr.end());
CHECK_EQ(arr.size(), 0u);
CHECK_EQ(deep_copy(arr), arr);
}
TEST_CASE("from empty array") {
auto arr = json_value::parse("[]")->to_array();
CHECK(arr.empty());
CHECK(arr.is_empty());
CHECK(arr.begin() == arr.end());
CHECK_EQ(arr.size(), 0u);
CHECK_EQ(deep_copy(arr), arr);
}
TEST_CASE("from non-empty array") {
auto arr = json_value::parse(R"_([1, "two", 3.0])_")->to_array();
CHECK(!arr.empty());
CHECK(!arr.is_empty());
CHECK(arr.begin() != arr.end());
REQUIRE_EQ(arr.size(), 3u);
CHECK_EQ((*arr.begin()).to_integer(), 1);
std::vector<json_value> vals;
for (const auto& val : arr) {
vals.emplace_back(val);
}
REQUIRE_EQ(vals.size(), 3u);
CHECK_EQ(vals[0].to_integer(), 1);
CHECK_EQ(vals[1].to_string(), "two");
CHECK_EQ(vals[2].to_double(), 3.0);
CHECK_EQ(deep_copy(arr), arr);
}
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_object
#include "caf/json_object.hpp"
#include "core-test.hpp"
#include "caf/json_array.hpp"
#include "caf/json_value.hpp"
using namespace caf;
using namespace std::literals;
TEST_CASE("default-constructed") {
auto obj = json_object{};
CHECK(obj.empty());
CHECK(obj.is_empty());
CHECK(obj.begin() == obj.end());
CHECK_EQ(obj.size(), 0u);
CHECK(obj.value("foo").is_undefined());
CHECK_EQ(deep_copy(obj), obj);
}
TEST_CASE("from empty object") {
auto obj = json_value::parse("{}")->to_object();
CHECK(obj.empty());
CHECK(obj.is_empty());
CHECK(obj.begin() == obj.end());
CHECK_EQ(obj.size(), 0u);
CHECK(obj.value("foo").is_undefined());
CHECK_EQ(deep_copy(obj), obj);
}
TEST_CASE("from non-empty object") {
auto obj = json_value::parse(R"_({"a": "one", "b": 2})_")->to_object();
CHECK(!obj.empty());
CHECK(!obj.is_empty());
CHECK(obj.begin() != obj.end());
REQUIRE_EQ(obj.size(), 2u);
CHECK_EQ(obj.begin().key(), "a");
CHECK_EQ(obj.begin().value().to_string(), "one");
CHECK_EQ(obj.value("a").to_string(), "one");
CHECK_EQ(obj.value("b").to_integer(), 2);
CHECK(obj.value("c").is_undefined());
std::vector<std::pair<std::string_view, json_value>> vals;
for (const auto& val : obj) {
vals.emplace_back(val);
}
REQUIRE_EQ(vals.size(), 2u);
CHECK_EQ(vals[0].first, "a");
CHECK_EQ(vals[0].second.to_string(), "one");
CHECK_EQ(vals[1].first, "b");
CHECK_EQ(vals[1].second.to_integer(), 2);
CHECK_EQ(deep_copy(obj), obj);
}
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_value
#include "caf/json_value.hpp"
#include "core-test.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
using namespace caf;
using namespace std::literals;
TEST_CASE("default-constructed") {
auto val = json_value{};
CHECK(val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from undefined") {
auto val = json_value::undefined();
CHECK(!val.is_null());
CHECK(val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from integer") {
auto val = unbox(json_value::parse("42"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(val.is_integer());
CHECK(!val.is_double());
CHECK(val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 42);
CHECK_EQ(val.to_double(), 42.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from double") {
auto val = unbox(json_value::parse("42.0"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(val.is_double());
CHECK(val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 42);
CHECK_EQ(val.to_double(), 42.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from bool") {
auto val = unbox(json_value::parse("true"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), true);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
MESSAGE(deep_to_string(val));
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from string") {
auto val = unbox(json_value::parse(R"_("Hello, world!")_"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(val.is_string());
CHECK(!val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), "Hello, world!"sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from empty array") {
auto val = unbox(json_value::parse("[]"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_array().size(), 0u);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from non-empty array") {
auto val = unbox(json_value::parse("[1, 2, 3]"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_array().size(), 3u);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from empty object") {
auto val = unbox(json_value::parse("{}"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from non-empty object") {
auto val = unbox(json_value::parse(R"_({"foo": "bar"})_"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(!val.is_array());
CHECK(val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 1u);
CHECK_EQ(deep_copy(val), val);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment