Commit a3b6ddc5 authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/neverlord/json-api'

parents 7168be7a c5bb6a65
......@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file. The format
is based on [Keep a Changelog](https://keepachangelog.com).
## [Unreleased]
### Added
- The new classes `json_value`, `json_array` and `json_object` allow working
with JSON inputs directly. Actors can also pass around JSON values safely.
## [0.19.0-rc.1] - 2020-10-31
### Added
......
......@@ -161,7 +161,11 @@ caf_add_component(
src/ipv6_address.cpp
src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp
src/json_array.cpp
src/json_builder.cpp
src/json_object.cpp
src/json_reader.cpp
src/json_value.cpp
src/json_writer.cpp
src/load_inspector.cpp
src/local_actor.cpp
......@@ -313,7 +317,11 @@ caf_add_component(
ipv6_address
ipv6_endpoint
ipv6_subnet
json_array
json_builder
json_object
json_reader
json_value
json_writer
load_inspector
logger
......
This diff is collapsed.
......@@ -55,20 +55,20 @@ public:
using other = allocator<U>;
};
explicit allocator(monotonic_buffer_resource* mbr) : mbr_(mbr) {
constexpr explicit allocator(monotonic_buffer_resource* mbr) : mbr_(mbr) {
// nop
}
allocator() : mbr_(nullptr) {
constexpr allocator() : mbr_(nullptr) {
// nop
}
allocator(const allocator&) = default;
constexpr allocator(const allocator&) = default;
allocator& operator=(const allocator&) = default;
constexpr allocator& operator=(const allocator&) = default;
template <class U>
allocator(const allocator<U>& other) : mbr_(other.resource()) {
constexpr allocator(const allocator<U>& other) : mbr_(other.resource()) {
// nop
}
......
......@@ -110,6 +110,11 @@ class ipv4_subnet;
class ipv6_address;
class ipv6_endpoint;
class ipv6_subnet;
class json_array;
class json_object;
class json_reader;
class json_value;
class json_writer;
class local_actor;
class mailbox_element;
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_builder.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()};
}
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *arr_, indentation_factor);
}
// -- 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_;
};
// -- free functions -----------------------------------------------------------
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);
}
/// @relates json_array
CAF_CORE_EXPORT std::string to_string(const json_array& arr);
} // 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/json_value.hpp"
#include "caf/json_writer.hpp"
#include "caf/serializer.hpp"
namespace caf {
/// Serializes an inspectable object to a @ref json_value.
class json_builder : public serializer {
public:
// -- member types -----------------------------------------------------------
using super = serializer;
using type = json_writer::type;
// -- constructors, destructors, and assignment operators --------------------
json_builder();
explicit json_builder(actor_system& sys);
explicit json_builder(execution_unit* ctx);
json_builder(const json_builder&) = delete;
json_builder& operator=(const json_builder&) = delete;
~json_builder() override;
// -- properties -------------------------------------------------------------
/// Returns whether the writer omits empty fields entirely (true) or renders
/// empty fields as `$field: null` (false).
[[nodiscard]] bool skip_empty_fields() const noexcept {
return skip_empty_fields_;
}
/// Configures whether the writer omits empty fields.
void skip_empty_fields(bool value) noexcept {
skip_empty_fields_ = value;
}
/// Returns whether the writer omits '@type' annotations for JSON objects.
[[nodiscard]] bool skip_object_type_annotation() const noexcept {
return skip_object_type_annotation_;
}
/// Configures whether the writer omits '@type' annotations for JSON objects.
void skip_object_type_annotation(bool value) noexcept {
skip_object_type_annotation_ = value;
}
/// Returns the suffix for generating type annotation fields for variant
/// fields. For example, CAF inserts field called "@foo${field_type_suffix}"
/// for a variant field called "foo".
[[nodiscard]] std::string_view field_type_suffix() const noexcept {
return field_type_suffix_;
}
/// Configures whether the writer omits empty fields.
void field_type_suffix(std::string_view suffix) noexcept {
field_type_suffix_ = suffix;
}
// -- modifiers --------------------------------------------------------------
/// Restores the writer to its initial state.
void reset();
/// Seals the JSON value, i.e., rendering it immutable, and returns it. After
/// calling this member function, the @ref json_builder is in a moved-from
/// state and users may only call @c reset to start a new building process or
/// destroy this instance.
json_value seal();
// -- overrides --------------------------------------------------------------
bool begin_object(type_id_t type, std::string_view name) override;
bool end_object() override;
bool begin_field(std::string_view) override;
bool begin_field(std::string_view name, bool is_present) override;
bool begin_field(std::string_view name, span<const type_id_t> types,
size_t index) override;
bool begin_field(std::string_view name, bool is_present,
span<const type_id_t> types, size_t index) override;
bool end_field() override;
bool begin_tuple(size_t size) override;
bool end_tuple() override;
bool begin_key_value_pair() override;
bool end_key_value_pair() override;
bool begin_sequence(size_t size) override;
bool end_sequence() override;
bool begin_associative_array(size_t size) override;
bool end_associative_array() override;
bool value(std::byte x) override;
bool value(bool x) override;
bool value(int8_t x) override;
bool value(uint8_t x) override;
bool value(int16_t x) override;
bool value(uint16_t x) override;
bool value(int32_t x) override;
bool value(uint32_t x) override;
bool value(int64_t x) override;
bool value(uint64_t x) override;
bool value(float x) override;
bool value(double x) override;
bool value(long double x) override;
bool value(std::string_view x) override;
bool value(const std::u16string& x) override;
bool value(const std::u32string& x) override;
bool value(span<const std::byte> x) override;
private:
// -- implementation details -------------------------------------------------
template <class T>
bool number(T);
using key_type = std::string_view;
// -- state management -------------------------------------------------------
void init();
// Returns the current top of the stack or `null` if empty.
type top();
// Returns the current top of the stack or `null` if empty.
template <class T = detail::json::value>
T* top_ptr();
// Returns the current top-level object.
detail::json::object* top_obj();
// Enters a new level of nesting.
void push(detail::json::value*, type);
// Enters a new level of nesting with type member.
void push(detail::json::value::member*);
// Enters a new level of nesting with type key.
void push(key_type*);
// Backs up one level of nesting.
bool pop();
// Backs up one level of nesting but checks that current top is `t` before.
bool pop_if(type t);
// Sets an error reason that the inspector failed to write a t.
void fail(type t);
// Checks whether any element in the stack has the type `object`.
bool inside_object() const noexcept;
// -- member variables -------------------------------------------------------
// Our output.
detail::json::value* val_;
// Storage for the assembled output.
detail::json::storage_ptr storage_;
struct entry {
union {
detail::json::value* val_ptr;
detail::json::member* mem_ptr;
key_type* key_ptr;
};
type t;
entry(detail::json::value* ptr, type ptr_type) noexcept {
val_ptr = ptr;
t = ptr_type;
}
explicit entry(detail::json::member* ptr) noexcept {
mem_ptr = ptr;
t = type::member;
}
explicit entry(key_type* ptr) noexcept {
key_ptr = ptr;
t = type::key;
}
entry(const entry&) noexcept = default;
entry& operator=(const entry&) noexcept = default;
};
// Bookkeeping for where we are in the current object.
std::vector<entry> stack_;
// Configures whether we omit empty fields entirely (true) or render empty
// fields as `$field: null` (false).
bool skip_empty_fields_ = json_writer::skip_empty_fields_default;
// Configures whether we omit the top-level '@type' annotation.
bool skip_object_type_annotation_ = false;
std::string_view field_type_suffix_ = json_writer::field_type_suffix_default;
};
} // 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()};
}
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *obj_, indentation_factor);
}
// -- 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);
}
/// @relates json_object
CAF_CORE_EXPORT std::string to_string(const json_object& val);
} // namespace caf
......@@ -133,6 +133,17 @@ public:
/// @note Implicitly calls `reset`.
bool load(std::string_view json_text);
/// Parses the content of the file under the given @p path. After loading the
/// content of the JSON file, the reader is ready for attempting to
/// deserialize inspectable objects.
/// @note Implicitly calls `reset`.
bool load_file(const char* path);
/// @copydoc load_file
bool load_file(const std::string& path) {
return load_file(path.c_str());
}
/// Reverts the state of the reader back to where it was after calling `load`.
/// @post The reader is ready for attempting to deserialize another
/// inspectable object.
......
// 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);
/// Attempts to parse the content of the file at @p path as JSON input into a
/// self-contained value.
static expected<json_value> parse_file(const char* path);
/// @copydoc parse_file
static expected<json_value> parse_file(const std::string& path);
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *val_, indentation_factor);
}
// -- 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_;
};
// -- free functions -----------------------------------------------------------
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);
}
/// @relates json_value
CAF_CORE_EXPORT std::string to_string(const json_value& val);
} // namespace caf
......@@ -210,7 +210,7 @@ private:
void init();
// Returns the current top of the stack or `null_literal` if empty.
// Returns the current top of the stack or `null` if empty.
type top();
// Enters a new level of nesting.
......@@ -300,4 +300,12 @@ private:
const type_id_mapper* mapper_ = &default_mapper_;
};
/// @relates json_writer::type
CAF_CORE_EXPORT std::string_view as_json_type_name(json_writer::type t);
/// @relates json_writer::type
constexpr bool can_morph(json_writer::type from, json_writer::type to) {
return from == json_writer::type::element && to != json_writer::type::member;
}
} // namespace caf
......@@ -16,6 +16,8 @@ namespace caf {
/// Stores all information necessary for implementing an FSM-based parser.
template <class Iterator, class Sentinel>
struct parser_state {
using iterator_type = Iterator;
/// Current position of the parser.
Iterator i;
......
......@@ -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_u16string))
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::error))
CAF_ADD_TYPE_ID(core_module, (caf::exit_msg))
......@@ -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_endpoint))
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_id))
CAF_ADD_TYPE_ID(core_module, (caf::node_down_msg))
......@@ -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::pec))
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::stream))
CAF_ADD_TYPE_ID(core_module, (caf::stream_abort_msg))
......
This diff is collapsed.
......@@ -21,6 +21,9 @@
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.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_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_array.hpp"
namespace caf {
std::string to_string(const json_array& arr) {
std::string result;
arr.print_to(result);
return result;
}
} // namespace caf
This diff is collapsed.
// 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();
}
std::string to_string(const json_object& obj) {
std::string result;
obj.print_to(result);
return result;
}
} // namespace caf
......@@ -8,6 +8,8 @@
#include "caf/detail/print.hpp"
#include "caf/string_algorithms.hpp"
#include <fstream>
namespace {
static constexpr const char class_name[] = "caf::json_reader";
......@@ -145,19 +147,41 @@ json_reader::~json_reader() {
bool json_reader::load(std::string_view json_text) {
reset();
string_parser_state ps{json_text.begin(), json_text.end()};
root_ = detail::json::parse_shallow(ps, &buf_);
if (ps.code != pec::success) {
set_error(make_error(ps));
st_ = nullptr;
return false;
}
err_.reset();
detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
st_->reserve(16);
st_->emplace_back(root_);
return true;
}
bool json_reader::load_file(const char* path) {
using iterator_t = std::istreambuf_iterator<char>;
reset();
std::ifstream input{path};
if (!input.is_open()) {
emplace_error(sec::cannot_open_file);
return false;
}
detail::json::file_parser_state ps{iterator_t{input}, iterator_t{}};
root_ = detail::json::parse(ps, &buf_);
if (ps.code != pec::success) {
set_error(make_error(ps));
st_ = nullptr;
return false;
} else {
err_.reset();
detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
st_->reserve(16);
st_->emplace_back(root_);
return true;
}
err_.reset();
detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
st_->reserve(16);
st_->emplace_back(root_);
return true;
}
void json_reader::revert() {
......
// 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"
#include <fstream>
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 {json_value{root, std::move(storage)}};
return {make_error(ps)};
}
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 {json_value{root, std::move(storage)}};
return {make_error(ps)};
}
expected<json_value> json_value::parse_in_situ(std::string& str) {
auto storage = make_counted<detail::json::storage>();
detail::json::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 {json_value{root, std::move(storage)}};
return {make_error(ps)};
}
expected<json_value> json_value::parse_file(const char* path) {
using iterator_t = std::istreambuf_iterator<char>;
std::ifstream input{path};
if (!input.is_open())
return make_error(sec::cannot_open_file);
auto storage = make_counted<detail::json::storage>();
detail::json::file_parser_state ps{iterator_t{input}, iterator_t{}};
auto root = detail::json::parse(ps, &storage->buf);
if (ps.code == pec::success)
return {json_value{root, std::move(storage)}};
return {make_error(ps)};
}
expected<json_value> json_value::parse_file(const std::string& path) {
return parse_file(path.c_str());
}
// -- free functions -----------------------------------------------------------
std::string to_string(const json_value& val) {
std::string result;
val.print_to(result);
return result;
}
} // namespace caf
......@@ -13,17 +13,9 @@ namespace {
static constexpr const char class_name[] = "caf::json_writer";
constexpr bool can_morph(json_writer::type from, json_writer::type to) {
return from == json_writer::type::element && to != json_writer::type::member;
}
constexpr const char* json_type_names[] = {"element", "object", "member",
"array", "string", "number",
"bool", "null"};
constexpr const char* json_type_name(json_writer::type t) {
return json_type_names[static_cast<uint8_t>(t)];
}
constexpr std::string_view json_type_names[] = {"element", "object", "member",
"array", "string", "number",
"bool", "null"};
char last_non_ws_char(const std::vector<char>& buf) {
auto not_ws = [](char c) { return !std::isspace(c); };
......@@ -104,7 +96,7 @@ bool json_writer::begin_object(type_id_t id, std::string_view name) {
pop();
return true;
};
if (inside_object() || skip_object_type_annotation_)
if (skip_object_type_annotation_ || inside_object())
return begin_associative_array(0);
else
return begin_associative_array(0) // Put opening paren, ...
......@@ -140,7 +132,7 @@ bool json_writer::begin_field(std::string_view name, bool is_present) {
return true;
default: {
std::string str = "expected object, found ";
str += json_type_name(t);
str += as_json_type_name(t);
emplace_error(sec::runtime_error, class_name, __func__, std::move(str));
return false;
}
......@@ -222,7 +214,7 @@ bool json_writer::begin_key_value_pair() {
return true;
default: {
std::string str = "expected object, found ";
str += json_type_name(t);
str += as_json_type_name(t);
emplace_error(sec::runtime_error, class_name, __func__, std::move(str));
return false;
}
......@@ -489,12 +481,12 @@ bool json_writer::pop_if(type t) {
return true;
} else {
std::string str = "pop_if failed: expected ";
str += json_type_name(t);
str += as_json_type_name(t);
if (stack_.empty()) {
str += ", found an empty stack";
} else {
str += ", found ";
str += json_type_name(stack_.back().t);
str += as_json_type_name(stack_.back().t);
}
emplace_error(sec::runtime_error, std::move(str));
return false;
......@@ -509,13 +501,13 @@ bool json_writer::pop_if_next(type t) {
return true;
} else {
std::string str = "pop_if_next failed: expected ";
str += json_type_name(t);
str += as_json_type_name(t);
if (stack_.size() < 2) {
str += ", found a stack of size ";
detail::print(str, stack_.size());
} else {
str += ", found ";
str += json_type_name(stack_[stack_.size() - 2].t);
str += as_json_type_name(stack_[stack_.size() - 2].t);
}
emplace_error(sec::runtime_error, std::move(str));
return false;
......@@ -536,9 +528,9 @@ bool json_writer::morph(type t, type& prev) {
return true;
} else {
std::string str = "cannot convert ";
str += json_type_name(stack_.back().t);
str += as_json_type_name(stack_.back().t);
str += " to ";
str += json_type_name(t);
str += as_json_type_name(t);
emplace_error(sec::runtime_error, std::move(str));
return false;
}
......@@ -555,7 +547,7 @@ void json_writer::unsafe_morph(type t) {
void json_writer::fail(type t) {
std::string str = "failed to write a ";
str += json_type_name(t);
str += as_json_type_name(t);
str += ": invalid position (begin/end mismatch?)";
emplace_error(sec::runtime_error, std::move(str));
}
......@@ -589,4 +581,10 @@ void json_writer::sep() {
}
}
// -- free functions -----------------------------------------------------------
std::string_view as_json_type_name(json_writer::type t) {
return json_type_names[static_cast<uint8_t>(t)];
}
} // namespace caf
#pragma once
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/cow_vector.hpp"
#include "caf/fwd.hpp"
#include "caf/result.hpp"
......@@ -13,6 +16,32 @@
#include <string>
#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 ----------------------------
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) {
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) {
if (xs.empty()) {
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;
namespace {
std::string printed(const json_array& arr) {
std::string result;
arr.print_to(result, 2);
return result;
}
} // namespace
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(to_string(arr), "[]");
CHECK_EQ(printed(arr), "[]");
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(to_string(arr), "[]");
CHECK_EQ(printed(arr), "[]");
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(to_string(arr), R"_([1, "two", 3])_");
CHECK_EQ(printed(arr), "[\n 1,\n \"two\",\n 3\n]");
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_builder
#include "caf/json_builder.hpp"
#include "core-test.hpp"
#include "caf/json_value.hpp"
#include <string_view>
using namespace caf;
using namespace std::literals;
namespace {
struct fixture {
fixture() {
builder.skip_object_type_annotation(true);
}
std::string printed(const json_value& val, size_t indentation_factor = 0) {
std::string result;
val.print_to(result, indentation_factor);
return result;
}
json_builder builder;
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
TEST_CASE("empty JSON value") {
auto val = builder.seal();
CHECK(val.is_null());
}
TEST_CASE("integer") {
CHECK(builder.value(int32_t{42}));
auto val = builder.seal();
CHECK(val.is_integer());
CHECK_EQ(val.to_integer(), 42);
}
TEST_CASE("floating point") {
CHECK(builder.value(4.2));
auto val = builder.seal();
CHECK(val.is_double());
CHECK_EQ(val.to_double(), 4.2);
}
TEST_CASE("boolean") {
CHECK(builder.value(true));
auto val = builder.seal();
CHECK(val.is_bool());
CHECK_EQ(val.to_bool(), true);
}
TEST_CASE("string") {
CHECK(builder.value("Hello, world!"sv));
auto val = builder.seal();
CHECK(val.is_string());
CHECK_EQ(val.to_string(), "Hello, world!"sv);
}
TEST_CASE("array") {
auto xs = std::vector{1, 2, 3};
CHECK(builder.apply(xs));
auto val = builder.seal();
CHECK(val.is_array());
CHECK_EQ(printed(val), "[1, 2, 3]"sv);
}
TEST_CASE("flat object") {
auto req = my_request{10, 20};
if (!CHECK(builder.apply(req))) {
MESSAGE("builder: " << builder.get_error());
}
auto val = builder.seal();
CHECK(val.is_object());
CHECK_EQ(printed(val), R"_({"a": 10, "b": 20})_");
}
TEST_CASE("flat object with type annotation") {
builder.skip_object_type_annotation(false);
auto req = my_request{10, 20};
if (!CHECK(builder.apply(req))) {
MESSAGE("builder: " << builder.get_error());
}
auto val = builder.seal();
CHECK(val.is_object());
CHECK_EQ(printed(val), R"_({"@type": "my_request", "a": 10, "b": 20})_");
}
namespace {
constexpr std::string_view rect_str = R"_({
"top-left": {
"x": 10,
"y": 10
},
"bottom-right": {
"x": 20,
"y": 20
}
})_";
} // namespace
TEST_CASE("nested object") {
auto rect = rectangle{{10, 10}, {20, 20}};
if (!CHECK(builder.apply(rect))) {
MESSAGE("builder: " << builder.get_error());
}
auto val = builder.seal();
CHECK(val.is_object());
CHECK_EQ(printed(val, 2), rect_str);
}
namespace {
constexpr std::string_view annotated_rect_str = R"_({
"@type": "rectangle",
"top-left": {
"x": 10,
"y": 10
},
"bottom-right": {
"x": 20,
"y": 20
}
})_";
} // namespace
TEST_CASE("nested object with type annotation") {
builder.skip_object_type_annotation(false);
auto rect = rectangle{{10, 10}, {20, 20}};
if (!CHECK(builder.apply(rect))) {
MESSAGE("builder: " << builder.get_error());
}
auto val = builder.seal();
CHECK(val.is_object());
CHECK_EQ(printed(val, 2), annotated_rect_str);
}
END_FIXTURE_SCOPE()
// 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;
namespace {
std::string printed(const json_object& obj) {
std::string result;
obj.print_to(result, 2);
return result;
}
} // namespace
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(to_string(obj), "{}");
CHECK_EQ(printed(obj), "{}");
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(to_string(obj), "{}");
CHECK_EQ(printed(obj), "{}");
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(to_string(obj), R"_({"a": "one", "b": 2})_");
CHECK_EQ(printed(obj), "{\n \"a\": \"one\",\n \"b\": 2\n}");
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;
namespace {
std::string printed(const json_value& val) {
std::string result;
val.print_to(result, 2);
return result;
}
} // namespace
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(to_string(val), "null");
CHECK_EQ(printed(val), "null");
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(to_string(val), "null");
CHECK_EQ(printed(val), "null");
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(to_string(val), "42");
CHECK_EQ(printed(val), "42");
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(to_string(val), "42");
CHECK_EQ(printed(val), "42");
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);
CHECK_EQ(to_string(val), "true");
CHECK_EQ(printed(val), "true");
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(to_string(val), R"_("Hello, world!")_");
CHECK_EQ(printed(val), R"_("Hello, world!")_");
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(to_string(val), "[]");
CHECK_EQ(printed(val), "[]");
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from array of size 1") {
auto val = unbox(json_value::parse("[1]"));
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(), 1u);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "[1]");
CHECK_EQ(printed(val), "[\n 1\n]");
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from array of size 3") {
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(to_string(val), "[1, 2, 3]");
CHECK_EQ(printed(val), "[\n 1,\n 2,\n 3\n]");
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(to_string(val), "{}");
CHECK_EQ(printed(val), "{}");
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(to_string(val), R"_({"foo": "bar"})_");
CHECK_EQ(printed(val), "{\n \"foo\": \"bar\"\n}");
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