Compare commits

..

45 Commits

Author SHA1 Message Date
ToruNiina
c4aecc8e4b chore: update README badges 2019-02-13 22:36:29 +09:00
Toru Niina
60c81d06a0 Merge pull request #25 from ToruNiina/hotfix
fix: open file as binary-mode #16
2019-02-13 21:14:15 +09:00
ToruNiina
46569da231 fix: avoid auto-conversion while making test case 2019-02-13 19:51:54 +09:00
ToruNiina
5e20a8ff16 fix: add scope to the test case to flush 2019-02-13 19:26:52 +09:00
ToruNiina
dd9319245e fix: open file as binary-mode #16
to avoid inconsistency between file size (obtained by tellg) and the
size of the actual contents that would be read later
2019-02-13 19:18:09 +09:00
ToruNiina
53f6b8268b fix: compare offset_datetime correctly 2019-02-13 13:34:26 +09:00
ToruNiina
32dcc35918 move return_type_of_t from result to traits 2019-02-13 13:34:03 +09:00
ToruNiina
8c3854b28b update README 2019-01-31 15:37:25 +09:00
Toru Niina
75af9c79df Merge pull request #22 from xaxousis/master
Fix multiple definition error
2019-01-31 01:34:33 +09:00
Quentin Khan
1dfe32acd8 Fix multiple definition error 2019-01-30 17:06:23 +01:00
Toru Niina
5dfdbe4bff Merge pull request #20 from ToruNiina/format-error
add an extra parameter `hints` to format_error
2018-12-27 20:34:53 +09:00
Toru Niina
4584eeb57a Merge pull request #19 from ToruNiina/find-default-type
add default template arg to toml::find
2018-12-27 20:34:36 +09:00
ToruNiina
aa67069387 move hints to the internal function 2018-12-27 16:32:20 +09:00
ToruNiina
ee3424ad51 add an extra parameter hints to format_error 2018-12-27 16:26:23 +09:00
ToruNiina
17def14ab6 add default template arg to toml::find
in most of the use cases, toml::value is used (to show error message).
2018-12-27 15:58:50 +09:00
Toru Niina
b5b8830c29 Merge pull request #17 from ToruNiina/hotfix
fix the error with BOM and end of file w/o newline
2018-12-24 16:37:10 +09:00
ToruNiina
87a5c844c2 add test cases for the end-of-file problems 2018-12-24 16:02:32 +09:00
ToruNiina
11c7ee4501 fix the case of file w/o newline at the end
toml::parse failed with the file that contains whitespace or comment at
the end of file without newline. this commit fixes the error.
2018-12-24 16:00:33 +09:00
ToruNiina
d24a188d4c fix the error while reading BOM.
remove possible UB because of the use-after-move.
2018-12-24 15:06:26 +09:00
Toru Niina
29876221f8 Merge pull request #15 from ToruNiina/performance
speedup by removing needless format_underline
2018-12-23 18:30:19 +09:00
ToruNiina
7c03c446fe speedup by removing needless format_underline
drastical speedup for long toml files
2018-12-23 15:22:12 +09:00
Toru Niina
cfdd4d4a90 Merge pull request #14 from ToruNiina/error-message
improve error message quality
2018-12-22 18:46:00 +09:00
ToruNiina
5546b3389d Merge branch 'master' into error-message 2018-12-22 17:55:59 +09:00
ToruNiina
9c95992dad fix error message for empty value 2018-12-22 17:44:09 +09:00
ToruNiina
edb48b2872 add test_error_detection to check it detects error 2018-12-22 17:43:42 +09:00
ToruNiina
c63ac7e435 detect syntax_error; appending array-of-tables
toml file like the following is explicitly prohibited.
a = [{b = 1}]
[[a]]
b = 2
this commit detects this kind of syntax-error while parsing toml file
2018-12-22 17:07:06 +09:00
ToruNiina
fec49aaaa3 fix error message: add missing spaces 2018-12-22 17:06:36 +09:00
ToruNiina
617187969c fix result::unwrap_or with rvalue ref; merge branch 'hotfix' 2018-12-17 23:54:17 +09:00
ToruNiina
e3217cd572 quit returning rvalue ref from unwrap_or 2018-12-17 23:17:45 +09:00
ToruNiina
4d02f399a2 add temporary to receive rvalue 2018-12-17 23:03:53 +09:00
ToruNiina
24723226f1 remove template argument from result::unwrap_or 2018-12-17 19:18:16 +09:00
ToruNiina
7b3684b54e add and_other and or_other to toml::result
effectively same as Rust's std::Result::and and or.
2018-12-17 18:24:41 +09:00
ToruNiina
13c1f9c259 output filename of the second value2 if different
in format_error.
2018-12-17 18:07:57 +09:00
ToruNiina
6df75ad28e fix README 2018-12-17 16:56:09 +09:00
ToruNiina
74fc70cfee return copied object from except
having reference makes some technical difficulties.
2018-12-17 10:57:40 +09:00
Toru Niina
91ac2debce Merge pull request #12 from ToruNiina/hotfix
enable to get toml::value as toml::value
2018-12-17 10:50:05 +09:00
Toru Niina
0de89a9f19 Merge pull request #13 from ToruNiina/error-format
enable to show user-defined error message with (a) toml::value(s)
2018-12-17 10:49:47 +09:00
ToruNiina
130609bf5f update README 2018-12-16 23:51:38 +09:00
ToruNiina
ab41e7acb9 enable to pass 2 value and change interface for clarity 2018-12-16 21:50:18 +09:00
ToruNiina
c15bc8df4a add format_error(toml::value, msg, comment) 2018-12-16 21:46:32 +09:00
ToruNiina
19524dbc4b fix silly typo 2018-12-16 21:13:21 +09:00
ToruNiina
c2e733a65d enable to get toml::value as toml::value 2018-12-16 20:50:40 +09:00
ToruNiina
0c08b9e940 fix typo 2018-12-15 22:02:46 +09:00
ToruNiina
06197605ba add link to toml-v0.5.0 official 2018-12-13 23:58:37 +09:00
ToruNiina
5c24cfd325 add example of multi-line error message 2018-12-13 23:47:32 +09:00
14 changed files with 1077 additions and 229 deletions

