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) - [Decoding a toml file](#decoding-a-toml-file)
- [In the case of syntax error](#in-the-case-of-syntax-error) - [In the case of syntax error](#in-the-case-of-syntax-error)
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) - [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) - [In the case of type error](#in-the-case-of-type-error)
- [Dotted keys](#dotted-keys) - [Dotted keys](#dotted-keys)
- [Casting a toml value](#casting-a-toml-value) - [Casting a toml value](#casting-a-toml-value)
- [Checking value type](#checking-value-type) - [Checking value type](#checking-value-type)
- [More about conversion](#more-about-conversion) - [More about conversion](#more-about-conversion)
- [Getting an array](#getting-an-array) - [Converting an array](#converting-an-array)
- [Getting a table](#getting-a-table) - [Converting a table](#converting-a-table)
- [Getting an array of tables](#getting-an-array-of-tables) - [Getting an array of tables](#getting-an-array-of-tables)
- [Cost of conversion](#cost-of-conversion) - [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) - [Getting with a fallback](#getting-with-a-fallback)
- [Expecting conversion](#expecting-conversion) - [Expecting conversion](#expecting-conversion)
- [Visiting a toml::value](#visiting-a-tomlvalue) - [Visiting a toml::value](#visiting-a-tomlvalue)
@@ -67,7 +67,6 @@ int main()
- [TOML literal](#toml-literal) - [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Getting comments related to a value](#getting-comments)
- [Serializing TOML data](#serializing-toml-data) - [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types) - [Underlying types](#underlying-types)
- [Breaking Changes from v2](#breaking-changes-from-v2) - [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); }).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 toml11 provides `toml::visit` to apply a function to `toml::value` in the
same way as `std::variant`. 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"}}; v{{"a", 42}, {"b", "baz"}};
const auto foo = toml::get<extlib2::foo>(v); const auto foo = toml::get<extlib2::foo>(v);
BOOST_CHECK_EQUAL(foo.a, 42); BOOST_CHECK_EQUAL(foo.a, 42);
BOOST_CHECK_EQUAL(foo.b, "baz"); 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); BOOST_CHECK_EQUAL(v, v2);
} }
@@ -172,14 +173,15 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
BOOST_CHECK_EQUAL(v, v2); 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"}}; v{{"a", 42}, {"b", "baz"}};
const auto bar = toml::get<extlib2::bar>(v); const auto bar = toml::get<extlib2::bar>(v);
BOOST_CHECK_EQUAL(bar.a, 42); BOOST_CHECK_EQUAL(bar.a, 42);
BOOST_CHECK_EQUAL(bar.b, "baz"); 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); 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{ v{
toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}}, 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); 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") == BOOST_CHECK(toml::find<std::vector<std::string>>(bit, "multi_line_array") ==
expected_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. // 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 <iostream>
#include <fstream> #include <fstream>
BOOST_AUTO_TEST_CASE(test_example) BOOST_AUTO_TEST_CASE(test_example)
{ {
const auto data = toml::parse("toml/tests/example.toml"); const auto data = toml::parse("toml/tests/example.toml");
@@ -31,6 +30,31 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_CHECK(data == serialized); 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) BOOST_AUTO_TEST_CASE(test_fruit)
{ {
const auto data = toml::parse("toml/tests/fruit.toml"); const auto data = toml::parse("toml/tests/fruit.toml");
@@ -42,6 +66,17 @@ BOOST_AUTO_TEST_CASE(test_fruit)
BOOST_CHECK(data == serialized); 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) BOOST_AUTO_TEST_CASE(test_hard_example)
{ {
const auto data = toml::parse("toml/tests/hard_example.toml"); 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"); const auto serialized = toml::parse("tmp3.toml");
BOOST_CHECK(data == serialized); 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) explicit preserve_comments(std::vector<std::string>&& c)
: comments(std::move(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(const discard_comments&) {}
explicit preserve_comments(size_type n): comments(n) {} 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(const std::vector<std::string>&) noexcept {}
explicit discard_comments(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(const preserve_comments&) noexcept {}
explicit discard_comments(size_type) 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{})); 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; table_type data;
// root object is also a table, but without [tablename] // root object is also a table, but without [tablename]
if(auto tab = parse_ml_table<value_type>(loc)) 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: " return err(format_underline("[error]: toml::parse_toml_file: "
"unknown line appeared", {{std::addressof(loc), "unknown format"}})); "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 } // detail

View File

@@ -304,38 +304,45 @@ struct region final : public region_base
// # this also. // # this also.
// a = value # not this. // 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 // range [line_start, iter) represents the previous line
const auto line_start = std::find( const auto line_start = std::find(
rev_iter(iter), rev_iter(this->begin()), '\n').base(); rev_iter(iter), rev_iter(this->begin()), '\n').base();
const auto comment_found = std::find(line_start, iter, '#'); const auto comment_found = std::find(line_start, iter, '#');
if(comment_found == iter) if(comment_found == iter)
{ {
break; // comment not found. break; // comment not found.
} }
// exclude the following case. // exclude the following case.
// > a = "foo" # comment // <-- this is not a comment for b but a. // > a = "foo" # comment // <-- this is not a comment for b but a.
// > b = "current value" // > b = "current value"
if(std::all_of(line_start, comment_found, if(std::all_of(line_start, comment_found,
[](const char c) noexcept -> bool { [](const char c) noexcept -> bool {
return c == ' ' || c == '\t'; return c == ' ' || c == '\t';
})) }))
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter); auto str = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();} if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str)); 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 += ','; current_line += ',';
} }
} }
if(!current_line.empty())
{
if(current_line.back() != '\n') {current_line += '\n';}
token += current_line;
}
token += "]\n"; token += "]\n";
return token; return token;
} }

View File

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