#include <include/types.h>
#include <boost/algorithm/string.hpp>
+#include <boost/tokenizer.hpp>
using namespace json_spirit;
string json_string(buf_, len);
success = read(json_string, data);
- if (success)
+ if (success) {
handle_value(data);
- else
+ if (data.type() != obj_type &&
+ data.type() != array_type) {
+ if (data.type() == str_type)
+ data_string = data.get_str();
+ else
+ data_string = write(data, raw_utf8);
+ }
+ } else {
set_failure();
+ }
return success;
}
return i->second;
}
+const JSONFormattable& JSONFormattable::operator[](size_t index) const
+{
+ if (index >= arr.size()) {
+ return default_formattable;
+ }
+ return arr[index];
+}
+
+JSONFormattable& JSONFormattable::operator[](const string& name)
+{
+ auto i = obj.find(name);
+ if (i == obj.end()) {
+ return default_formattable;
+ }
+ return i->second;
+}
+
+JSONFormattable& JSONFormattable::operator[](size_t index)
+{
+ if (index >= arr.size()) {
+ return default_formattable;
+ }
+ return arr[index];
+}
+
+bool JSONFormattable::exists(const string& name) const
+{
+ auto i = obj.find(name);
+ return (i != obj.end());
+}
+
+bool JSONFormattable::exists(size_t index) const
+{
+ return (index < arr.size());
+}
+
bool JSONFormattable::find(const string& name, string *val) const
{
auto i = obj.find(name);
return (*this)[name].def(def_val);
}
+struct field_entity {
+ bool is_obj{false}; /* either obj field or array entity */
+ string name; /* if obj */
+ int index{0}; /* if array */
+
+ field_entity() {}
+ field_entity(const string& n) : is_obj(true), name(n) {}
+ field_entity(int i) : is_obj(false), index(i) {}
+};
+
+static int parse_entity(const string& s, vector<field_entity> *result)
+{
+ size_t ofs = 0;
+
+ while (ofs < s.size()) {
+ size_t next_arr = s.find('[', ofs);
+ if (next_arr == string::npos) {
+ if (ofs != 0) {
+ return -EINVAL;
+ }
+ result->push_back(field_entity(s));
+ return 0;
+ }
+ if (next_arr > ofs) {
+ string field = s.substr(ofs, next_arr - ofs);
+ result->push_back(field_entity(field));
+ ofs = next_arr;
+ }
+ size_t end_arr = s.find(']', next_arr + 1);
+ if (end_arr == string::npos) {
+ return -EINVAL;
+ }
+
+ string index_str = s.substr(next_arr + 1, end_arr - next_arr - 1);
+
+ ofs = end_arr + 1;
+
+ result->push_back(field_entity(atoi(index_str.c_str())));
+ }
+ return 0;
+}
+
+int JSONFormattable::set(const string& name, const string& val)
+{
+ boost::escaped_list_separator<char> els('\\', '.', '"');
+ boost::tokenizer<boost::escaped_list_separator<char> > tok(name, els);
+
+ JSONFormattable *f = this;
+
+ JSONParser jp;
+
+ bool is_json = jp.parse(val.c_str(), val.size());
+
+ for (auto i : tok) {
+ vector<field_entity> v;
+ int ret = parse_entity(i, &v);
+ if (ret < 0) {
+ return ret;
+ }
+ for (auto vi : v) {
+ if (f->type == FMT_NONE) {
+ if (vi.is_obj) {
+ f->type = FMT_OBJ;
+ } else {
+ f->type = FMT_ARRAY;
+ }
+ }
+
+ if (f->type == FMT_OBJ) {
+ if (!vi.is_obj) {
+ return -EINVAL;
+ }
+ f = &f->obj[vi.name];
+ } else if (f->type == FMT_ARRAY) {
+ if (vi.is_obj) {
+ return -EINVAL;
+ }
+ if ((size_t)vi.index >= f->arr.size()) {
+ f->arr.resize(vi.index + 1);
+ }
+ f = &f->arr[vi.index];
+ }
+ }
+ }
+
+ if (is_json) {
+ f->decode_json(&jp);
+ } else {
+ f->type = FMT_STRING;
+ f->str = val;
+ }
+
+ return 0;
+}
+
+int JSONFormattable::erase(const string& name)
+{
+ boost::escaped_list_separator<char> els('\\', '.', '"');
+ boost::tokenizer<boost::escaped_list_separator<char> > tok(name, els);
+
+ JSONFormattable *f = this;
+ JSONFormattable *parent = nullptr;
+ field_entity last_entity;
+
+ for (auto i : tok) {
+ vector<field_entity> v;
+ int ret = parse_entity(i, &v);
+ if (ret < 0) {
+ return ret;
+ }
+ for (auto vi : v) {
+ if (f->type == FMT_NONE) {
+ if (vi.is_obj) {
+ f->type = FMT_OBJ;
+ } else {
+ f->type = FMT_ARRAY;
+ }
+ }
+
+ parent = f;
+
+ last_entity = vi;
+
+ if (f->type == FMT_OBJ) {
+ if (!vi.is_obj) {
+ return -EINVAL;
+ }
+ auto iter = f->obj.find(vi.name);
+ if (iter == f->obj.end()) {
+ return 0; /* nothing to erase */
+ }
+ f = &iter->second;
+ } else if (f->type == FMT_ARRAY) {
+ if (vi.is_obj) {
+ return -EINVAL;
+ }
+ if ((size_t)vi.index >= f->arr.size()) {
+ return 0; /* index beyond array boundaries */
+ }
+ f = &f->arr[vi.index];
+ }
+ }
+ }
+
+ if (!parent) {
+ *this = JSONFormattable(); /* erase everything */
+ } else {
+ if (last_entity.is_obj) {
+ parent->obj.erase(last_entity.name);
+ } else {
+ parent->arr.erase(parent->arr.begin() + last_entity.index);
+ }
+ }
+
+ return 0;
+}
+
void encode_json(const char *name, const JSONFormattable& v, Formatter *f)
{
switch (v.type) {
}
const JSONFormattable& operator[](const std::string& name) const;
- const JSONFormattable& operator[](const char * name) const {
- return this->operator[](std::string(name));
- }
+ const JSONFormattable& operator[](size_t index) const;
+
+ JSONFormattable& operator[](const std::string& name);
+ JSONFormattable& operator[](size_t index);
operator std::string() const {
return str;
return this->operator[](name)(T());
}
+ template<class T>
+ T operator[](const std::string& name) {
+ return this->operator[](name)(T());
+ }
+
string operator ()(const char *def_val) const {
return def(string(def_val));
}
return def(def_val);
}
+ bool exists(const string& name) const;
+ bool exists(size_t index) const;
+
std::string def(const std::string& def_val) const;
int def(int def_val) const;
bool def(bool def_val) const;
bool find(const std::string& name, std::string *val) const;
std::string get(const std::string& name, const std::string& def_val) const;
+
int get_int(const std::string& name, int def_val) const;
bool get_bool(const std::string& name, bool def_val) const;
+
+ int set(const string& name, const string& val);
+ int erase(const string& name);
};
WRITE_CLASS_ENCODER(JSONFormattable)
static void parse_tier_config_param(const string& s, map<string, string, ltstr_nocase>& out)
{
+ int level = 0;
+ string cur_conf;
list<string> confs;
- get_str_list(s, ",", confs);
+ for (auto c : s) {
+ switch (c) {
+ case '{':
+ ++level;
+ break;
+ case '}':
+ --level;
+ break;
+ case ',':
+ if (level == 0) {
+ confs.push_back(cur_conf);
+ }
+ cur_conf.clear();
+ break;
+ default:
+ cur_conf += c;
+ break;
+ };
+ }
+ if (!cur_conf.empty()) {
+ confs.push_back(cur_conf);
+ }
+
for (auto c : confs) {
ssize_t pos = c.find("=");
if (pos < 0) {
- out[c] = "";
+ out[""] = c;
} else {
out[c.substr(0, pos)] = c.substr(pos + 1);
}
string *ptier_type = (tier_type_specified ? &tier_type : nullptr);
for (auto a : tier_config_add) {
- zone.tier_config.set(a.first, a.second);
+ int r = zone.tier_config.set(a.first, a.second);
+ if (r < 0) {
+ cerr << "ERROR: failed to set configurable: " << a << std::endl;
+ return EINVAL;
+ }
}
bool *psync_from_all = (sync_from_all_specified ? &sync_from_all : nullptr);
zone.system_key.id = access_key;
zone.system_key.key = secret_key;
zone.realm_id = realm_id;
- zone.tier_config = tier_config_add;
+ for (auto a : tier_config_add) {
+ int r = zone.tier_config.set(a.first, a.second);
+ if (r < 0) {
+ cerr << "ERROR: failed to set configurable: " << a << std::endl;
+ return EINVAL;
+ }
+ }
ret = zone.create();
if (ret < 0) {
if (tier_config_add.size() > 0) {
for (auto add : tier_config_add) {
- zone.tier_config[add.first] = add.second;
+ zone.tier_config.set(add.first, add.second);
}
need_zone_update = true;
}
test_json_formattable.cc
)
add_ceph_unittest(unittest_json_formattable ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_json_formattable)
+# add_dependencies(unittest_json_formattable ceph-common)
target_link_libraries(unittest_json_formattable ceph-common global ${BLKID_LIBRARIES})
# unittest_sharedptr_registry
}
+TEST(formatable, set) {
+ JSONFormattable f, f2;
+
+ f.set("", "{ \"abc\": \"xyz\"}");
+ ASSERT_EQ((string)f["abc"], "xyz");
+
+ f.set("aaa", "111");
+ ASSERT_EQ((string)f["abc"], "xyz");
+ ASSERT_EQ((int)f["aaa"], 111);
+
+ f.set("obj", "{ \"a\": \"10\", \"b\": \"20\"}");
+ ASSERT_EQ((int)f["obj"]["a"], 10);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+
+ f.set("obj.c", "30");
+
+ ASSERT_EQ((int)f["obj"]["c"], 30);
+}
+
+TEST(formatable, erase) {
+ JSONFormattable f, f2;
+
+ f.set("", "{ \"abc\": \"xyz\"}");
+ ASSERT_EQ((string)f["abc"], "xyz");
+
+ f.set("aaa", "111");
+ ASSERT_EQ((string)f["abc"], "xyz");
+ ASSERT_EQ((int)f["aaa"], 111);
+ f.erase("aaa");
+ ASSERT_EQ((int)f["aaa"], 0);
+
+ f.set("obj", "{ \"a\": \"10\", \"b\": \"20\"}");
+ ASSERT_EQ((int)f["obj"]["a"], 10);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+ f.erase("obj.a");
+ ASSERT_EQ((int)f["obj"]["a"], 0);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+}
+
+static void dumpf(const JSONFormattable& f)
+{
+ JSONFormatter formatter;
+ formatter.open_object_section("bla");
+ ::encode_json("f", f, &formatter);
+ formatter.close_section();
+ formatter.flush(cout);
+}
+
+TEST(formatable, set_array) {
+ JSONFormattable f, f2;
+
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_EQ(f["asd"].array().size(), 1);
+ ASSERT_EQ((string)f["asd"][0], "xyz");
+
+ f.set("bbb[0][0]", "10");
+ f.set("bbb[0][1]", "20");
+ ASSERT_EQ(f["bbb"].array().size(), 1);
+ ASSERT_EQ(f["bbb"][0].array().size(), 2);
+ ASSERT_EQ((string)f["bbb"][0][0], "10");
+ ASSERT_EQ((int)f["bbb"][0][1], 20);
+ f.set("bbb[0][1]", "25");
+ ASSERT_EQ(f["bbb"][0].array().size(), 2);
+ ASSERT_EQ((int)f["bbb"][0][1], 25);
+
+ f.set("foo.asd[0][0]", "{ \"field\": \"xyz\"}");
+ ASSERT_EQ((string)f["foo"]["asd"][0][0]["field"], "xyz");
+
+ ASSERT_EQ(f.set("foo[0]", "\"zzz\""), -EINVAL); /* can't assign array to an obj entity */
+
+ f2.set("[0]", "{ \"field\": \"xyz\"}");
+ ASSERT_EQ((string)f2[0]["field"], "xyz");
+}
+
+TEST(formatable, erase_array) {
+ JSONFormattable f;
+
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_EQ(f["asd"].array().size(), 1);
+ ASSERT_EQ((string)f["asd"][0], "xyz");
+ ASSERT_TRUE(f["asd"].exists(0));
+ f.erase("asd[0]");
+ ASSERT_FALSE(f["asd"].exists(0));
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_TRUE(f["asd"].exists(0));
+ f["asd"].erase("[0]");
+ ASSERT_FALSE(f["asd"].exists(0));
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_TRUE(f["asd"].exists(0));
+ f.erase("asd");
+ ASSERT_FALSE(f["asd"].exists(0));
+ ASSERT_FALSE(f.exists("asd"));
+}
+