View File

@@ -3,12 +3,13 @@ toml11
[![Build Status](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![Build status](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases)
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
c++11 header-only toml parser depending only on c++ standard library.
C++11 header-only toml parser depending only on C++ standard library.
compatible to the latest version of TOML v0.5.0 after version 2.0.0.
compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) after version 2.0.0.
Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and works with C++98 & Boost.
@@ -42,8 +43,14 @@ In the case of file open error, it will throw `std::runtime_error`.
You can also pass a `stream` to the `toml::parse` function after checking the status.
Note that on __Windows OS__, stream that is opened as text-mode automatically converts
CRLF ("\r\n") into LF ("\n") and this leads inconsistency between file size and
the contents that would be read. This causes weird error. To use a file stream
with `toml::parse`, don't forget to pass binary mode flag when you open the
stream.
```cpp
std::ifstream ifs("sample.toml");
std::ifstream ifs("sample.toml", std::ios_base::binary);
assert(ifs.good());
const auto data = toml::parse(ifs /*, "filename" (optional)*/);
```
@@ -64,6 +71,19 @@ terminate called after throwing an instance of 'toml::syntax_error'
| ^------ expected newline, but got '='. # error reason
```
If you (mistakenly) duplicate tables and got an error, you may want to see where the other is. toml11 shows both at the same time.
```console
terminate called after throwing an instance of 'toml::syntax_error'
what(): [error] toml::insert_value: table ("table") already exists.
--> duplicate-table.toml
1 | [table]
| ~~~~~~~ table already exists here
...
3 | [table]
| ~~~~~~~ table defined twice
```
Since the error message generation is generally a difficult task, the current status is not ideal. toml11 needs your help. If you encounter a weird error message, please let us know and contribute to improve the quality!
### Getting a toml value
@@ -389,6 +409,59 @@ terminate called after throwing an instance of 'std::range_error'
| ~~~~~~~~~ should be in [0x00..0x10FFFF]
```
### Formatting your error
When you encounter an error after you read the toml value, you may want to
show the error with the value.
toml11 provides you a function that formats user-defined error message with
related values. With a code like the following,
```cpp
const auto value = toml::find<int>(data, "num");
if(value < 0)
{
std::cerr << toml::format_error("[error] value should be positive",
data.at("num"), "positive number required")
<< std::endl;
}
```
you will get an error message like this.
```console
[error] value should be positive
--> example.toml
3 | num = -42
| ~~~ positive number required
```
When you pass two values to `toml::format_error`,
```cpp
const auto min = toml::find<int>(range, "min");
const auto max = toml::find<int>(range, "max");
if(max < min)
{
std::cerr << toml::format_error("[error] max should be larger than min",
data.at("min"), "minimum number here",
data.at("max"), "maximum number here");
<< std::endl;
}
```
you will get an error message like this.
```console
[error] max should be larger than min
--> example.toml
3 | min = 54
| ~~ minimum number here
...
4 | max = 42
| ~~ maximum number here
```
## Underlying types
The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below.
@@ -419,6 +492,8 @@ I thank the contributor for providing great feature to this repository.
- Guillaume Fraux (@Luthaf)
- Windows support and CI on Appvayor
- Intel Compiler support
- Quentin Khan (@xaxousis)
- Found & Fixed a bug around ODR
## Licensing terms

View File

@@ -26,7 +26,8 @@ set(TEST_NAMES
test_from_toml
test_parse_file
test_parse_unicode
)
test_error_detection
)
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC)

View File

