diff --git a/README.md b/README.md index 108a646..ffe7d8e 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,19 @@ const auto data = toml::parse("fruit.toml"); const auto bar = toml::find(data, "values", 1); ``` +Before calling `toml::find`, you can check if a value corresponding to a key +exists. You can use both `bool toml::value::contains(const key&) const` and +`std::size_t toml::value::count(const key&) const`. Those behaves like the +`std::map::contains` and `std::map::count`. + +```cpp +const auto data = toml::parse("fruit.toml"); +if(data.contains("fruit") && data.at("fruit").count("physical") != 0) +{ + // ... +} +``` + ### In case of error If the value does not exist, `toml::find` throws `std::out_of_range` with the @@ -885,14 +898,25 @@ toml::value v(toml::local_time(std::chrono::hours(10))); ``` You can construct an array object not only from `initializer_list`, but also -from STL containers. +from STL containers. In that case, the element type must be convertible to +`toml::value`. ```cpp std::vector vec{1,2,3,4,5}; -toml::value v = vec; +toml::value v(vec); ``` -All the elements of `initializer_list` should be convertible into `toml::value`. +When you construct an array value, all the elements of `initializer_list` +must be convertible into `toml::value`. + +If a `toml::value` has an array, you can `push_back` an element in it. + +```cpp +toml::value v{1,2,3,4,5}; +v.push_back(6); +``` + +`emplace_back` also works. ## Preserving comments diff --git a/tests/test_value.cpp b/tests/test_value.cpp index c2f02db..927decf 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -966,3 +966,54 @@ BOOST_AUTO_TEST_CASE(test_value_bracket) BOOST_CHECK_THROW(v1["foo"], toml::type_error); } } + +BOOST_AUTO_TEST_CASE(test_value_map_methods) +{ + { + toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; + + BOOST_TEST(v1.count("foo") == 1u); + BOOST_TEST(v1.count("bar") == 1u); + BOOST_TEST(v1.count("baz") == 1u); + BOOST_TEST(v1.count("qux") == 0u); + + BOOST_TEST( v1.contains("foo")); + BOOST_TEST( v1.contains("bar")); + BOOST_TEST( v1.contains("baz")); + BOOST_TEST(!v1.contains("qux")); + + BOOST_TEST(v1.size() == 3); + + v1["qux"] = 54; + BOOST_TEST(v1.count("qux") == 1u); + BOOST_TEST(v1.contains("qux")); + BOOST_TEST(v1.size() == 4); + } + { + toml::value v1(42); + BOOST_CHECK_THROW(v1.size() , toml::type_error); + BOOST_CHECK_THROW(v1.count("k") , toml::type_error); + BOOST_CHECK_THROW(v1.contains("k"), toml::type_error); + } +} + +BOOST_AUTO_TEST_CASE(test_value_vector_methods) +{ + { + toml::value v1{1, 2, 3, 4, 5}; + + BOOST_TEST(v1.size() == 5); + + v1.push_back(6); + BOOST_TEST(v1.size() == 6); + + v1.emplace_back(6); + BOOST_TEST(v1.size() == 7); + } + { + toml::value v1(42); + BOOST_CHECK_THROW(v1.size(), toml::type_error); + BOOST_CHECK_THROW(v1.push_back(1), toml::type_error); + BOOST_CHECK_THROW(v1.emplace_back(1), toml::type_error); + } +} diff --git a/toml/value.hpp b/toml/value.hpp index b5a4e86..9e44cba 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1606,6 +1606,101 @@ class basic_value return this->as_array(std::nothrow)[idx]; } + void push_back(const value_type& x) + { + if(this->type_ != value_t::array) + { + throw type_error(detail::format_underline( + "toml::value::push_back(value): bad_cast to array type", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + this->as_array(std::nothrow).push_back(x); + return; + } + void push_back(value_type&& x) + { + if(this->type_ != value_t::array) + { + throw type_error(detail::format_underline( + "toml::value::push_back(value): bad_cast to array type", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + this->as_array(std::nothrow).push_back(std::move(x)); + return; + } + + template + value_type& emplace_back(Ts&& ... args) + { + if(this->type_ != value_t::array) + { + throw type_error(detail::format_underline( + "toml::value::emplace_back(value): bad_cast to array type", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + this->as_array(std::nothrow).emplace_back(std::forward(args) ...); + return this->as_array(std::nothrow).back(); + } + + std::size_t size() const + { + switch(this->type_) + { + case value_t::array: + { + return this->as_array().size(); + } + case value_t::table: + { + return this->as_table().size(); + } + case value_t::string: + { + return this->as_string().str.size(); + } + default: + { + throw type_error(detail::format_underline( + "toml::value::size(): bad_cast to container types", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + } + } + + std::size_t count(const key_type& k) const + { + if(this->type_ != value_t::table) + { + throw type_error(detail::format_underline( + "toml::value::count(key): bad_cast to table type", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + return this->as_table().count(k); + } + + bool contains(const key_type& k) const + { + if(this->type_ != value_t::table) + { + throw type_error(detail::format_underline( + "toml::value::contains(key): bad_cast to table type", { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + return (this->as_table().count(k) != 0); + } + source_location location() const { return source_location(this->region_info_.get());