aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-02-13 11:05:37 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-02-13 11:05:37 +0200
commitfc1fb583de222caecdb956c623765b6a1a047937 (patch)
tree144391aab338f11687a5584287d4e32943e21fd6
parent20e49b4e63779abc0e25bec4c74399a83ec8a83c (diff)
Extend json_value C++ interface
-rw-r--r--libbuild2/json.cxx62
-rw-r--r--libbuild2/json.hxx69
-rw-r--r--libbuild2/json.ixx127
3 files changed, 240 insertions, 18 deletions
diff --git a/libbuild2/json.cxx b/libbuild2/json.cxx
index f368679..6295b59 100644
--- a/libbuild2/json.cxx
+++ b/libbuild2/json.cxx
@@ -61,6 +61,17 @@ namespace build2
const json_value null_json_value (json_type::null);
[[noreturn]] void
+ json_as_throw (json_type t, json_type e)
+ {
+ string m;
+ m = "expected ";
+ m += to_string (e, true);
+ m += " instead of ";
+ m += to_string (t, true);
+ throw invalid_argument (move (m));
+ }
+
+ [[noreturn]] static void
at_throw (json_type t, json_type e, bool index)
{
string m;
@@ -68,16 +79,16 @@ namespace build2
if (t != e && t != json_type::null)
{
m = "expected ";
- m += to_string (e);
+ m += to_string (e, true);
m += " instead of ";
- m += to_string (t);
+ m += to_string (t, true);
throw invalid_argument (move (m));
}
else
{
m = index ? "index" : "name";
m += " out of range in ";
- m += to_string (e);
+ m += to_string (e, true);
throw std::out_of_range (move (m));
}
}
@@ -106,6 +117,7 @@ namespace build2
at_throw (type, json_type::array, true);
}
+#if 0
const json_value& json_value::
operator[] (size_t index) const
{
@@ -145,6 +157,7 @@ namespace build2
at_throw (type, json_type::array, true);
}
+#endif
const json_value& json_value::
at (const char* name) const
@@ -182,6 +195,40 @@ namespace build2
at_throw (type, json_type::object, false);
}
+ const json_value* json_value::
+ find (const char* name) const
+ {
+ if (type == json_type::object)
+ {
+ auto i (find_if (object.begin (), object.end (),
+ [name] (const json_member& m)
+ {
+ return m.name == name;
+ }));
+ return i != object.end () ? &i->value : nullptr;
+ }
+
+ at_throw (type, json_type::object, false);
+ }
+
+ json_value* json_value::
+ find (const char* name)
+ {
+ if (type == json_type::object)
+ {
+ auto i (find_if (object.begin (), object.end (),
+ [name] (const json_member& m)
+ {
+ return m.name == name;
+ }));
+
+ return i != object.end () ? &i->value : nullptr;
+ }
+
+ at_throw (type, json_type::object, false);
+ }
+
+#if 0
const json_value& json_value::
operator[] (const char* name) const
{
@@ -229,6 +276,7 @@ namespace build2
at_throw (type, json_type::object, false);
}
+#endif
int json_value::
compare (const json_value& v) const noexcept
@@ -683,8 +731,8 @@ namespace build2
p.line (),
p.column (),
p.position (),
- string_type ("expected ") + to_string (*et) + " instead of " +
- to_string (t));
+ string_type ("expected ") + to_string (*et, true) + " instead of " +
+ to_string (t, true));
}
switch (t)
@@ -782,8 +830,8 @@ namespace build2
throw invalid_json_output (
nullopt,
invalid_json_output::error_code::invalid_value,
- string_type ("expected ") + to_string (*et) + " instead of " +
- to_string (type));
+ string_type ("expected ") + to_string (*et, true) + " instead of " +
+ to_string (type, true));
}
switch (type)
diff --git a/libbuild2/json.hxx b/libbuild2/json.hxx
index 1f2694d..538c87d 100644
--- a/libbuild2/json.hxx
+++ b/libbuild2/json.hxx
@@ -87,6 +87,8 @@ namespace build2
json_type type;
+ // Unchecked value access.
+ //
union
{
bool boolean;
@@ -97,6 +99,32 @@ namespace build2
object_type object;
};
+ // Checked value access.
+ //
+ // If the type matches, return the corresponding member of the union.
+ // Otherwise throw std::invalid_argument.
+ //
+ bool as_bool () const;
+ bool& as_bool ();
+
+ int64_t as_int64 () const;
+ int64_t& as_int64 ();
+
+ uint64_t as_uint64 () const;
+ uint64_t& as_uint64 ();
+
+ const string_type& as_string () const;
+ string_type& as_string ();
+
+ const array_type& as_array () const;
+ array_type& as_array ();
+
+ const object_type& as_object () const;
+ object_type& as_object ();
+
+
+ // Construction.
+ //
explicit
json_value (json_type = json_type::null) noexcept;
@@ -161,7 +189,7 @@ namespace build2
// position (filling any missing elements in between with nulls) and
// returns that. All three functions throw std::invalid_argument if the
// value is not an array or null with null treated as (missing) array
- // rather than wrong value type (and with at() functons throwing
+ // rather than wrong value type (and with at() functions throwing
// out_of_range in this case).
//
// Note that non-const operator[] will not only insert a new element but
@@ -174,8 +202,8 @@ namespace build2
// can lead to inefficiencies or even undesirable semantics during
// otherwise read-only access of a non-const object due to the potential
// insertion of null values for missing array elements. As a result, it's
- // recommended to alwas use a const reference for read-only access (or use
- // the at() interface if this is deemed too easy to forget).
+ // recommended to always use a const reference for read-only access (or
+ // use the at() interface if this is deemed too easy to forget).
//
const json_value&
at (size_t) const;
@@ -183,22 +211,25 @@ namespace build2
json_value&
at (size_t);
+#if 0
const json_value&
operator[] (size_t) const;
json_value&
operator[] (size_t);
+#endif
// Object member access.
//
// If a member with the specified name is not found in the object, the
- // at() functions throw std::out_of_range, the const operator[] returns
- // null_json_value, and the non-const operator[] adds a new member with
- // the specified name and null value and returns that value. All three
- // functions throw std::invalid_argument if the value is not an array or
- // null with null treated as (missing) object rather than wrong value type
- // (and with at() functons throwing out_of_range in this case).
+ // at() functions throw std::out_of_range, the find() function returns
+ // NULL, the const operator[] returns null_json_value, and the non-const
+ // operator[] adds a new member with the specified name and null value and
+ // returns that value. All three functions throw std::invalid_argument if
+ // the value is not an object or null with null treated as (missing)
+ // object rather than wrong value type (and with at() functions throwing
+ // out_of_range in this case).
//
// Note that non-const operator[] will not only insert a new member but
// will also turn the value it is called upon into object if it is null.
@@ -210,8 +241,8 @@ namespace build2
// can lead to inefficiencies or even undesirable semantics during
// otherwise read-only access of a non-const object due to the potential
// insertion of null values for missing object members. As a result, it's
- // recommended to alwas use a const reference for read-only access (or use
- // the at() interface if this is deemed too easy to forget).
+ // recommended to always use a const reference for read-only access (or
+ // use the at() interface if this is deemed too easy to forget).
//
const json_value&
at (const char*) const;
@@ -219,11 +250,19 @@ namespace build2
json_value&
at (const char*);
+ const json_value*
+ find (const char*) const;
+
+ json_value*
+ find (const char*);
+
+#if 0
const json_value&
operator[] (const char*) const;
json_value&
operator[] (const char*);
+#endif
const json_value&
at (const string_type&) const;
@@ -231,11 +270,19 @@ namespace build2
json_value&
at (const string_type&);
+ const json_value*
+ find (const string_type&) const;
+
+ json_value*
+ find (const string_type&);
+
+#if 0
const json_value&
operator[] (const string_type&) const;
json_value&
operator[] (const string_type&);
+#endif
// Note that the moved-from value becomes JSON null value.
//
diff --git a/libbuild2/json.ixx b/libbuild2/json.ixx
index c2b8845..76cd00a 100644
--- a/libbuild2/json.ixx
+++ b/libbuild2/json.ixx
@@ -3,6 +3,119 @@
namespace build2
{
+ [[noreturn]] LIBBUILD2_SYMEXPORT void
+ json_as_throw (json_type actual, json_type expected);
+
+ inline bool json_value::
+ as_bool () const
+ {
+ if (type == json_type::boolean)
+ return boolean;
+
+ json_as_throw (type, json_type::boolean);
+ }
+
+ inline bool& json_value::
+ as_bool ()
+ {
+ if (type == json_type::boolean)
+ return boolean;
+
+ json_as_throw (type, json_type::boolean);
+ }
+
+ inline int64_t json_value::
+ as_int64 () const
+ {
+ if (type == json_type::signed_number)
+ return signed_number;
+
+ json_as_throw (type, json_type::signed_number);
+ }
+
+ inline int64_t& json_value::
+ as_int64 ()
+ {
+ if (type == json_type::signed_number)
+ return signed_number;
+
+ json_as_throw (type, json_type::signed_number);
+ }
+
+ inline uint64_t json_value::
+ as_uint64 () const
+ {
+ if (type == json_type::unsigned_number ||
+ type == json_type::hexadecimal_number)
+ return unsigned_number;
+
+ json_as_throw (type, json_type::unsigned_number);
+ }
+
+ inline uint64_t& json_value::
+ as_uint64 ()
+ {
+ if (type == json_type::unsigned_number ||
+ type == json_type::hexadecimal_number)
+ return unsigned_number;
+
+ json_as_throw (type, json_type::unsigned_number);
+ }
+
+ inline const string& json_value::
+ as_string () const
+ {
+ if (type == json_type::string)
+ return string;
+
+ json_as_throw (type, json_type::string);
+ }
+
+ inline string& json_value::
+ as_string ()
+ {
+ if (type == json_type::string)
+ return string;
+
+ json_as_throw (type, json_type::string);
+ }
+
+ inline const json_value::array_type& json_value::
+ as_array () const
+ {
+ if (type == json_type::array)
+ return array;
+
+ json_as_throw (type, json_type::array);
+ }
+
+ inline json_value::array_type& json_value::
+ as_array ()
+ {
+ if (type == json_type::array)
+ return array;
+
+ json_as_throw (type, json_type::array);
+ }
+
+ inline const json_value::object_type& json_value::
+ as_object () const
+ {
+ if (type == json_type::object)
+ return object;
+
+ json_as_throw (type, json_type::object);
+ }
+
+ inline json_value::object_type& json_value::
+ as_object ()
+ {
+ if (type == json_type::object)
+ return object;
+
+ json_as_throw (type, json_type::object);
+ }
+
inline json_value::
~json_value () noexcept
{
@@ -81,6 +194,19 @@ namespace build2
return at (n.c_str ());
}
+ inline const json_value* json_value::
+ find (const string_type& n) const
+ {
+ return find (n.c_str ());
+ }
+
+ inline json_value* json_value::
+ find (const string_type& n)
+ {
+ return find (n.c_str ());
+ }
+
+#if 0
inline const json_value& json_value::
operator[] (const string_type& n) const
{
@@ -92,6 +218,7 @@ namespace build2
{
return operator[] (n.c_str ());
}
+#endif
inline json_value::
json_value (json_value&& v) noexcept