Merge branch 'v3' of gitlab.com:ToruNiina/toml11 into v3

This commit is contained in:
ToruNiina
2019-06-19 19:05:22 +09:00
9 changed files with 365 additions and 219 deletions

View File

@@ -47,17 +47,17 @@ int main()
- [Decoding a toml file](#decoding-a-toml-file)
- [In the case of syntax error](#in-the-case-of-syntax-error)
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
- [Finding a toml value](#getting-a-toml-value)
- [Finding a toml value](#finding-a-toml-value-from-a-table)
- [In the case of type error](#in-the-case-of-type-error)
- [Dotted keys](#dotted-keys)
- [Casting a toml value](#casting-a-toml-value)
- [Checking value type](#checking-value-type)
- [More about conversion](#more-about-conversion)
- [Getting an array](#getting-an-array)
- [Getting a table](#getting-a-table)
- [Converting an array](#converting-an-array)
- [Converting a table](#converting-a-table)
- [Getting an array of tables](#getting-an-array-of-tables)
- [Cost of conversion](#cost-of-conversion)
- [Getting datetime and its variants](#getting-datetime-and-its-variants)
- [Converting datetime and its variants](#converting-datetime-and-its-variants)
- [Getting with a fallback](#getting-with-a-fallback)
- [Expecting conversion](#expecting-conversion)
- [Visiting a toml::value](#visiting-a-tomlvalue)
@@ -67,7 +67,6 @@ int main()
- [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Getting comments related to a value](#getting-comments)
- [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types)
- [Breaking Changes from v2](#breaking-changes-from-v2)
@@ -711,7 +710,7 @@ const auto value = toml::expect<int>(data.at("number"))
}).unwrap_or(/*default value =*/ 3.14);
```
## Visiting toml::value
## Visiting a toml::value
toml11 provides `toml::visit` to apply a function to `toml::value` in the
same way as `std::variant`.

View File

@@ -134,14 +134,15 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods)
}
{
const toml::basic_value<toml::discard_comment, std::map, std::deque>
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{{"a", 42}, {"b", "baz"}};
const auto foo = toml::get<extlib2::foo>(v);
BOOST_CHECK_EQUAL(foo.a, 42);
BOOST_CHECK_EQUAL(foo.b, "baz");
const toml::value v2(foo);
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v2(foo);
BOOST_CHECK_EQUAL(v, v2);
}
@@ -172,14 +173,15 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
BOOST_CHECK_EQUAL(v, v2);
}
{
const toml::basic_value<toml::discard_comment, std::map, std::deque>
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{{"a", 42}, {"b", "baz"}};
const auto bar = toml::get<extlib2::bar>(v);
BOOST_CHECK_EQUAL(bar.a, 42);
BOOST_CHECK_EQUAL(bar.b, "baz");
const toml::value v2(bar);
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v2(bar);
BOOST_CHECK_EQUAL(v, v2);
}
@@ -253,12 +255,12 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion)
}
{
const toml::basic_value<toml::discard_comment, std::map, std::deque>
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{
toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}},
toml::table{{"a", 45}, {"b", "foobar"}},
toml::table{{"a", 45}, {"b", "foobar"}}
};
const auto foos = toml::get<std::vector<extlib2::foo>>(v);

View File

@@ -193,6 +193,206 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
BOOST_CHECK(toml::find<std::vector<std::string>>(bit, "multi_line_array") ==
expected_multi_line_array);
}
BOOST_AUTO_TEST_CASE(test_hard_example_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/hard_example.toml");
const auto the = toml::find(data, "the");
BOOST_CHECK_EQUAL(toml::find<std::string>(the, "test_string"),
"You'll hate me after this - #");
const auto hard = toml::find(the, "hard");
const std::vector<std::string> expected_the_hard_test_array{"] ", " # "};
BOOST_CHECK(toml::find<std::vector<std::string>>(hard, "test_array") ==
expected_the_hard_test_array);
const std::vector<std::string> expected_the_hard_test_array2{
"Test #11 ]proved that", "Experiment #9 was a success"};
BOOST_CHECK(toml::find<std::vector<std::string>>(hard, "test_array2") ==
expected_the_hard_test_array2);
BOOST_CHECK_EQUAL(toml::find<std::string>(hard, "another_test_string"),
" Same thing, but with a string #");
BOOST_CHECK_EQUAL(toml::find<std::string>(hard, "harder_test_string"),
" And when \"'s are in the string, along with # \"");
const auto bit = toml::find(hard, "bit#");
BOOST_CHECK_EQUAL(toml::find<std::string>(bit, "what?"),
"You don't think some user won't do that?");
const std::vector<std::string> expected_multi_line_array{"]"};
BOOST_CHECK(toml::find<std::vector<std::string>>(bit, "multi_line_array") ==
expected_multi_line_array);
}
BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
BOOST_CHECK_EQUAL(toml::find<std::string>(data, "title"), "TOML Example");
const auto& owner = toml::find(data, "owner");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "name"), "Tom Preston-Werner");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "organization"), "GitHub");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "bio"),
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_CHECK_EQUAL(toml::find<toml::offset_datetime>(owner, "dob"),
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
BOOST_CHECK_EQUAL(toml::find(owner, "dob").comments().at(0),
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(database, "server"), "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_CHECK_EQUAL(toml::find<int >(database, "connection_max"), 5000);
BOOST_CHECK_EQUAL(toml::find<bool>(database, "enabled"), true);
}
const auto& servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
BOOST_CHECK_EQUAL(alpha.comments().at(0),
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "ip"), "10.0.0.1");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "dc"), "eqdc10");
const auto& beta = toml::find(servers, "beta");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "ip"), "10.0.0.2");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "dc"), "eqdc10");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "country"),
"\xE4\xB8\xAD\xE5\x9B\xBD");
BOOST_CHECK_EQUAL(toml::find(beta, "country").comments().at(0),
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
{
BOOST_CHECK_EQUAL(toml::find(clients, "data").comments().at(0),
" just an update to make sure parsers support it");
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) ==
expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) ==
expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") ==
expected_hosts);
BOOST_CHECK_EQUAL(toml::find(clients, "hosts").comments().at(0),
" Line breaks are OK when inside arrays");
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(0).at("name")),
"Hammer");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(0).at("sku")),
738594937);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("name")),
"Nail");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(1).at("sku")),
284758393);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("color")),
"gray");
}
}
BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque
>("toml/tests/example.toml");
static_assert(std::is_same<typename decltype(data)::table_type,
std::map<toml::key, typename std::remove_cv<decltype(data)>::type>
>::value, "");
static_assert(std::is_same<typename decltype(data)::array_type,
std::deque<typename std::remove_cv<decltype(data)>::type>
>::value, "");
BOOST_CHECK_EQUAL(toml::find<std::string>(data, "title"), "TOML Example");
const auto& owner = toml::find(data, "owner");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "name"), "Tom Preston-Werner");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "organization"), "GitHub");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "bio"),
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_CHECK_EQUAL(toml::find<toml::offset_datetime>(owner, "dob"),
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
BOOST_CHECK_EQUAL(toml::find(owner, "dob").comments().at(0),
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(database, "server"), "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_CHECK_EQUAL(toml::find<int >(database, "connection_max"), 5000);
BOOST_CHECK_EQUAL(toml::find<bool>(database, "enabled"), true);
}
const auto& servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
BOOST_CHECK_EQUAL(alpha.comments().at(0),
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "ip"), "10.0.0.1");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "dc"), "eqdc10");
const auto& beta = toml::find(servers, "beta");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "ip"), "10.0.0.2");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "dc"), "eqdc10");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "country"),
"\xE4\xB8\xAD\xE5\x9B\xBD");
BOOST_CHECK_EQUAL(toml::find(beta, "country").comments().at(0),
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
{
BOOST_CHECK_EQUAL(toml::find(clients, "data").comments().at(0),
" just an update to make sure parsers support it");
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) ==
expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) ==
expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") ==
expected_hosts);
BOOST_CHECK_EQUAL(toml::find(clients, "hosts").comments().at(0),
" Line breaks are OK when inside arrays");
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(0).at("name")),
"Hammer");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(0).at("sku")),
738594937);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("name")),
"Nail");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(1).at("sku")),
284758393);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("color")),
"gray");
}
}
// ---------------------------------------------------------------------------
// after here, the test codes generate the content of a file.
@@ -695,174 +895,3 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
}
}
BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
BOOST_CHECK_EQUAL(toml::find<std::string>(data, "title"), "TOML Example");
const auto& owner = toml::find(data, "owner");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "name"), "Tom Preston-Werner");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "organization"), "GitHub");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "bio"),
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_CHECK_EQUAL(toml::find<toml::offset_datetime>(owner, "dob"),
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
BOOST_CHECK_EQUAL(toml::find(owner, "dob").comments().at(0),
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(database, "server"), "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_CHECK_EQUAL(toml::find<int >(database, "connection_max"), 5000);
BOOST_CHECK_EQUAL(toml::find<bool>(database, "enabled"), true);
}
const auto& servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
BOOST_CHECK_EQUAL(alpha.comments().at(0),
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "ip"), "10.0.0.1");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "dc"), "eqdc10");
const auto& beta = toml::find(servers, "beta");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "ip"), "10.0.0.2");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "dc"), "eqdc10");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "country"),
"\xE4\xB8\xAD\xE5\x9B\xBD");
BOOST_CHECK_EQUAL(toml::find(beta, "country").comments().at(0),
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
{
BOOST_CHECK_EQUAL(toml::find(clients, "data").comments().at(0),
" just an update to make sure parsers support it");
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) ==
expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) ==
expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") ==
expected_hosts);
BOOST_CHECK_EQUAL(toml::find(clients, "hosts").comments().at(0),
" Line breaks are OK when inside arrays");
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(0).at("name")),
"Hammer");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(0).at("sku")),
738594937);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("name")),
"Nail");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(1).at("sku")),
284758393);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("color")),
"gray");
}
}
BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque
>("toml/tests/example.toml");
static_assert(std::is_same<typename decltype(data)::table_type,
std::map<toml::key, typename std::remove_cv<decltype(data)>::type>
>::value, "");
static_assert(std::is_same<typename decltype(data)::array_type,
std::deque<typename std::remove_cv<decltype(data)>::type>
>::value, "");
BOOST_CHECK_EQUAL(toml::find<std::string>(data, "title"), "TOML Example");
const auto& owner = toml::find(data, "owner");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "name"), "Tom Preston-Werner");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "organization"), "GitHub");
BOOST_CHECK_EQUAL(toml::find<std::string>(owner, "bio"),
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_CHECK_EQUAL(toml::find<toml::offset_datetime>(owner, "dob"),
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
BOOST_CHECK_EQUAL(toml::find(owner, "dob").comments().at(0),
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
{
BOOST_CHECK_EQUAL(toml::find<std::string>(database, "server"), "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_CHECK_EQUAL(toml::find<int >(database, "connection_max"), 5000);
BOOST_CHECK_EQUAL(toml::find<bool>(database, "enabled"), true);
}
const auto& servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
BOOST_CHECK_EQUAL(alpha.comments().at(0),
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "ip"), "10.0.0.1");
BOOST_CHECK_EQUAL(toml::find<std::string>(alpha, "dc"), "eqdc10");
const auto& beta = toml::find(servers, "beta");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "ip"), "10.0.0.2");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "dc"), "eqdc10");
BOOST_CHECK_EQUAL(toml::find<std::string>(beta, "country"),
"\xE4\xB8\xAD\xE5\x9B\xBD");
BOOST_CHECK_EQUAL(toml::find(beta, "country").comments().at(0),
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
{
BOOST_CHECK_EQUAL(toml::find(clients, "data").comments().at(0),
" just an update to make sure parsers support it");
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) ==
expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) ==
expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") ==
expected_hosts);
BOOST_CHECK_EQUAL(toml::find(clients, "hosts").comments().at(0),
" Line breaks are OK when inside arrays");
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(0).at("name")),
"Hammer");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(0).at("sku")),
738594937);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("name")),
"Nail");
BOOST_CHECK_EQUAL(toml::get<std::int64_t>(products.at(1).at("sku")),
284758393);
BOOST_CHECK_EQUAL(toml::get<std::string>(products.at(1).at("color")),
"gray");
}
}

View File

@@ -9,7 +9,6 @@
#include <iostream>
#include <fstream>
BOOST_AUTO_TEST_CASE(test_example)
{
const auto data = toml::parse("toml/tests/example.toml");
@@ -31,6 +30,31 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_example_with_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
{
std::ofstream ofs("tmp1_com.toml");
ofs << std::setw(80) << data;
}
auto serialized = toml::parse<toml::preserve_comments>("tmp1_com.toml");
{
auto& owner = toml::find<typename decltype(serialized)::table_type>(serialized, "owner");
auto& bio = toml::get<std::string>(owner.at("bio"));
const auto CR = std::find(bio.begin(), bio.end(), '\r');
if(CR != bio.end())
{
bio.erase(CR);
}
}
BOOST_CHECK(data == serialized);
{
std::ofstream ofs("tmp1_com1.toml");
ofs << std::setw(80) << serialized;
}
}
BOOST_AUTO_TEST_CASE(test_fruit)
{
const auto data = toml::parse("toml/tests/fruit.toml");
@@ -42,6 +66,17 @@ BOOST_AUTO_TEST_CASE(test_fruit)
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_fruit_with_comments)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/fruit.toml");
{
std::ofstream ofs("tmp2_com.toml");
ofs << std::setw(80) << data;
}
const auto serialized = toml::parse<toml::preserve_comments>("tmp2_com.toml");
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_hard_example)
{
const auto data = toml::parse("toml/tests/hard_example.toml");
@@ -52,3 +87,18 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
const auto serialized = toml::parse("tmp3.toml");
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_hard_example_with_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/hard_example.toml");
{
std::ofstream ofs("tmp3_com.toml");
ofs << std::setw(80) << data;
}
const auto serialized = toml::parse<toml::preserve_comments>("tmp3_com.toml");
{
std::ofstream ofs("tmp3_com1.toml");
ofs << std::setw(80) << serialized;
}
BOOST_CHECK(data == serialized);
}

View File

@@ -55,6 +55,17 @@ struct preserve_comments
explicit preserve_comments(std::vector<std::string>&& c)
: comments(std::move(c))
{}
preserve_comments& operator=(const std::vector<std::string>& c)
{
comments = c;
return *this;
}
preserve_comments& operator=(std::vector<std::string>&& c)
{
comments = std::move(c);
return *this;
}
explicit preserve_comments(const discard_comments&) {}
explicit preserve_comments(size_type n): comments(n) {}
@@ -278,6 +289,9 @@ struct discard_comments
explicit discard_comments(const std::vector<std::string>&) noexcept {}
explicit discard_comments(std::vector<std::string>&&) noexcept {}
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
explicit discard_comments(const preserve_comments&) noexcept {}
explicit discard_comments(size_type) noexcept {}

View File

@@ -1703,6 +1703,44 @@ result<Value, std::string> parse_toml_file(location<Container>& loc)
return ok(value_type(table_type{}));
}
// put the first line as a region of a file
const region<Container> file(loc, loc.iter(),
std::find(loc.iter(), loc.end(), '\n'));
// The first successive comments that are separated from the first value
// by an empty line are for a file itself.
// ```toml
// # this is a comment for a file.
//
// key = "the first value"
// ```
// ```toml
// # this is a comment for "the first value".
// key = "the first value"
// ```
std::vector<std::string> comments;
using lex_first_comments = sequence<
repeat<sequence<maybe<lex_ws>, lex_comment, lex_newline>, at_least<1>>,
sequence<maybe<lex_ws>, lex_newline>
>;
if(const auto token = lex_first_comments::invoke(loc))
{
location<std::string> inner_loc(loc.name(), token.unwrap().str());
while(inner_loc.iter() != inner_loc.end())
{
maybe<lex_ws>::invoke(inner_loc); // remove ws if exists
if(lex_newline::invoke(inner_loc))
{
assert(inner_loc.iter() == inner_loc.end());
break; // empty line found.
}
auto com = lex_comment::invoke(inner_loc).unwrap().str();
com.erase(com.begin()); // remove # sign
comments.push_back(std::move(com));
lex_newline::invoke(inner_loc);
}
}
table_type data;
// root object is also a table, but without [tablename]
if(auto tab = parse_ml_table<value_type>(loc))
@@ -1752,7 +1790,11 @@ result<Value, std::string> parse_toml_file(location<Container>& loc)
return err(format_underline("[error]: toml::parse_toml_file: "
"unknown line appeared", {{std::addressof(loc), "unknown format"}}));
}
return ok(data);
Value v(std::move(data), file);
v.comments() = comments;
return ok(std::move(v));
}
} // detail

View File

@@ -304,38 +304,45 @@ struct region final : public region_base
// # this also.
// a = value # not this.
// ```
auto iter = this->line_begin(); // points the first character
while(iter != this->begin())
// # this is a comment for `a`, not array elements.
// a = [1, 2, 3, 4, 5]
if(this->first() == std::find_if(this->line_begin(), this->first(),
[](const char c) noexcept -> bool {return c == '[' || c == '{';}))
{
iter = std::prev(iter);
auto iter = this->line_begin(); // points the first character
while(iter != this->begin())
{
iter = std::prev(iter);
// range [line_start, iter) represents the previous line
const auto line_start = std::find(
rev_iter(iter), rev_iter(this->begin()), '\n').base();
const auto comment_found = std::find(line_start, iter, '#');
if(comment_found == iter)
{
break; // comment not found.
}
// range [line_start, iter) represents the previous line
const auto line_start = std::find(
rev_iter(iter), rev_iter(this->begin()), '\n').base();
const auto comment_found = std::find(line_start, iter, '#');
if(comment_found == iter)
{
break; // comment not found.
}
// exclude the following case.
// > a = "foo" # comment // <-- this is not a comment for b but a.
// > b = "current value"
if(std::all_of(line_start, comment_found,
[](const char c) noexcept -> bool {
return c == ' ' || c == '\t';
}))
{
// unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str));
// exclude the following case.
// > a = "foo" # comment // <-- this is not a comment for b but a.
// > b = "current value"
if(std::all_of(line_start, comment_found,
[](const char c) noexcept -> bool {
return c == ' ' || c == '\t';
}))
{
// unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str));
}
else
{
break;
}
iter = line_start;
}
else
{
break;
}
iter = line_start;
}
}

View File

@@ -333,6 +333,11 @@ struct serializer
current_line += ',';
}
}
if(!current_line.empty())
{
if(current_line.back() != '\n') {current_line += '\n';}
token += current_line;
}
token += "]\n";
return token;
}

View File

@@ -779,18 +779,17 @@ class basic_value
}
template<typename T, typename std::enable_if<
detail::is_convertible_to_toml_value<T, value_type>::value,
std::is_convertible<T, value_type>::value,
std::nullptr_t>::type = nullptr>
basic_value(std::initializer_list<T> list)
: type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{}))
{
array_type ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));}
array_type ary(list.begin(), list.end());
assigner(this->array_, std::move(ary));
}
template<typename T, typename std::enable_if<
detail::is_convertible_to_toml_value<T, value_type>::value,
std::is_convertible<T, value_type>::value,
std::nullptr_t>::type = nullptr>
basic_value& operator=(std::initializer_list<T> list)
{
@@ -798,8 +797,7 @@ class basic_value
this->type_ = value_t::array;
this->region_info_ = std::make_shared<region_base>(region_base{});
array_type ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));}
array_type ary(list.begin(), list.end());
assigner(this->array_, std::move(ary));
return *this;
}