diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4f3e52a..3de9785 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,6 +20,7 @@ set(TEST_NAMES test_parse_inline_table test_parse_key test_parse_table_key + test_literals test_get test_get_related_func test_from_toml diff --git a/tests/test_literals.cpp b/tests/test_literals.cpp new file mode 100644 index 0000000..7431f80 --- /dev/null +++ b/tests/test_literals.cpp @@ -0,0 +1,129 @@ +#define BOOST_TEST_MODULE "test_literals" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include + +BOOST_AUTO_TEST_CASE(test_file_as_literal) +{ + using namespace toml::literals::toml_literals; + + { + const toml::value r{{"a", 42}, {"b", "baz"}}; + const toml::value v = u8R"( + a = 42 + b = "baz" + )"_toml; + + BOOST_CHECK_EQUAL(r, v); + } + { + const toml::value r{ + {"c", 3.14}, + {"table", toml::table{{"a", 42}, {"b", "baz"}}} + }; + const toml::value v = u8R"( + c = 3.14 + [table] + a = 42 + b = "baz" + )"_toml; + + BOOST_CHECK_EQUAL(r, v); + } +} + +BOOST_AUTO_TEST_CASE(test_value_as_literal) +{ + using namespace toml::literals::toml_literals; + + { + const toml::value v1 = u8"true"_toml; + const toml::value v2 = u8"false"_toml; + + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); + BOOST_CHECK(toml::get(v1)); + BOOST_CHECK(!toml::get(v2)); + } + { + const toml::value v1 = u8"123_456"_toml; + const toml::value v2 = u8"0b0010"_toml; + const toml::value v3 = u8"0xDEADBEEF"_toml; + + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_integer()); + BOOST_CHECK(v3.is_integer()); + BOOST_CHECK_EQUAL(toml::get(v1), 123456); + BOOST_CHECK_EQUAL(toml::get(v2), 2); + BOOST_CHECK_EQUAL(toml::get(v3), 0xDEADBEEF); + } + { + const toml::value v1 = u8"3.1415"_toml; + const toml::value v2 = u8"6.02e+23"_toml; + + BOOST_CHECK(v1.is_float()); + BOOST_CHECK(v2.is_float()); + BOOST_CHECK_CLOSE(toml::get(v1), 3.1415, 0.00001); + BOOST_CHECK_CLOSE(toml::get(v2), 6.02e23, 0.0001); + } + { + const toml::value v1 = u8R"("foo")"_toml; + const toml::value v2 = u8R"('foo')"_toml; + const toml::value v3 = u8R"("""foo""")"_toml; + const toml::value v4 = u8R"('''foo''')"_toml; + + BOOST_CHECK(v1.is_string()); + BOOST_CHECK(v2.is_string()); + BOOST_CHECK(v3.is_string()); + BOOST_CHECK(v4.is_string()); + BOOST_CHECK_EQUAL(toml::get(v1), "foo"); + BOOST_CHECK_EQUAL(toml::get(v2), "foo"); + BOOST_CHECK_EQUAL(toml::get(v3), "foo"); + BOOST_CHECK_EQUAL(toml::get(v4), "foo"); + } + { + const toml::value v1 = u8R"([1,2,3])"_toml; + + BOOST_CHECK(v1.is_array()); + BOOST_CHECK((toml::get>(v1) == std::vector{1,2,3})); + } + { + const toml::value v1 = u8R"({a = 42})"_toml; + + BOOST_CHECK(v1.is_table()); + BOOST_CHECK((toml::get>(v1) == + std::map{{"a", 42}})); + } + { + const toml::value v1 = u8"1979-05-27"_toml; + + BOOST_CHECK(v1.is_local_date()); + BOOST_CHECK_EQUAL(toml::get(v1), + toml::local_date(1979, toml::month_t::May, 27)); + } + { + const toml::value v1 = u8"12:00:00"_toml; + + BOOST_CHECK(v1.is_local_time()); + BOOST_CHECK(toml::get(v1) == std::chrono::hours(12)); + } + { + const toml::value v1 = u8"1979-05-27T07:32:00"_toml; + BOOST_CHECK(v1.is_local_datetime()); + BOOST_CHECK_EQUAL(toml::get(v1), + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0))); + } + { + const toml::value v1 = "1979-05-27T07:32:00Z"_toml; + BOOST_CHECK(v1.is_offset_datetime()); + BOOST_CHECK_EQUAL(toml::get(v1), + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); + } +} diff --git a/toml.hpp b/toml.hpp index 8cd95af..fde2a3c 100644 --- a/toml.hpp +++ b/toml.hpp @@ -34,6 +34,7 @@ #endif #include "toml/parser.hpp" +#include "toml/literal.hpp" #include "toml/serializer.hpp" #include "toml/from_toml.hpp" #include "toml/get.hpp" diff --git a/toml/get.hpp b/toml/get.hpp index ed211b0..c0e47bb 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -113,7 +113,7 @@ inline std::string get(value&& v) template::value, std::nullptr_t>::type = nullptr> -inline T get(value& v) +inline T get(const value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.cast())); @@ -125,7 +125,7 @@ inline T get(value& v) template::value, std::nullptr_t>::type = nullptr> -inline T get(value& v) +inline T get(const value& v) { switch(v.type()) { diff --git a/toml/literal.hpp b/toml/literal.hpp new file mode 100644 index 0000000..3642626 --- /dev/null +++ b/toml/literal.hpp @@ -0,0 +1,55 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_LITERAL_HPP +#define TOML11_LITERAL_HPP +#include "parser.hpp" + +namespace toml +{ +inline namespace literals +{ +inline namespace toml_literals +{ + +inline ::toml::value operator""_toml(const char* str, std::size_t len) +{ + ::toml::detail::location> + loc(/* filename = */ std::string("TOML literal encoded in a C++ code"), + /* contents = */ std::vector(str, str + len)); + + // if there are some comments or empty lines, skip them. + using skip_line = ::toml::detail::repeat, + ::toml::detail::maybe<::toml::detail::lex_comment>, + ::toml::detail::lex_newline + >, ::toml::detail::at_least<1>>; + skip_line::invoke(loc); + + // if there are some whitespaces before a value, skip them. + using skip_ws = ::toml::detail::repeat< + ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; + skip_ws::invoke(loc); + + // literal may be a bare value. try them first. + if(auto data = ::toml::detail::parse_value(loc)) + { + return data.unwrap(); + } + + // literal is a TOML file (i.e. multiline table). + if(auto data = ::toml::detail::parse_toml_file(loc)) + { + loc.iter() = loc.begin(); // rollback to the top of the literal + return ::toml::value(std::move(data.unwrap()), + ::toml::detail::region>(std::move(loc))); + } + else // none of them. + { + throw ::toml::syntax_error(data.unwrap_err()); + } +} + +} // toml_literals +} // literals +} // toml +#endif//TOML11_LITERAL_HPP