diff options
Diffstat (limited to 'libbuild2/json.hxx')
-rw-r--r-- | libbuild2/json.hxx | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/libbuild2/json.hxx b/libbuild2/json.hxx new file mode 100644 index 0000000..816b55c --- /dev/null +++ b/libbuild2/json.hxx @@ -0,0 +1,233 @@ +// file : libbuild2/json.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_JSON_HXX +#define LIBBUILD2_JSON_HXX + +#include <libbuild2/types.hxx> +#include <libbuild2/utility.hxx> + +#include <libbuild2/export.hxx> + +namespace butl +{ + namespace json + { + class parser; + class buffer_serializer; + } +} + +namespace build2 +{ + // This JSON representation has two extensions compared to the standard JSON + // model: it distinguishes between signed and unsigned numbers and + // represents an object member as a JSON value rather than, say, a pair of a + // string and value. The latter allows us to use the JSON value itself as an + // element of a container. + // + enum class json_type + { + null, + boolean, + signed_number, + unsigned_number, + string, + array, + object, + }; + + class LIBBUILD2_SYMEXPORT json_value + { + public: + using string_type = build2::string; + using container_type = vector<json_value>; + + json_type type; + + optional<string_type> name; // If present, then this is a member with value. + + union + { + bool boolean; + int64_t signed_number; + uint64_t unsigned_number; + string_type string; + container_type container; // arrary and object + }; + + // Throws invalid_json_input. + // + explicit + json_value (butl::json::parser&); + + explicit + json_value (json_type t = json_type::null) + : type (t) + { + switch (type) + { + case json_type::null: break; + case json_type::boolean: boolean = false; break; + case json_type::signed_number: signed_number = 0; break; + case json_type::unsigned_number: unsigned_number = 0; break; + case json_type::string: new (&string) string_type (); break; + case json_type::array: + case json_type::object: new (&container) container_type (); break; + } + } + + json_value (string_type member_name, json_type t) + : json_value (t) {name = move (member_name);} + + explicit + json_value (std::nullptr_t) + : type (json_type::null) {} + + json_value (string_type member_name, std::nullptr_t v) + : json_value (v) {name = move (member_name);} + + explicit + json_value (bool v) + : type (json_type::boolean), boolean (v) {} + + json_value (string_type member_name, bool v) + : json_value (v) {name = move (member_name);} + + explicit + json_value (int64_t v) + : type (json_type::signed_number), signed_number (v) {} + + json_value (string_type member_name, int64_t v) + : json_value (v) {name = move (member_name);} + + explicit + json_value (uint64_t v) + : type (json_type::unsigned_number), unsigned_number (v) {} + + json_value (string_type member_name, uint64_t v) + : json_value (v) {name = move (member_name);} + + explicit + json_value (string_type v) + : type (json_type::string), string (move (v)) {} + + json_value (string_type member_name, string_type v) + : json_value (move (v)) {name = move (member_name);} + + explicit + json_value (container_type v, json_type t) + : type (t), container (move (v)) + { +#ifndef NDEBUG + assert (t == json_type::array || t == json_type::object); + + for (const json_value& e: container) + assert (e.name.has_value () == (t == json_type::object)); +#endif + } + + json_value (string_type member_name, container_type v, json_type t) + : json_value (move (v), t) {name = move (member_name);} + + // Note that the moved-from value becomes null. + // + json_value (json_value&& v) noexcept + : type (v.type), name (move (v.name)) + { + switch (type) + { + case json_type::null: + break; + case json_type::boolean: + boolean = v.boolean; + break; + case json_type::signed_number: + signed_number = v.signed_number; + break; + case json_type::unsigned_number: + unsigned_number = v.unsigned_number; + break; + case json_type::string: + new (&string) string_type (move (v.string)); + v.string.~string_type (); + break; + case json_type::array: + case json_type::object: + new (&container) container_type (move (v.container)); + v.container.~container_type (); + break; + } + + v.type = json_type::null; + v.name = nullopt; + } + + json_value& operator= (json_value&& v) noexcept + { + if (this != &v) + { + this->~json_value (); + new (this) json_value (move (v)); + } + return *this; + } + + json_value (const json_value& v) + : type (v.type), name (v.name) + { + switch (type) + { + case json_type::null: + break; + case json_type::boolean: + boolean = v.boolean; + break; + case json_type::signed_number: + signed_number = v.signed_number; + break; + case json_type::unsigned_number: + unsigned_number = v.unsigned_number; + break; + case json_type::string: + new (&string) string_type (v.string); + break; + case json_type::array: + case json_type::object: + new (&container) container_type (v.container); + break; + } + } + + json_value& operator= (const json_value& v) + { + if (this != &v) + { + this->~json_value (); + new (this) json_value (v); + } + return *this; + } + + ~json_value () noexcept + { + switch (type) + { + case json_type::null: + case json_type::boolean: + case json_type::signed_number: + case json_type::unsigned_number: break; + case json_type::string: string.~string_type (); break; + case json_type::array: + case json_type::object: container.~container_type (); break; + } + } + }; + + // Throws invalid_json_output. + // + LIBBUILD2_SYMEXPORT void + serialize (butl::json::buffer_serializer&, const json_value&); +} + +#endif // LIBBUILD2_JSON_HXX |