@@ -0,0 +1,199 @@
#define BOOST_TEST_MODULE "test_error_detection"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <iostream>
#include <fstream>
BOOST_AUTO_TEST_CASE(test_detect_empty_key)
{
std::istringstream stream(std::string("= \"value\""));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_empty_key");
}
catch(const toml::syntax_error& syn)
{
// to see the error message
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_missing_value)
{
std::istringstream stream(std::string("a ="));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_missing_value");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_too_many_value)
{
std::istringstream stream(std::string("a = 1 = \"value\""));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_too_many_value");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_duplicate_table)
{
std::istringstream stream(std::string(
"[table]\n"
"a = 42\n"
"[table]\n"
"b = 42\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_duplicate_table");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_conflict_array_table)
{
std::istringstream stream(std::string(
"[[table]]\n"
"a = 42\n"
"[table]\n"
"b = 42\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_conflict_array_table");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_conflict_table_array)
{
std::istringstream stream(std::string(
"[table]\n"
"a = 42\n"
"[[table]]\n"
"b = 42\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_conflict_table_array");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_duplicate_value)
{
std::istringstream stream(std::string(
"a = 1\n"
"a = 2\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_duplicate_value");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_conflicting_value)
{
std::istringstream stream(std::string(
"a.b = 1\n"
"a.b.c = 2\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_conflicting_value");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array)
{
std::istringstream stream(std::string(
"a = [1, 1.0]\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_inhomogeneous_array");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}
BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table)
{
std::istringstream stream(std::string(
"a = [{b = 1}]\n"
"[[a]]\n"
"b = 2\n"
));
bool exception_thrown = false;
try
{
toml::parse(stream, "test_detect_appending_array_of_table");
}
catch(const toml::syntax_error& syn)
{
std::cerr << syn.what() << std::endl;
exception_thrown = true;
}
BOOST_CHECK(exception_thrown);
}

View File

@@ -116,6 +116,14 @@ BOOST_AUTO_TEST_CASE(test_get_exact)
tab["key3"] = toml::value(123);
BOOST_CHECK(tab == toml::get<toml::table>(v));
}
{
toml::value v1(42);
BOOST_CHECK(v1 == toml::get<toml::value>(v1));
toml::value v2(54);
toml::get<toml::value>(v1) = v2;
BOOST_CHECK(v2 == toml::get<toml::value>(v1));
}
}
BOOST_AUTO_TEST_CASE(test_get_integer_type)

View File

@@ -68,9 +68,14 @@ BOOST_AUTO_TEST_CASE(test_expect)
{
toml::value v1(42);
toml::value v2(3.14);
BOOST_CHECK_EQUAL(42, toml::expect<int>(v1).unwrap_or(0));
BOOST_CHECK_EQUAL( 0, toml::expect<int>(v2).unwrap_or(0));
BOOST_CHECK_EQUAL("42", toml::expect<int>(v1).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none")));
BOOST_CHECK_EQUAL("none", toml::expect<int>(v2).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none")));
const auto v1_or_0 = toml::expect<int>(v1).unwrap_or(0);
const auto v2_or_0 = toml::expect<int>(v2).unwrap_or(0);
BOOST_CHECK_EQUAL(42, v1_or_0);
BOOST_CHECK_EQUAL( 0, v2_or_0);
const auto v1_or_none = toml::expect<int>(v1).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"));
const auto v2_or_none = toml::expect<int>(v2).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"));
BOOST_CHECK_EQUAL("42", v1_or_none);
BOOST_CHECK_EQUAL("none", v2_or_none);
}
}

View File

@@ -194,3 +194,504 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
// ---------------------------------------------------------------------------
// after here, the test codes generate the content of a file.
BOOST_AUTO_TEST_CASE(test_file_with_BOM)
{
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss, "test_file_with_BOM.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
);
{
std::ofstream ofs("tmp.toml");
ofs << table;
}
const auto data = toml::parse("tmp.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss, "test_file_with_BOM_CRLF.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
);
{
// with text-mode, "\n" is converted to "\r\n" and the resulting
// value will be "\r\r\n". To avoid the additional "\r", use binary
// mode.
std::ofstream ofs("tmp.toml", std::ios_base::binary);
ofs.write(table.data(), table.size());
}
const auto data = toml::parse("tmp.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_file_without_newline_at_the_end_of_file)
{
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\""
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\""
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_CRLF.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\" # comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\" # comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\" \t"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_ws.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\" \t"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_ws.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_files_end_with_comment)
{
// comment w/o newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
"# one more comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// comment w/ newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
"# one more comment\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// CRLF version
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
"# one more comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
"# one more comment\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
{
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces but no newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" "
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// CRLF
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" "
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}

View File

@@ -409,3 +409,33 @@ BOOST_AUTO_TEST_CASE(test_or_else)
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge");
}
}
BOOST_AUTO_TEST_CASE(test_and_or_other)
{
{
const toml::result<int, std::string> r1(toml::ok(42));
const toml::result<int, std::string> r2(toml::err<std::string>("foo"));
BOOST_CHECK_EQUAL(r1, r1.or_other(r2));
BOOST_CHECK_EQUAL(r2, r1.and_other(r2));
BOOST_CHECK_EQUAL(42, r1.or_other(r2).unwrap());
BOOST_CHECK_EQUAL("foo", r1.and_other(r2).unwrap_err());
}
{
auto r1_gen = []() -> toml::result<int, std::string> {
return toml::ok(42);
};
auto r2_gen = []() -> toml::result<int, std::string> {
return toml::err<std::string>("foo");
};
const auto r3 = r1_gen();
const auto r4 = r2_gen();
BOOST_CHECK_EQUAL(r3, r1_gen().or_other (r2_gen()));
BOOST_CHECK_EQUAL(r4, r1_gen().and_other(r2_gen()));
BOOST_CHECK_EQUAL(42, r1_gen().or_other (r2_gen()).unwrap());
BOOST_CHECK_EQUAL("foo", r1_gen().and_other(r2_gen()).unwrap_err());
}
}

View File

@@ -33,6 +33,30 @@ inline T&& get(value&& v)
return std::move(v.cast<detail::toml_value_t<T>::value>());
}
// ============================================================================
// T == toml::value; identity transformation.
template<typename T, typename std::enable_if<
std::is_same<T, ::toml::value>::value, std::nullptr_t>::type = nullptr>
inline T& get(value& v)
{
return v;
}
template<typename T, typename std::enable_if<
std::is_same<T, ::toml::value>::value, std::nullptr_t>::type = nullptr>
inline T const& get(const value& v)
{
return v;
}
template<typename T, typename std::enable_if<
std::is_same<T, ::toml::value>::value, std::nullptr_t>::type = nullptr>
inline T&& get(value&& v)
{
return std::move(v);
}
// ============================================================================
// integer convertible from toml::Integer
@@ -271,7 +295,7 @@ T get(const toml::value& v)
// ============================================================================
// find and get
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
find(const toml::table& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -283,7 +307,7 @@ find(const toml::table& tab, const toml::key& ky,
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&>()))
find(toml::table& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -295,7 +319,7 @@ find(toml::table& tab, const toml::key& ky,
}
return ::toml::get<T>(tab[ky]);
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
find(toml::table&& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -308,7 +332,7 @@ find(toml::table&& tab, const toml::key& ky,
return ::toml::get<T>(std::move(tab[ky]));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
find(const toml::value& v, const toml::key& ky)
{
@@ -321,7 +345,7 @@ find(const toml::value& v, const toml::key& ky)
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&>()))
find(toml::value& v, const toml::key& ky)
{
@@ -334,7 +358,7 @@ find(toml::value& v, const toml::key& ky)
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
find(toml::value&& v, const toml::key& ky)
{
@@ -456,52 +480,23 @@ auto get_or(toml::value&& v, const toml::key& ky, T&& opt)
// expect
template<typename T>
auto expect(const toml::value& v)
-> result<decltype(::toml::get<T>(v)), std::string>
result<T, std::string> expect(const toml::value& v) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const type_error& te)
catch(const std::exception& e)
{
return err(te.what());
return err(e.what());
}
}
template<typename T>
auto expect(toml::value& v)
-> result<decltype(::toml::get<T>(v)), std::string>
result<T, std::string> expect(const toml::value& v, const toml::key& k) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const type_error& te)
{
return err(te.what());
}
}
template<typename T>
auto expect(toml::value&& v)
-> result<decltype(::toml::get<T>(std::move(v))), std::string>
{
try
{
return ok(get<T>(std::move(v)));
}
catch(const type_error& te)
{
return err(te.what());
}
}
template<typename T>
auto expect(const toml::value& v, const toml::key& k)
-> result<decltype(::toml::get<T>(v, k)), std::string>
{
try
{
return ok(get<T>(v, k));
return ok(find<T>(v, k));
}
catch(const std::exception& e)
{
@@ -509,65 +504,12 @@ auto expect(const toml::value& v, const toml::key& k)
}
}
template<typename T>
auto expect(toml::value& v, const toml::key& k)
-> result<decltype(::toml::get<T>(v, k)), std::string>
result<T, std::string> expect(const toml::table& t, const toml::key& k,
std::string tablename = "unknown table") noexcept
{
try
{
return ok(get<T>(v, k));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T>
auto expect(toml::value&& v, const toml::key& k)
-> result<decltype(::toml::get<T>(std::move(v), k)), std::string>
{
try
{
return ok(get<T>(std::move(v), k));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T>
auto expect(const toml::table& t, const toml::key& k, std::string tn)
-> result<decltype(::toml::get<T>(t, k, std::move(tn))), std::string>
{
try
{
return ok(get<T>(t, k, std::move(tn)));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T>
auto expect(toml::table& t, const toml::key& k, std::string tn)
-> result<decltype(::toml::get<T>(t, k, std::move(tn))), std::string>
{
try
{
return ok(get<T>(t, k, std::move(tn)));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T>
auto expect(toml::table&& t, const toml::key& k, std::string tn)
-> result<decltype(::toml::get<T>(std::move(t), k, std::move(tn))), std::string>
{
try
{
return ok(get<T>(std::move(t), k, std::move(tn)));
return ok(find<T>(t, k, std::move(tablename)));
}
catch(const std::exception& e)
{

View File

@@ -34,8 +34,8 @@ parse_boolean(location<Container>& loc)
}
}
loc.iter() = first; //rollback
return err(format_underline("[error] toml::parse_boolean", loc,
"token is not boolean", {"boolean is `true` or `false`"}));
return err(std::string("[error] toml::parse_boolean: "
"the next token is not a boolean"));
}
template<typename Container>
@@ -63,8 +63,8 @@ parse_binary_integer(location<Container>& loc)
return ok(std::make_pair(retval, token.unwrap()));
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_binary_integer", loc,
"token is not binary integer", {"binary integer is like: 0b0011"}));
return err(std::string("[error] toml::parse_binary_integer:"
"the next token is not an integer"));
}
template<typename Container>
@@ -84,9 +84,8 @@ parse_octal_integer(location<Container>& loc)
return ok(std::make_pair(retval, token.unwrap()));
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_octal_integer", loc,
"token is not octal integer", {"octal integer is like: 0o775"}));
return err(std::string("[error] toml::parse_octal_integer:"
"the next token is not an integer"));
}
template<typename Container>
@@ -106,8 +105,8 @@ parse_hexadecimal_integer(location<Container>& loc)
return ok(std::make_pair(retval, token.unwrap()));
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_hexadecimal_integer", loc,
"token is not hex integer", {"hex integer is like: 0xC0FFEE"}));
return err(std::string("[error] toml::parse_hexadecimal_integer"
"the next token is not an integer"));
}
template<typename Container>
@@ -134,10 +133,8 @@ parse_integer(location<Container>& loc)
return ok(std::make_pair(retval, token.unwrap()));
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_integer", loc,
"token is not integer", {"integer is like: +42",
"hex integer is like: 0xC0FFEE", "octal integer is like: 0o775",
"binary integer is like: 0b0011"}));
return err(std::string("[error] toml::parse_integer: "
"the next token is not an integer"));
}
template<typename Container>
@@ -225,8 +222,8 @@ parse_floating(location<Container>& loc)
return ok(std::make_pair(v, token.unwrap()));
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_floating: ", loc,
"token is not a float", {"floating point is like: -3.14e+1"}));
return err(std::string("[error] toml::parse_floating: "
"the next token is not a float"));
}
template<typename Container>
@@ -286,9 +283,8 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
const auto first = loc.iter();
if(first == loc.end() || *first != '\\')
{
return err(format_underline("[error]: "
"toml::parse_escape_sequence: location does not points \"\\\"",
loc, "should be \"\\\""));
return err(std::string("[error]: toml::parse_escape_sequence: "
"the next token is not an escape sequence \"\\\""));
}
++loc.iter();
switch(*loc.iter())
@@ -527,8 +523,8 @@ parse_string(location<Container>& loc)
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
return err(format_underline("[error] toml::parse_string: not a string",
loc, "not a string"));
return err(std::string("[error] toml::parse_string: "
"the next token is not a string"));
}
template<typename Container>
@@ -576,11 +572,9 @@ parse_local_date(location<Container>& loc)
}
else
{
auto msg = format_underline("[error]: toml::parse_local_date: "
"invalid format", loc, token.unwrap_err(),
{"local date is like: 1979-05-27"});
loc.iter() = first;
return err(std::move(msg));
return err(std::string("[error]: toml::parse_local_date: "
"the next token is not a local_date"));
}
}
@@ -661,11 +655,9 @@ parse_local_time(location<Container>& loc)
}
else
{
auto msg = format_underline("[error]: toml::parse_local_time: "
"invalid format", loc, token.unwrap_err(),
{"local time is like: 00:32:00.999999"});
loc.iter() = first;
return err(std::move(msg));
return err(std::string("[error]: toml::parse_local_time: "
"the next token is not a local_time"));
}
}
@@ -706,11 +698,9 @@ parse_local_datetime(location<Container>& loc)
}
else
{
auto msg = format_underline("[error]: toml::parse_local_datetime: "
"invalid format", loc, token.unwrap_err(),
{"local datetime is like: 1979-05-27T00:32:00.999999"});
loc.iter() = first;
return err(std::move(msg));
return err(std::string("[error]: toml::parse_local_datetime: "
"the next token is not a local_datetime"));
}
}
@@ -757,12 +747,9 @@ parse_offset_datetime(location<Container>& loc)
}
else
{
auto msg = format_underline("[error]: toml::parse_offset_datetime: "
"invalid format", loc, token.unwrap_err(),
{"offset datetime is like: 1979-05-27T00:32:00-07:00",
"or in UTC (w/o offset) : 1979-05-27T00:32:00Z"});
loc.iter() = first;
return err(std::move(msg));
return err(std::string("[error]: toml::parse_offset_datetime: "
"the next token is not a local_datetime"));
}
}
@@ -781,8 +768,8 @@ result<key, std::string> parse_simple_key(location<Container>& loc)
{
return ok(bare.unwrap().str());
}
return err(format_underline("[error] toml::parse_simple_key: "
"the next token is not a simple key", loc, "not a key"));
return err(std::string("[error] toml::parse_simple_key: "
"the next token is not a simple key"));
}
// dotted key become vector of keys
@@ -835,8 +822,8 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
{
return ok(std::vector<key>(1, smpl.unwrap()));
}
return err(format_underline("toml::parse_key: the next token is not a key",
loc, "not a key"));
return err(std::string("[error] toml::parse_key: "
"the next token is not a key"));
}
// forward-decl to implement parse_array and parse_table
@@ -854,8 +841,7 @@ parse_array(location<Container>& loc)
}
if(*loc.iter() != '[')
{
return err(format_underline("[error] toml::parse_array: "
"token is not an array", loc, "should be ["));
return err("[error] toml::parse_array: token is not an array");
}
++loc.iter();
@@ -881,7 +867,7 @@ parse_array(location<Container>& loc)
throw syntax_error(format_underline(
"[error] toml::parse_array: type of elements should be the "
"same each other.", region<Container>(loc, first, loc.iter()),
"inhomogenous types"));
"inhomogeneous types"));
}
retval.push_back(std::move(val.unwrap()));
}
@@ -903,13 +889,13 @@ parse_array(location<Container>& loc)
}
else
{
return err(format_underline("[error] toml::parse_array: "
"missing array separator `,`", loc, "should be `,`"));
throw syntax_error(format_underline("[error] toml::parse_array:"
" missing array separator `,`", loc, "should be `,`"));
}
}
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_array: "
throw syntax_error(format_underline("[error] toml::parse_array: "
"array did not closed by `]`", loc, "should be closed"));
}
@@ -971,7 +957,8 @@ parse_key_value_pair(location<Container>& loc)
}
else
{
msg = val.unwrap_err();
msg = format_underline("[error] toml::parse_key_value_pair: "
"invalid value format", loc, val.unwrap_err());
}
loc.iter() = first;
return err(msg);
@@ -1035,9 +1022,9 @@ insert_nested_key(table& root, const toml::value& v,
"[error] toml::insert_value: array of table (\"",
format_dotted_keys(first, last), "\") collides with"
" existing value"), get_region(tab->at(k)),
concat_to_string("this ", tab->at(k).type(), "value"
"already exists"), get_region(v), "while inserting"
"this array-of-tables"));
concat_to_string("this ", tab->at(k).type(),
" value already exists"), get_region(v),
"while inserting this array-of-tables"));
}
array& a = tab->at(k).template cast<toml::value_t::Array>();
if(!(a.front().is(value_t::Table)))
@@ -1046,9 +1033,34 @@ insert_nested_key(table& root, const toml::value& v,
"[error] toml::insert_value: array of table (\"",
format_dotted_keys(first, last), "\") collides with"
" existing value"), get_region(tab->at(k)),
concat_to_string("this ", tab->at(k).type(), "value"
"already exists"), get_region(v), "while inserting"
"this array-of-tables"));
concat_to_string("this ", tab->at(k).type(),
" value already exists"), get_region(v),
"while inserting this array-of-tables"));
}
// avoid conflicting array of table like the following.
// ```toml
// a = [{b = 42}] # define a as an array of *inline* tables
// [[a]] # a is an array of *multi-line* tables
// b = 54
// ```
// Here, from the type information, these cannot be detected
// bacause inline table is also a table.
// But toml v0.5.0 explicitly says it is invalid. The above
// array-of-tables has a static size and appending to the
// array is invalid.
// In this library, multi-line table value has a region
// that points to the key of the table (e.g. [[a]]). By
// comparing the first two letters in key, we can detect
// the array-of-table is inline or multiline.
if(detail::get_region(a.front()).str().substr(0,2) != "[[")
{
throw syntax_error(format_underline(concat_to_string(
"[error] toml::insert_value: array of table (\"",
format_dotted_keys(first, last), "\") collides with"
" existing array-of-tables"), get_region(tab->at(k)),
concat_to_string("this ", tab->at(k).type(),
" value has static size"), get_region(v),
"appending this to the statically sized array"));
}
a.push_back(v);
return ok(true);
@@ -1155,10 +1167,11 @@ parse_inline_table(location<Container>& loc)
table retval;
if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
{
return err(format_underline("[error] toml::parse_inline_table: "
"the next token is not an inline table", loc, "not `{`."));
return err(std::string("[error] toml::parse_inline_table: "
"the next token is not an inline table"));
}
++loc.iter();
// it starts from "{". it should be formatted as inline-table
while(loc.iter() != loc.end())
{
maybe<lex_ws>::invoke(loc);
@@ -1198,13 +1211,14 @@ parse_inline_table(location<Container>& loc)
}
else
{
return err(format_underline("[error] toml:::parse_inline_table:"
" missing table separator `,` ", loc, "should be `,`"));
throw syntax_error(format_underline("[error] "
"toml:::parse_inline_table: missing table separator `,` ",
loc, "should be `,`"));
}
}
}
loc.iter() = first;
return err(format_underline("[error] toml::parse_inline_table: "
throw syntax_error(format_underline("[error] toml::parse_inline_table: "
"inline table did not closed by `}`", loc, "should be closed"));
}
@@ -1331,7 +1345,7 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
return err(std::string("toml::parse_ml_table: input is empty"));
}
// XXX at lest one newline is needed
// XXX at lest one newline is needed.
using skip_line = repeat<
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>, at_least<1>>;
skip_line::invoke(loc);
@@ -1351,6 +1365,7 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
loc.iter() = before;
return ok(tab);
}
if(const auto kv = parse_key_value_pair(loc))
{
const std::vector<key>& keys = kv.unwrap().first;
@@ -1367,6 +1382,17 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
return err(kv.unwrap_err());
}
// comment lines are skipped by the above function call.
// However, since the `skip_line` requires at least 1 newline, it fails
// if the file ends with ws and/or comment without newline.
// `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment`
// itself. To skip the last ws and/or comment, call lexers.
// It does not matter if these fails, so the return value is discarded.
lex_ws::invoke(loc);
lex_comment::invoke(loc);
// skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines
// and comments after the last key-value pairs are allowed.
const auto newline = skip_line::invoke(loc);
if(!newline && loc.iter() != loc.end())
{
@@ -1379,11 +1405,10 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
return err(msg);
}
// comment lines are skipped by the above function call.
// However, if the file ends with comment without newline,
// it might cause parsing error because skip_line matches
// `comment + newline`, not `comment` itself. to skip the
// last comment, call lex_comment one more time.
// the skip_lines only matches with lines that includes newline.
// to skip the last line that includes comment and/or whitespace
// but no newline, call them one more time.
lex_ws::invoke(loc);
lex_comment::invoke(loc);
}
return ok(tab);
@@ -1472,10 +1497,10 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
// be compared to char. However, since we are always out of luck, we need to
// check our chars are equivalent to BOM. To do this, first we need to
// convert char to unsigned char to guarantee the comparability.
if(letters.size() >= 3)
if(loc.source()->size() >= 3)
{
std::array<unsigned char, 3> BOM;
std::memcpy(BOM.data(), letters.data(), 3);
std::memcpy(BOM.data(), loc.source()->data(), 3);
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
{
loc.iter() += 3; // BOM found. skip.
@@ -1492,7 +1517,7 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
inline table parse(const std::string& fname)
{
std::ifstream ifs(fname.c_str());
std::ifstream ifs(fname.c_str(), std::ios_base::binary);
if(!ifs.good())
{
throw std::runtime_error("toml::parse: file open error -> " + fname);

View File

@@ -262,10 +262,8 @@ inline std::string format_underline(const std::string& message,
std::max(line_number1.size(), line_number2.size());
std::ostringstream retval;
retval << message;
retval << newline;
retval << " --> ";
retval << reg1.name() << newline;;
retval << message << newline;
retval << " --> " << reg1.name() << newline;
// ---------------------------------------
retval << ' ' << std::setw(line_num_width) << line_number1;
retval << " | " << line1 << newline;
@@ -276,7 +274,14 @@ inline std::string format_underline(const std::string& message,
retval << ' ';
retval << comment_for_underline1 << newline;
// ---------------------------------------
retval << " ..." << newline;
if(reg2.name() != reg1.name())
{
retval << " --> " << reg2.name() << newline;
}
else
{
retval << " ..." << newline;
}
retval << ' ' << std::setw(line_num_width) << line_number2;
retval << " | " << line2 << newline;
retval << make_string(line_num_width + 1, ' ');

View File

@@ -2,6 +2,7 @@
// Distributed under the MIT License.
#ifndef TOML11_RESULT_H
#define TOML11_RESULT_H
#include "traits.hpp"
#include <type_traits>
#include <stdexcept>
#include <utility>
@@ -13,19 +14,6 @@
namespace toml
{
#if __cplusplus >= 201703L
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
template<typename T>
struct success
{
@@ -396,23 +384,20 @@ struct result
return std::move(this->succ.value);
}
template<typename U>
value_type& unwrap_or(U& opt) &
value_type& unwrap_or(value_type& opt) &
{
if(is_err()) {return opt;}
return this->succ.value;
}
template<typename U>
value_type const& unwrap_or(U const& opt) const&
value_type const& unwrap_or(value_type const& opt) const&
{
if(is_err()) {return opt;}
return this->succ.value;
}
template<typename U>
value_type&& unwrap_or(U&& opt) &&
value_type unwrap_or(value_type opt) &&
{
if(is_err()) {return std::move(opt);}
return std::move(this->succ.value);
if(is_err()) {return opt;}
return this->succ.value;
}
error_type& unwrap_err() &
@@ -444,21 +429,21 @@ struct result
// F: T -> U
// retval: result<U, E>
template<typename F>
result<return_type_of_t<F, value_type&>, error_type>
result<detail::return_type_of_t<F, value_type&>, error_type>
map(F&& f) &
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type const&>, error_type>
result<detail::return_type_of_t<F, value_type const&>, error_type>
map(F&& f) const&
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type &&>, error_type>
result<detail::return_type_of_t<F, value_type &&>, error_type>
map(F&& f) &&
{
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
@@ -469,21 +454,21 @@ struct result
// F: E -> F
// retval: result<T, F>
template<typename F>
result<value_type, return_type_of_t<F, error_type&>>
result<value_type, detail::return_type_of_t<F, error_type&>>
map_err(F&& f) &
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type const&>>
result<value_type, detail::return_type_of_t<F, error_type const&>>
map_err(F&& f) const&
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type&&>>
result<value_type, detail::return_type_of_t<F, error_type&&>>
map_err(F&& f) &&
{
if(this->is_err()){return err(f(std::move(this->as_err())));}
@@ -494,21 +479,21 @@ struct result
// F: T -> U
// retval: U
template<typename F, typename U>
return_type_of_t<F, value_type&>
detail::return_type_of_t<F, value_type&>
map_or_else(F&& f, U&& opt) &
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type const&>
detail::return_type_of_t<F, value_type const&>
map_or_else(F&& f, U&& opt) const&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type&&>
detail::return_type_of_t<F, value_type&&>
map_or_else(F&& f, U&& opt) &&
{
if(this->is_err()){return std::forward<U>(opt);}
@@ -519,21 +504,21 @@ struct result
// F: E -> U
// retval: U
template<typename F, typename U>
return_type_of_t<F, error_type&>
detail::return_type_of_t<F, error_type&>
map_err_or_else(F&& f, U&& opt) &
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
return_type_of_t<F, error_type const&>
detail::return_type_of_t<F, error_type const&>
map_err_or_else(F&& f, U&& opt) const&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
return_type_of_t<F, error_type&&>
detail::return_type_of_t<F, error_type&&>
map_err_or_else(F&& f, U&& opt) &&
{
if(this->is_ok()){return std::forward<U>(opt);}
@@ -545,21 +530,21 @@ struct result
// toml::err(error_type) should be convertible to U.
// normally, type U is another result<S, F> and E is convertible to F
template<typename F>
return_type_of_t<F, value_type&>
detail::return_type_of_t<F, value_type&>
and_then(F&& f) &
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type const&>
detail::return_type_of_t<F, value_type const&>
and_then(F&& f) const&
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type&&>
detail::return_type_of_t<F, value_type&&>
and_then(F&& f) &&
{
if(this->is_ok()){return f(std::move(this->as_ok()));}
@@ -571,27 +556,47 @@ struct result
// toml::ok(value_type) should be convertible to U.
// normally, type U is another result<S, F> and T is convertible to S
template<typename F>
return_type_of_t<F, error_type&>
detail::return_type_of_t<F, error_type&>
or_else(F&& f) &
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type const&>
detail::return_type_of_t<F, error_type const&>
or_else(F&& f) const&
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type&&>
detail::return_type_of_t<F, error_type&&>
or_else(F&& f) &&
{
if(this->is_err()){return f(std::move(this->as_err()));}
return ok(std::move(this->as_ok()));
}
// if *this is error, returns *this. otherwise, returns other.
result and_other(const result& other) const&
{
return this->is_err() ? *this : other;
}
result and_other(result&& other) &&
{
return this->is_err() ? std::move(*this) : std::move(other);
}
// if *this is okay, returns *this. otherwise, returns other.
result or_other(const result& other) const&
{
return this->is_ok() ? *this : other;
}
result or_other(result&& other) &&
{
return this->is_ok() ? std::move(*this) : std::move(other);
}
void swap(result<T, E>& other)
{
result<T, E> tmp(std::move(*this));
@@ -638,5 +643,22 @@ void swap(result<T, E>& lhs, result<T, E>& rhs)
return;
}
// this might be confusing because it eagerly evaluated, while in the other
// cases operator && and || are short-circuited.
//
// template<typename T, typename E>
// inline result<T, E>
// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? rhs : lhs;
// }
//
// template<typename T, typename E>
// inline result<T, E>
// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? lhs : rhs;
// }
} // toml11
#endif// TOML11_RESULT_H

View File

@@ -15,6 +15,9 @@ namespace detail
template<typename T>
using unwrap_t = typename std::decay<T>::type;
// ---------------------------------------------------------------------------
// check whether type T is a kind of container/map class
struct has_iterator_impl
{
template<typename T> static std::true_type check(typename T::iterator*);
@@ -63,6 +66,9 @@ struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){}
#undef decltype(...)
#endif
// ---------------------------------------------------------------------------
// C++17 and/or/not
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
@@ -80,6 +86,9 @@ struct disjunction<T, Ts...> :
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
// ---------------------------------------------------------------------------
// normal type checker
template<typename T> struct is_std_pair : std::false_type{};
template<typename T1, typename T2>
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
@@ -92,7 +101,9 @@ template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
// to use toml::get<std::tuple<T1, T2, ...>> in C++11
// ---------------------------------------------------------------------------
// C++14 index_sequence
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -116,6 +127,21 @@ struct index_sequence_maker<0>
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
// ---------------------------------------------------------------------------
// return_type_of_t
#if __cplusplus >= 201703L
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
}// detail
}//toml
#endif // TOML_TRAITS

View File

@@ -137,17 +137,6 @@ template<> struct toml_value_t<LocalDate >{static constexpr value_t value =
template<> struct toml_value_t<LocalTime >{static constexpr value_t value = value_t::LocalTime ;};
template<> struct toml_value_t<Array >{static constexpr value_t value = value_t::Array ;};
template<> struct toml_value_t<Table >{static constexpr value_t value = value_t::Table ;};
template<typename T> constexpr value_t toml_value_t<T>::value;
constexpr value_t toml_value_t<Boolean >::value;
constexpr value_t toml_value_t<Integer >::value;
constexpr value_t toml_value_t<Float >::value;
constexpr value_t toml_value_t<String >::value;
constexpr value_t toml_value_t<OffsetDatetime>::value;
constexpr value_t toml_value_t<LocalDatetime >::value;
constexpr value_t toml_value_t<LocalDate >::value;
constexpr value_t toml_value_t<LocalTime >::value;
constexpr value_t toml_value_t<Array >::value;
constexpr value_t toml_value_t<Table >::value;
template<typename T>
struct is_exact_toml_type : disjunction<

View File

@@ -767,6 +767,8 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs)
return lhs.cast<value_t::Float >() < rhs.cast<value_t::Float >();
case value_t::String :
return lhs.cast<value_t::String >() < rhs.cast<value_t::String >();
case value_t::OffsetDatetime:
return lhs.cast<value_t::OffsetDatetime>() < rhs.cast<value_t::OffsetDatetime>();
case value_t::LocalDatetime:
return lhs.cast<value_t::LocalDatetime>() < rhs.cast<value_t::LocalDatetime>();
case value_t::LocalDate:
@@ -800,5 +802,23 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs)
return !(lhs < rhs);
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v, const std::string& comment,
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg, detail::get_region(v), comment,
std::move(hints));
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v1, const std::string& comment1,
const toml::value& v2, const std::string& comment2,
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg, detail::get_region(v1), comment1,
detail::get_region(v2), comment2,
std::move(hints));
}
}// toml
#endif// TOML11_VALUE