mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
52 Commits
v3.0.0-bet
...
find-fuzzy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bf4ac0965 | ||
|
|
d53026837a | ||
|
|
6d31cccc5b | ||
|
|
99e46813f4 | ||
|
|
c0df39ca49 | ||
|
|
3e6747cfeb | ||
|
|
4cebd660fd | ||
|
|
43907de365 | ||
|
|
9b43171b65 | ||
|
|
c9543d8d9e | ||
|
|
15b68a89c6 | ||
|
|
7a1b5bd64e | ||
|
|
e332e018db | ||
|
|
b1ec6d87bd | ||
|
|
8dded288b4 | ||
|
|
0f491c7f3a | ||
|
|
5edf43a1d2 | ||
|
|
cffc605505 | ||
|
|
fb91936a1d | ||
|
|
8833292858 | ||
|
|
3fe04aff77 | ||
|
|
138f030b5d | ||
|
|
2eb2e0a753 | ||
|
|
87e0ba201e | ||
|
|
24a05c7c93 | ||
|
|
c3653b85f1 | ||
|
|
00b05c63b9 | ||
|
|
35b7c79ebd | ||
|
|
9ef146d022 | ||
|
|
2c192af35d | ||
|
|
c2435b0d56 | ||
|
|
9b12b17d5e | ||
|
|
e61b38fac2 | ||
|
|
716f7bacba | ||
|
|
299d1098e4 | ||
|
|
c272188060 | ||
|
|
0fc0967f6f | ||
|
|
df0d870c97 | ||
|
|
d5299fef04 | ||
|
|
937a3b4a2e | ||
|
|
0502924d25 | ||
|
|
6182f3ee9d | ||
|
|
3624e4b690 | ||
|
|
37e96ed8dc | ||
|
|
79e7511871 | ||
|
|
284f122433 | ||
|
|
134475e292 | ||
|
|
28b3f7d6fb | ||
|
|
6b5fd349aa | ||
|
|
76e44a0c48 | ||
|
|
b4bbd0a005 | ||
|
|
f9ee645dc2 |
@@ -15,6 +15,29 @@ jobs:
|
||||
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
|
||||
go get github.com/BurntSushi/toml-test
|
||||
$GOPATH/bin/toml-test ./check_toml_test
|
||||
test_serialization:
|
||||
docker:
|
||||
- image: circleci/buildpack-deps:bionic
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
g++ --version
|
||||
cd tests/
|
||||
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization
|
||||
git clone https://github.com/BurntSushi/toml-test.git
|
||||
cp check_serialization toml-test/tests/valid
|
||||
cd toml-test/tests/valid
|
||||
for f in $(ls ./*.toml);
|
||||
do echo "==> ${f}";
|
||||
cat ${f};
|
||||
echo "---------------------------------------";
|
||||
./check_serialization ${f};
|
||||
if [ $? -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
echo "=======================================";
|
||||
done
|
||||
output_result:
|
||||
docker:
|
||||
- image: circleci/buildpack-deps:bionic
|
||||
@@ -24,7 +47,7 @@ jobs:
|
||||
command: |
|
||||
g++ --version
|
||||
cd tests/
|
||||
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check.cpp -o check
|
||||
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check
|
||||
git clone https://github.com/BurntSushi/toml-test.git
|
||||
cp check toml-test/tests/invalid
|
||||
cp check toml-test/tests/valid
|
||||
@@ -56,4 +79,5 @@ workflows:
|
||||
test:
|
||||
jobs:
|
||||
- test_suite
|
||||
- test_serialization
|
||||
- output_result
|
||||
|
||||
@@ -3,9 +3,9 @@ enable_testing()
|
||||
|
||||
project(toml11)
|
||||
|
||||
set(toml11_VERSION_MAYOR 2)
|
||||
set(toml11_VERSION_MINOR 4)
|
||||
set(toml11_VERSION_PATCH 0)
|
||||
set(toml11_VERSION_MAYOR 3)
|
||||
set(toml11_VERSION_MINOR 0)
|
||||
set(toml11_VERSION_PATCH 1)
|
||||
set(toml11_VERSION
|
||||
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
|
||||
)
|
||||
|
||||
260
README.md
260
README.md
@@ -22,7 +22,7 @@ You can see the error messages about invalid files and serialization results of
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include <toml11/toml.hpp>
|
||||
#include <toml.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
@@ -47,8 +47,9 @@ int main()
|
||||
- [Decoding a toml file](#decoding-a-toml-file)
|
||||
- [In the case of syntax error](#in-the-case-of-syntax-error)
|
||||
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
|
||||
- [Finding a toml value](#finding-a-toml-value-from-a-table)
|
||||
- [In the case of type error](#in-the-case-of-type-error)
|
||||
- [Finding a toml value](#finding-a-toml-value)
|
||||
- [Finding a value in a table](#finding-a-value-in-a-table)
|
||||
- [In case of error](#in-case-of-error)
|
||||
- [Dotted keys](#dotted-keys)
|
||||
- [Casting a toml value](#casting-a-toml-value)
|
||||
- [Checking value type](#checking-value-type)
|
||||
@@ -101,19 +102,19 @@ to pass a filename to the `toml::parse` function.
|
||||
|
||||
```cpp
|
||||
const std::string fname("sample.toml");
|
||||
const toml::table data = toml::parse(fname);
|
||||
const toml::value data = toml::parse(fname);
|
||||
```
|
||||
|
||||
If it encounters an error while opening a file, it will throw `std::runtime_error`.
|
||||
|
||||
You can also pass a `std::istream` to the `toml::parse` function.
|
||||
To show a filename in an error message, it is recommended to pass the filename
|
||||
with the stream.
|
||||
To show a filename in an error message, however, it is recommended to pass the
|
||||
filename with the stream.
|
||||
|
||||
```cpp
|
||||
std::ifstream ifs("sample.toml", std::ios_base::binary);
|
||||
assert(ifs.good());
|
||||
const auto data = toml::parse(ifs, /*optional*/ "sample.toml");
|
||||
const auto data = toml::parse(ifs, /*optional -> */ "sample.toml");
|
||||
```
|
||||
|
||||
**Note**: When you are **on Windows, open a file in binary mode**.
|
||||
@@ -185,7 +186,7 @@ representing unicode character is not a valid UTF-8 codepoint.
|
||||
| ^--------- should be in [0x00..0x10FFFF]
|
||||
```
|
||||
|
||||
## Finding a toml value from a table
|
||||
## Finding a toml value
|
||||
|
||||
After parsing successfully, you can obtain the values from the result of
|
||||
`toml::parse` using `toml::find` function.
|
||||
@@ -196,8 +197,6 @@ answer = 42
|
||||
pi = 3.14
|
||||
numbers = [1,2,3]
|
||||
time = 1979-05-27T07:32:00Z
|
||||
[tab]
|
||||
key = "value"
|
||||
```
|
||||
|
||||
``` cpp
|
||||
@@ -206,23 +205,16 @@ const auto answer = toml::find<std::int64_t >(data, "answer");
|
||||
const auto pi = toml::find<double >(data, "pi");
|
||||
const auto numbers = toml::find<std::vector<int>>(data, "numbers");
|
||||
const auto timepoint = toml::find<std::chrono::system_clock::time_point>(data, "time");
|
||||
const auto tab = toml::find<toml::table>(data, "tab");
|
||||
const auto key = toml::find<std::string>(tab, "key");
|
||||
```
|
||||
|
||||
If the value does not exist, `toml::find` throws an error with the location of
|
||||
the table.
|
||||
By default, `toml::find` returns a `toml::value`.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'std::out_of_range'
|
||||
what(): [error] key "answer" not found
|
||||
--> example.toml
|
||||
6 | [tab]
|
||||
| ~~~~~ in this table
|
||||
```cpp
|
||||
const toml::value& answer = toml::find(data, "answer");
|
||||
```
|
||||
|
||||
When you pass an exact TOML type that does not require type conversion,
|
||||
`toml::get` returns a reference without copying the value.
|
||||
`toml::find` returns a reference without copying the value.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::parse("sample.toml");
|
||||
@@ -232,12 +224,6 @@ const auto& answer = toml::find<toml::integer>(data, "answer");
|
||||
If the specified type requires conversion, you can't take a reference to the value.
|
||||
See also [underlying types](#underlying-types).
|
||||
|
||||
By default, `toml::find` returns a `toml::value`.
|
||||
|
||||
```cpp
|
||||
const toml::value& answer = toml::find(data, "answer");
|
||||
```
|
||||
|
||||
**NOTE**: For some technical reason, automatic conversion between `integer` and
|
||||
`floating` is not supported. If you want to get a floating value even if a value
|
||||
has integer value, you need to convert it manually after obtaining a value,
|
||||
@@ -250,45 +236,40 @@ double x = vx.is_floating() ? vx.as_floating(std::nothrow) :
|
||||
// floating nor integer.
|
||||
```
|
||||
|
||||
----
|
||||
### Finding a value in a table
|
||||
|
||||
`toml::find` accepts arbitrary number of keys to find a value buried in a
|
||||
deep recursion of tables.
|
||||
There are several way to get a value defined in a table.
|
||||
First, you can get a table as a normal value and find a value from the table.
|
||||
|
||||
```toml
|
||||
[fruit]
|
||||
name = "apple"
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
```
|
||||
|
||||
``` cpp
|
||||
const auto data = toml::parse("fruit.toml");
|
||||
const auto& fruit = toml::find(data, "fruit");
|
||||
const auto name = toml::find<std::string>(fruit, "apple");
|
||||
|
||||
const auto& physical = toml::find(fruit, "physical");
|
||||
const auto color = toml::find<std::string>(fruit, "color");
|
||||
const auto shape = toml::find<std::string>(fruit, "shape");
|
||||
```
|
||||
|
||||
Here, variable `fruit` is a `toml::value` and can be used as the first argument
|
||||
of `toml::find`.
|
||||
|
||||
Second, you can pass as many arguments as the number of subtables to `toml::find`.
|
||||
|
||||
```cpp
|
||||
// # expecting the following example.toml
|
||||
// answer.to.the.ultimate.question = 42
|
||||
// # is equivalent to {"answer": {"to":{"the":{"ultimate:{"question":42}}}}}
|
||||
|
||||
const toml::table data = toml::parse("example.toml");
|
||||
const int a = toml::find<int>(data, "answer", "to", "the", "ultimate", "question");
|
||||
const auto data = toml::parse("fruit.toml");
|
||||
const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
|
||||
const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape");
|
||||
```
|
||||
|
||||
Of course, alternatively, you can call `toml::find` as many as you need.
|
||||
But it is a bother.
|
||||
|
||||
### In the case of type error
|
||||
|
||||
If the specified type differs from the actual value contained, it throws
|
||||
`toml::type_error` that inherits `std::exception`.
|
||||
|
||||
Similar to the case of syntax error, toml11 also displays clean error messages.
|
||||
The error message when you choose `int` to get `string` value would be like this.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'toml::type_error'
|
||||
what(): [error] toml::value bad_cast to integer
|
||||
--> example.toml
|
||||
3 | title = "TOML Example"
|
||||
| ~~~~~~~~~~~~~~ the actual type is string
|
||||
```
|
||||
|
||||
**NOTE**: In order to show this kind of error message, all the toml values have
|
||||
a pointer to represent its range in a file. The entire contents of a file is
|
||||
shared by `toml::value`s and remains on the heap memory. It is recommended to
|
||||
destruct all the `toml::value` classes after configuring your application
|
||||
if you have a large TOML file compared to the memory resource.
|
||||
|
||||
### Dotted keys
|
||||
|
||||
TOML v0.5.0 has a new feature named "dotted keys".
|
||||
@@ -310,13 +291,14 @@ shape = "round"
|
||||
You can get both of the above tables with the same c++ code.
|
||||
|
||||
```cpp
|
||||
const auto physical = toml::find<toml::table>(data, "physical");
|
||||
const auto physical = toml::find(data, "physical");
|
||||
const auto color = toml::find<std::string>(physical, "color");
|
||||
```
|
||||
|
||||
The following code does not work for the above toml file.
|
||||
|
||||
```cpp
|
||||
// XXX this does not work!
|
||||
const auto color = toml::find<std::string>(data, "physical.color");
|
||||
```
|
||||
|
||||
@@ -328,6 +310,117 @@ The above code works with the following toml file.
|
||||
# NOT {"physical": {"color": "orange"}}.
|
||||
```
|
||||
|
||||
### In case of error
|
||||
|
||||
If the value does not exist, `toml::find` throws an error with the location of
|
||||
the table.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'std::out_of_range'
|
||||
what(): [error] key "answer" not found
|
||||
--> example.toml
|
||||
6 | [tab]
|
||||
| ~~~~~ in this table
|
||||
```
|
||||
|
||||
**Note**: It is recommended to find a table as `toml::value` because it has much information
|
||||
compared to `toml::table`, which is an alias of
|
||||
`std::unordered_map<std::string, toml::value>`. Since `toml::table` does not have
|
||||
any information about toml file, such as where the table was defined in the file.
|
||||
|
||||
----
|
||||
|
||||
If the specified type differs from the actual value contained, it throws
|
||||
`toml::type_error` that inherits `std::exception`.
|
||||
|
||||
Similar to the case of syntax error, toml11 also displays clean error messages.
|
||||
The error message when you choose `int` to get `string` value would be like this.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'toml::type_error'
|
||||
what(): [error] toml::value bad_cast to integer
|
||||
--> example.toml
|
||||
3 | title = "TOML Example"
|
||||
| ~~~~~~~~~~~~~~ the actual type is string
|
||||
```
|
||||
|
||||
**NOTE**: In order to show this kind of error message, all the toml values have
|
||||
a pointer to represent its range in a file. The entire contents of a file is
|
||||
shared by `toml::value`s and remains on the heap memory. It is recommended to
|
||||
destruct all the `toml::value` classes after configuring your application
|
||||
if you have a large TOML file compared to the memory resource.
|
||||
|
||||
### Fuzzy Search
|
||||
|
||||
To find a value, you can use `find_fuzzy` instead of `find`.
|
||||
|
||||
```toml
|
||||
[foobar]
|
||||
# typo!
|
||||
anseer = 42
|
||||
```
|
||||
|
||||
```cpp
|
||||
const auto data = toml::parse("sample.toml");
|
||||
const auto foobar = toml::find(data, "foobar");
|
||||
const auto answer = toml::find_fuzzy<int>(data, "answer"); // it finds "anseer".
|
||||
```
|
||||
|
||||
When the specified key is not found, `toml::find_fuzzy` calculates
|
||||
[levenstein distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
|
||||
between the specified key and other keys.
|
||||
If it finds a key that is 1 away from the specified key by the Levenstein
|
||||
distance, it returns the corresponding value.
|
||||
|
||||
To allow a more distant string, you can explicitly pass `toml::levenstein_matcher`
|
||||
to `find_fuzzy`.
|
||||
|
||||
```cpp
|
||||
toml::levenstein_matcher lev(2); // allow distance <= 2
|
||||
const auto answer = toml::find_fuzzy<int>(data, "answer", lev);
|
||||
```
|
||||
|
||||
You can also use your own distance metric. Implement your `fuzzy_matcher` that
|
||||
has `operator()` that takes two strings and returns true if two strings resemble
|
||||
each other.
|
||||
|
||||
```cpp
|
||||
struct fuzzy_matcher
|
||||
{
|
||||
bool operator()(const std::string& lhs, const std::string& rhs) const
|
||||
{
|
||||
// return true if lhs matches with rhs.
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If there are multiple keys that meets the condition, it throws `runtime_error`.
|
||||
|
||||
However, in many cases, rather than just allowing typographical errors,
|
||||
you will want to suggest it and encouledge users to correct it.
|
||||
|
||||
If you pass a `fuzzy_matcher` to `toml::find`, a suggestion will be displayed
|
||||
in the error message.
|
||||
|
||||
```cpp
|
||||
toml::levenstein_matcher lev(1); // finds keys within distance <= 1
|
||||
const auto answer = toml::find<int>(data, "answer", lev); // it throws!
|
||||
```
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'std::out_of_range'
|
||||
what(): [error] key "answer" not found.
|
||||
--> hoge.toml
|
||||
1 | [foobar]
|
||||
| ~~~~~~~~ in this table
|
||||
...
|
||||
2 | anseer = 42
|
||||
| ~~ did you mean this here?
|
||||
```
|
||||
|
||||
Note: Currently, `find_fuzzy` and `find(value, key, matcher)` take only one key.
|
||||
The codes like `find_fuzzy(value, key1, key2, key3)` do not work.
|
||||
|
||||
## Casting a toml value
|
||||
|
||||
### `toml::get`
|
||||
@@ -349,18 +442,22 @@ contain one of the following types.
|
||||
- It depends. See [customizing containers](#customizing-containers) for detail.
|
||||
|
||||
To get a value inside, you can use `toml::get<T>()`. The usage is the same as
|
||||
`toml::find<T>` (actually, `toml::find` internally uses `toml::get`).
|
||||
`toml::find<T>` (actually, `toml::find` internally uses `toml::get` after casting
|
||||
a value to `toml::table`).
|
||||
|
||||
``` cpp
|
||||
const toml::value data = toml::parse("sample.toml");
|
||||
const toml::value answer_ = toml::get<toml::table >(data).at("answer")
|
||||
const toml::value answer_ = toml::get<toml::table >(data).at("answer");
|
||||
const std::int64_t answer = toml::get<std::int64_t>(answer_);
|
||||
```
|
||||
|
||||
When you pass an exact TOML type that does not require type conversion,
|
||||
`toml::get` returns a reference through which you can modify the content.
|
||||
`toml::get` returns a reference through which you can modify the content
|
||||
(if the `toml::value` is `const`, it returns `const` reference).
|
||||
|
||||
```cpp
|
||||
toml::value data = toml::parse("sample.toml");
|
||||
toml::value answer_ = toml::get<toml::table >(data).at("answer");
|
||||
toml::integer& answer = toml::get<toml::integer>(answer_);
|
||||
answer = 6 * 9; // write to data.answer. now `answer_` contains 54.
|
||||
```
|
||||
@@ -427,8 +524,6 @@ class value {
|
||||
} // toml
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Checking value type
|
||||
|
||||
You can check the type of a value by `is_xxx` function.
|
||||
@@ -909,10 +1004,17 @@ const auto data = toml::parse<
|
||||
>("example.toml");
|
||||
```
|
||||
|
||||
__NOTE__: Needless to say, the result types from `toml::parse(...)` and
|
||||
Needless to say, the result types from `toml::parse(...)` and
|
||||
`toml::parse<Com, Map, Cont>(...)` are different (unless you specify the same
|
||||
types as default).
|
||||
|
||||
Note that, since `toml::table` and `toml::array` is an alias for a table and an
|
||||
array of a default `toml::value`, so it is different from the types actually
|
||||
contained in a `toml::basic_value` when you customize containers.
|
||||
To get the actual type in a generic way, use
|
||||
`typename toml::basic_type<C, T, A>::table_type` and
|
||||
`typename toml::basic_type<C, T, A>::array_type`.
|
||||
|
||||
## TOML literal
|
||||
|
||||
toml11 supports `"..."_toml` literal.
|
||||
@@ -945,7 +1047,6 @@ inline namespace literals
|
||||
inline namespace toml_literals
|
||||
{
|
||||
toml::value operator"" _toml(const char* str, std::size_t len);
|
||||
|
||||
} // toml_literals
|
||||
} // literals
|
||||
} // toml
|
||||
@@ -1237,7 +1338,7 @@ const toml::source_location loc = v.location();
|
||||
toml11 enables you to serialize data into toml format.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::table{{"foo", 42}, {"bar", "baz"}};
|
||||
const toml::value data{{"foo", 42}, {"bar", "baz"}};
|
||||
std::cout << data << std::endl;
|
||||
// bar = "baz"
|
||||
// foo = 42
|
||||
@@ -1247,10 +1348,10 @@ toml11 automatically makes a small table and small array inline.
|
||||
You can specify the width to make them inline by `std::setw` for streams.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::table{
|
||||
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}},
|
||||
{"quux", toml::array{"small", "array", "of", "strings"}},
|
||||
{"foobar", toml::array{"this", "array", "of", "strings", "is", "too", "long",
|
||||
const toml::value data{
|
||||
{"qux", {{"foo", 42}, {"bar", "baz"}}},
|
||||
{"quux", {"small", "array", "of", "strings"}},
|
||||
{"foobar", {"this", "array", "of", "strings", "is", "too", "long",
|
||||
"to", "print", "into", "single", "line", "isn't", "it?"}},
|
||||
};
|
||||
|
||||
@@ -1290,7 +1391,7 @@ To control the precision of floating point numbers, you need to pass
|
||||
`std::setprecision` to stream.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::table{
|
||||
const toml::value data{
|
||||
{"pi", 3.141592653589793},
|
||||
{"e", 2.718281828459045}
|
||||
};
|
||||
@@ -1356,6 +1457,9 @@ are used. See [Customizing containers](#customizing-containers) for detail.
|
||||
flag that represents a kind of a string, `string_t::basic` and `string_t::literal`.
|
||||
Although `std::string` is not an exact toml type, still you can get a reference
|
||||
that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
|
||||
The most important difference between `std::string` and `toml::string` is that
|
||||
`toml::string` will be formatted as a TOML string when outputed with `ostream`.
|
||||
This feature is introduced to make it easy to write a custom serializer.
|
||||
|
||||
`Datetime` variants are `struct` that are defined in this library.
|
||||
Because `std::chrono::system_clock::time_point` is a __time point__,
|
||||
@@ -1384,8 +1488,12 @@ Between v2 and v3, those interfaces are rearranged.
|
||||
- See [Casting a toml::value](#casting-a-tomlvalue) and [Checking value type](#checking-value-type) for detail.
|
||||
- An overload of `toml::find` for `toml::table` has been dropped. Use `toml::value` version instead.
|
||||
- Because type conversion between a table and a value causes ambiguity while overload resolution
|
||||
- Since `toml::parse` now returns a `toml::value`, this feature becomes less important.
|
||||
- Also because `toml::table` is a normal STL container, implementing utility function is easy.
|
||||
- See [Finding a toml::value](#finding-a-tomlvalue) for detail.
|
||||
- See [Finding a toml::value](#finding-a-toml-value) for detail.
|
||||
- An overload of `operator<<` and `toml::format` for `toml::table`s are dropped.
|
||||
- Use `toml::value` instead.
|
||||
- See [Serializing TOML data](#serializing-toml-data) for detail.
|
||||
- Interface around comments.
|
||||
- See [Preserving Comments](#preserving-comments) for detail.
|
||||
- An ancient `from_toml/into_toml` has been removed. Use arbitrary type conversion support.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
set(TEST_NAMES
|
||||
test_datetime
|
||||
test_string
|
||||
test_utility
|
||||
test_result
|
||||
test_traits
|
||||
@@ -26,6 +27,7 @@ set(TEST_NAMES
|
||||
test_get_or
|
||||
test_find
|
||||
test_find_or
|
||||
test_find_fuzzy
|
||||
test_expect
|
||||
test_parse_file
|
||||
test_serialize_file
|
||||
|
||||
57
tests/check_serialization.cpp
Normal file
57
tests/check_serialization.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "toml.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
std::cerr << "usage: ./check [filename]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string filename(argv[1]);
|
||||
|
||||
{
|
||||
const auto data = toml::parse(filename);
|
||||
{
|
||||
std::ofstream ofs("tmp.toml");
|
||||
ofs << std::setprecision(16) << std::setw(80) << data;
|
||||
}
|
||||
const auto serialized = toml::parse("tmp.toml");
|
||||
|
||||
if(data != serialized)
|
||||
{
|
||||
std::cerr << "============================================================\n";
|
||||
std::cerr << "result (w/o comment) different: " << filename << std::endl;
|
||||
std::cerr << "------------------------------------------------------------\n";
|
||||
std::cerr << "# serialized\n";
|
||||
std::cerr << serialized;
|
||||
std::cerr << "------------------------------------------------------------\n";
|
||||
std::cerr << "# data\n";
|
||||
std::cerr << data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto data = toml::parse<toml::preserve_comments>(filename);
|
||||
{
|
||||
std::ofstream ofs("tmp.toml");
|
||||
ofs << std::setprecision(16) << std::setw(80) << data;
|
||||
}
|
||||
const auto serialized = toml::parse<toml::preserve_comments>("tmp.toml");
|
||||
if(data != serialized)
|
||||
{
|
||||
std::cerr << "============================================================\n";
|
||||
std::cerr << "result (w/ comment) different: " << filename << std::endl;
|
||||
std::cerr << "------------------------------------------------------------\n";
|
||||
std::cerr << "# serialized\n";
|
||||
std::cerr << serialized;
|
||||
std::cerr << "------------------------------------------------------------\n";
|
||||
std::cerr << "# data\n";
|
||||
std::cerr << data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -460,3 +460,55 @@ BOOST_AUTO_TEST_CASE(test_overwrite_comments)
|
||||
BOOST_TEST(u.as_integer() == 42);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_output_comments)
|
||||
{
|
||||
using value_type = toml::basic_value<toml::preserve_comments>;
|
||||
{
|
||||
const value_type v(42, {"comment1", "comment2"});
|
||||
std::ostringstream oss;
|
||||
oss << v.comments();
|
||||
|
||||
std::ostringstream ref;
|
||||
ref << "#comment1\n";
|
||||
ref << "#comment2\n";
|
||||
|
||||
BOOST_TEST(oss.str() == ref.str());
|
||||
}
|
||||
{
|
||||
const value_type v(42, {"comment1", "comment2"});
|
||||
std::ostringstream oss;
|
||||
|
||||
// If v is not a table, toml11 assumes that user is writing something
|
||||
// like the following.
|
||||
|
||||
oss << "answer = " << v;
|
||||
|
||||
BOOST_TEST(oss.str() == "answer = 42 #comment1comment2");
|
||||
}
|
||||
|
||||
{
|
||||
const value_type v(42, {"comment1", "comment2"});
|
||||
std::ostringstream oss;
|
||||
|
||||
// If v is not a table, toml11 assumes that user is writing something
|
||||
// like the following.
|
||||
|
||||
oss << toml::nocomment << "answer = " << v;
|
||||
|
||||
BOOST_TEST(oss.str() == "answer = 42");
|
||||
}
|
||||
|
||||
{
|
||||
const value_type v(42, {"comment1", "comment2"});
|
||||
std::ostringstream oss;
|
||||
|
||||
// If v is not a table, toml11 assumes that user is writing something
|
||||
// like the following.
|
||||
|
||||
oss << toml::nocomment << toml::showcomment << "answer = " << v;
|
||||
|
||||
BOOST_TEST(oss.str() == "answer = 42 #comment1comment2");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
350
tests/test_find_fuzzy.cpp
Normal file
350
tests/test_find_fuzzy.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#define BOOST_TEST_MODULE "test_find_fuzzy"
|
||||
|
||||
#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>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_levenstein_distance)
|
||||
{
|
||||
const toml::levenstein_matcher lev(1);
|
||||
|
||||
// distance == 0
|
||||
{
|
||||
const std::string s1("foobar");
|
||||
const std::string s2 = s1;
|
||||
|
||||
BOOST_TEST(lev.distance(s1, s2) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
const std::string s1("foobar");
|
||||
const std::string s2("foobaz");
|
||||
|
||||
BOOST_TEST(lev.distance(s1, s2) == 1);
|
||||
}
|
||||
{
|
||||
const std::string s1("foobar"); // insertion (+x)
|
||||
const std::string s2("fooxbar");
|
||||
|
||||
BOOST_TEST(lev.distance(s1, s2) == 1);
|
||||
}
|
||||
{
|
||||
const std::string s1("foobar");
|
||||
const std::string s2("fooar"); // insertion(+b)
|
||||
|
||||
BOOST_TEST(lev.distance(s1, s2) == 1);
|
||||
}
|
||||
|
||||
// distance > 1
|
||||
{
|
||||
const std::string s1("foobar");
|
||||
const std::string s2("fooquux");
|
||||
|
||||
BOOST_TEST(lev.distance(s1, s2) == 4);
|
||||
}
|
||||
{
|
||||
const std::string s1("foobar");
|
||||
const std::string s2("fooqu");
|
||||
|
||||
BOOST_TEST(s1 != s2);
|
||||
BOOST_TEST(lev.distance(s1, s2) == 3);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_find_fuzzy)
|
||||
{
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", "value"} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
|
||||
|
||||
static_assert(std::is_same<
|
||||
toml::value&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
|
||||
|
||||
toml::find_fuzzy(v, "key") = "foobar";
|
||||
BOOST_TEST(toml::find(v, "keu") == toml::value("foobar"));
|
||||
}
|
||||
{
|
||||
const toml::value v{
|
||||
{"keu", "value"} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
|
||||
|
||||
static_assert(std::is_same<
|
||||
toml::value const&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", "value"} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy(std::move(v), "key") == toml::value("value"));
|
||||
|
||||
static_assert(std::is_same<
|
||||
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", "value"} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "kiwi"), std::out_of_range);
|
||||
|
||||
static_assert(std::is_same<
|
||||
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
|
||||
}
|
||||
|
||||
// find with conversion
|
||||
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", 42} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
|
||||
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
const toml::value v{
|
||||
{"keu", 42} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
|
||||
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", 42} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_TEST(toml::find_fuzzy<int>(std::move(v), "key") == 42);
|
||||
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find_fuzzy<int>(std::move(v), "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", 42} // typo! key -> keu
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "kiwi"), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_find_fuzzy_throw)
|
||||
{
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", "value"}, // typo! key -> keu
|
||||
{"ky", "value"} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
|
||||
}
|
||||
{
|
||||
const toml::value v{
|
||||
{"keu", "value"}, // typo! key -> keu
|
||||
{"ky", "value"} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", "value"}, // typo! key -> keu
|
||||
{"ky", "value"} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "key"), std::out_of_range);
|
||||
}
|
||||
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", 42}, // typo! key -> keu
|
||||
{"ky", 42} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
|
||||
}
|
||||
{
|
||||
const toml::value v{
|
||||
{"keu", 42}, // typo! key -> keu
|
||||
{"ky", 42} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
|
||||
}
|
||||
{
|
||||
toml::value v{
|
||||
{"keu", 42}, // typo! key -> keu
|
||||
{"ky", 42} // typo! key -> ky
|
||||
};
|
||||
|
||||
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "key"), std::out_of_range);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_find_throw_typo_aware_exception)
|
||||
{
|
||||
using namespace toml::literals::toml_literals;
|
||||
const toml::levenstein_matcher lev(1);
|
||||
{
|
||||
toml::value v = u8R"(
|
||||
keu = "value"
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find(v, "key", lev);
|
||||
(void)ret; // suppress unused variable
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
static_assert(std::is_same<
|
||||
toml::value&, decltype(toml::find(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
keu = "value"
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find(v, "key", lev);
|
||||
(void)ret;
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
static_assert(std::is_same<
|
||||
toml::value const&, decltype(toml::find(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v = u8R"(
|
||||
keu = "value"
|
||||
)"_toml;
|
||||
|
||||
bool thrown = false; // since it moves, we need to check both once
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find(std::move(v), "key", lev);
|
||||
(void)ret;
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
thrown = true;
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
BOOST_TEST(thrown);
|
||||
static_assert(std::is_same<
|
||||
toml::value&, decltype(toml::find(v, "key"))>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_find_throw_conversion_typo_aware_exception)
|
||||
{
|
||||
using namespace toml::literals::toml_literals;
|
||||
const toml::levenstein_matcher lev(1);
|
||||
{
|
||||
toml::value v = u8R"(
|
||||
keu = 42
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find<int>(v, "key", lev);
|
||||
(void)ret; // suppress unused variable
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find<int>(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
keu = 42
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find<int>(v, "key", lev);
|
||||
(void)ret;
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find<int>(v, "key"))>::value, "");
|
||||
}
|
||||
{
|
||||
toml::value v = u8R"(
|
||||
keu = 42
|
||||
)"_toml;
|
||||
|
||||
bool thrown = false; // since it moves, we need to check both once
|
||||
try
|
||||
{
|
||||
const auto& ret = toml::find<int>(std::move(v), "key", lev);
|
||||
(void)ret;
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
// exception.what() should include the typo-ed key name
|
||||
const std::string what(oor.what());
|
||||
BOOST_TEST(what.find("keu") != std::string::npos);
|
||||
thrown = true;
|
||||
|
||||
// std::cout << what << std::endl;
|
||||
}
|
||||
BOOST_TEST(thrown);
|
||||
static_assert(std::is_same<int,
|
||||
decltype(toml::find<int>(v, "key"))>::value, "");
|
||||
}
|
||||
}
|
||||
@@ -358,3 +358,36 @@ BOOST_AUTO_TEST_CASE(test_find_or_string)
|
||||
BOOST_TEST("bazqux" == toml::find_or(v2, "key", lit));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_find_or_map)
|
||||
{
|
||||
using map_type = std::map<std::string, std::string>;
|
||||
{
|
||||
const toml::value v1{
|
||||
{"key", {{"key", "value"}}}
|
||||
};
|
||||
|
||||
const auto key = toml::find_or(v1, "key", map_type{});
|
||||
const auto key2 = toml::find_or(v1, "key2", map_type{});
|
||||
|
||||
BOOST_TEST(!key.empty());
|
||||
BOOST_TEST(key2.empty());
|
||||
|
||||
BOOST_TEST(key.size() == 1u);
|
||||
BOOST_TEST(key.at("key") == "value");
|
||||
}
|
||||
{
|
||||
const toml::value v1{
|
||||
{"key", {{"key", "value"}}}
|
||||
};
|
||||
|
||||
const auto key = toml::find_or<map_type>(v1, "key", map_type{});
|
||||
const auto key2 = toml::find_or<map_type>(v1, "key2", map_type{});
|
||||
|
||||
BOOST_TEST(!key.empty());
|
||||
BOOST_TEST(key2.empty());
|
||||
|
||||
BOOST_TEST(key.size() == 1u);
|
||||
BOOST_TEST(key.at("key") == "value");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#endif
|
||||
#include <toml.hpp>
|
||||
#include <iostream>
|
||||
|
||||
// to check it successfully compiles. it does not check the formatted string.
|
||||
|
||||
|
||||
@@ -45,6 +45,16 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
|
||||
b = "baz"
|
||||
)"_toml;
|
||||
|
||||
BOOST_TEST(r == v);
|
||||
}
|
||||
{
|
||||
const toml::value r{
|
||||
{"array_of_tables", toml::array{toml::table{}}}
|
||||
};
|
||||
const toml::value v = u8R"(
|
||||
[[array_of_tables]]
|
||||
)"_toml;
|
||||
|
||||
BOOST_TEST(r == v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,39 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
template<typename Comment,
|
||||
template<typename ...> class Table,
|
||||
template<typename ...> class Array>
|
||||
bool has_comment_inside(const toml::basic_value<Comment, Table, Array>& v)
|
||||
{
|
||||
if(!v.comments().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// v itself does not have a comment.
|
||||
if(v.is_array())
|
||||
{
|
||||
for(const auto& x : v.as_array())
|
||||
{
|
||||
if(has_comment_inside(x))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(v.is_table())
|
||||
{
|
||||
for(const auto& x : v.as_table())
|
||||
{
|
||||
if(has_comment_inside(x.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/example.toml");
|
||||
@@ -37,12 +70,12 @@ BOOST_AUTO_TEST_CASE(test_example_map_dq)
|
||||
const auto data = toml::parse<toml::discard_comments, std::map, std::deque>(
|
||||
"toml/tests/example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp1.toml");
|
||||
std::ofstream ofs("tmp1_map_dq.toml");
|
||||
ofs << std::setw(80) << data;
|
||||
}
|
||||
|
||||
auto serialized = toml::parse<toml::discard_comments, std::map, std::deque>(
|
||||
"tmp1.toml");
|
||||
"tmp1_map_dq.toml");
|
||||
{
|
||||
auto& owner = toml::find(serialized, "owner");
|
||||
auto& bio = toml::find<std::string>(owner, "bio");
|
||||
@@ -80,17 +113,46 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example_with_comment_nocomment)
|
||||
{
|
||||
{
|
||||
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp1_com_nocomment.toml");
|
||||
ofs << std::setw(80) << toml::nocomment << data;
|
||||
}
|
||||
const auto serialized = toml::parse<toml::preserve_comments>("tmp1_com_nocomment.toml");
|
||||
// check no comment exist
|
||||
BOOST_TEST(!has_comment_inside(serialized));
|
||||
}
|
||||
{
|
||||
const auto data_nocomment = toml::parse("toml/tests/example.toml");
|
||||
auto serialized = toml::parse("tmp1_com_nocomment.toml");
|
||||
{
|
||||
auto& owner = toml::find(serialized, "owner");
|
||||
auto& bio = toml::find<std::string>(owner, "bio");
|
||||
const auto CR = std::find(bio.begin(), bio.end(), '\r');
|
||||
if(CR != bio.end())
|
||||
{
|
||||
bio.erase(CR);
|
||||
}
|
||||
}
|
||||
// check collectly serialized
|
||||
BOOST_TEST(data_nocomment == serialized);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq)
|
||||
{
|
||||
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(
|
||||
"toml/tests/example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp1_com.toml");
|
||||
std::ofstream ofs("tmp1_com_map_dq.toml");
|
||||
ofs << std::setw(80) << data;
|
||||
}
|
||||
|
||||
auto serialized = toml::parse<toml::preserve_comments, std::map, std::deque>(
|
||||
"tmp1_com.toml");
|
||||
"tmp1_com_map_dq.toml");
|
||||
{
|
||||
auto& owner = toml::find(serialized, "owner");
|
||||
auto& bio = toml::find<std::string>(owner, "bio");
|
||||
@@ -102,11 +164,38 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq)
|
||||
}
|
||||
BOOST_TEST(data == serialized);
|
||||
{
|
||||
std::ofstream ofs("tmp1_com1.toml");
|
||||
std::ofstream ofs("tmp1_com1_map_dq.toml");
|
||||
ofs << std::setw(80) << serialized;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq_nocomment)
|
||||
{
|
||||
{
|
||||
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>("toml/tests/example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp1_com_map_dq_nocomment.toml");
|
||||
ofs << std::setw(80) << toml::nocomment << data;
|
||||
}
|
||||
const auto serialized = toml::parse<toml::preserve_comments, std::map, std::deque>("tmp1_com_map_dq_nocomment.toml");
|
||||
BOOST_TEST(!has_comment_inside(serialized));
|
||||
}
|
||||
{
|
||||
const auto data_nocomment = toml::parse("toml/tests/example.toml");
|
||||
auto serialized = toml::parse("tmp1_com_map_dq_nocomment.toml");
|
||||
{
|
||||
auto& owner = toml::find(serialized, "owner");
|
||||
auto& bio = toml::find<std::string>(owner, "bio");
|
||||
const auto CR = std::find(bio.begin(), bio.end(), '\r');
|
||||
if(CR != bio.end())
|
||||
{
|
||||
bio.erase(CR);
|
||||
}
|
||||
}
|
||||
BOOST_TEST(data_nocomment == serialized);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_fruit)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/fruit.toml");
|
||||
@@ -194,3 +283,23 @@ BOOST_AUTO_TEST_CASE(test_hard_example_with_comment)
|
||||
}
|
||||
BOOST_TEST(data == serialized);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_format_key)
|
||||
{
|
||||
{
|
||||
const toml::key key("normal_bare-key");
|
||||
BOOST_TEST("normal_bare-key" == toml::format_key(key));
|
||||
}
|
||||
{
|
||||
const toml::key key("key.include.dots");
|
||||
BOOST_TEST("\"key.include.dots\"" == toml::format_key(key));
|
||||
}
|
||||
{
|
||||
const toml::key key("key-include-unicode-\xE3\x81\x82");
|
||||
BOOST_TEST("\"key-include-unicode-\xE3\x81\x82\"" == toml::format_key(key));
|
||||
}
|
||||
{
|
||||
const toml::key key("special-chars-\\-\"-\b-\f-\r-\n-\t");
|
||||
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key));
|
||||
}
|
||||
}
|
||||
|
||||
113
tests/test_string.cpp
Normal file
113
tests/test_string.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#define BOOST_TEST_MODULE "test_string"
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <toml.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_basic_string)
|
||||
{
|
||||
{
|
||||
const toml::string str("basic string");
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "\"basic string\"");
|
||||
}
|
||||
{
|
||||
const std::string s1 ("basic string");
|
||||
const toml::string str(s1);
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "\"basic string\"");
|
||||
}
|
||||
{
|
||||
const toml::string str("basic string", toml::string_t::basic);
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "\"basic string\"");
|
||||
}
|
||||
{
|
||||
const std::string s1 ("basic string");
|
||||
const toml::string str(s1, toml::string_t::basic);
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "\"basic string\"");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_basic_ml_string)
|
||||
{
|
||||
{
|
||||
const toml::string str("basic\nstring");
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\"";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
}
|
||||
{
|
||||
const std::string s1 ("basic\nstring");
|
||||
const toml::string str(s1);
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\"";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
}
|
||||
{
|
||||
const toml::string str("basic\nstring", toml::string_t::basic);
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\"";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
|
||||
}
|
||||
{
|
||||
const std::string s1 ("basic\nstring");
|
||||
const toml::string str(s1, toml::string_t::basic);
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\"";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_literal_string)
|
||||
{
|
||||
{
|
||||
const toml::string str("literal string", toml::string_t::literal);
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "'literal string'");
|
||||
}
|
||||
{
|
||||
const std::string s1 ("literal string");
|
||||
const toml::string str(s1, toml::string_t::literal);
|
||||
std::ostringstream oss;
|
||||
oss << str;
|
||||
BOOST_TEST(oss.str() == "'literal string'");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_literal_ml_string)
|
||||
{
|
||||
{
|
||||
const toml::string str("literal\nstring", toml::string_t::literal);
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "'''\nliteral\nstring'''";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
|
||||
}
|
||||
{
|
||||
const std::string s1 ("literal\nstring");
|
||||
const toml::string str(s1, toml::string_t::literal);
|
||||
std::ostringstream oss1;
|
||||
oss1 << str;
|
||||
std::ostringstream oss2;
|
||||
oss2 << "'''\nliteral\nstring'''";
|
||||
BOOST_TEST(oss1.str() == oss2.str());
|
||||
}
|
||||
}
|
||||
|
||||
1
toml.hpp
1
toml.hpp
@@ -37,5 +37,6 @@
|
||||
#include "toml/literal.hpp"
|
||||
#include "toml/serializer.hpp"
|
||||
#include "toml/get.hpp"
|
||||
#include "toml/find.hpp"
|
||||
|
||||
#endif// TOML_FOR_MODERN_CPP
|
||||
|
||||
@@ -162,12 +162,15 @@ struct preserve_comments
|
||||
const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
|
||||
const_reverse_iterator crend() const noexcept {return comments.crend();}
|
||||
|
||||
friend bool operator==(const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator< (const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator> (const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs);
|
||||
friend bool operator==(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator!=(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator< (const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator<=(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator> (const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator>=(const preserve_comments&, const preserve_comments&);
|
||||
|
||||
friend void swap(preserve_comments&, std::vector<std::string>&);
|
||||
friend void swap(std::vector<std::string>&, preserve_comments&);
|
||||
|
||||
private:
|
||||
|
||||
@@ -186,6 +189,27 @@ inline void swap(preserve_comments& lhs, preserve_comments& rhs)
|
||||
lhs.swap(rhs);
|
||||
return;
|
||||
}
|
||||
inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
|
||||
{
|
||||
lhs.comments.swap(rhs);
|
||||
return;
|
||||
}
|
||||
inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
|
||||
{
|
||||
lhs.swap(rhs.comments);
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
|
||||
{
|
||||
for(const auto& c : com)
|
||||
{
|
||||
os << '#' << c << '\n';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
@@ -381,5 +405,12 @@ inline bool operator>=(const discard_comments&, const discard_comments&) noexcep
|
||||
|
||||
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
} // toml11
|
||||
#endif// TOML11_COMMENTS_HPP
|
||||
|
||||
786
toml/find.hpp
Normal file
786
toml/find.hpp
Normal file
@@ -0,0 +1,786 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_FIND_HPP
|
||||
#define TOML11_FIND_HPP
|
||||
#include "get.hpp"
|
||||
#include <numeric>
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// these overloads do not require to set T. and returns value itself.
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
const auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>&& find(basic_value<C, M, V>&& v, const key& ky)
|
||||
{
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return std::move(tab.at(ky));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// find<T>(value, key);
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
|
||||
find(const basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
|
||||
find(basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
|
||||
find(basic_value<C, M, V>&& v, const key& ky)
|
||||
{
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(std::move(tab.at(ky)));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// toml::find(toml::value, toml::key, Ts&& ... keys)
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, const basic_value<C, M, V>&>
|
||||
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, basic_value<C, M, V>&>
|
||||
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, basic_value<C, M, V>&&>
|
||||
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, decltype(get<T>(std::declval<const basic_value<C, M, V>&>()))>
|
||||
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&>()))>
|
||||
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
|
||||
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&&>()))>
|
||||
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// find_or(value, key, fallback)
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V> const&
|
||||
find_or(const basic_value<C, M, V>& v, const key& ky,
|
||||
const basic_value<C, M, V>& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return tab.at(ky);
|
||||
}
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>&
|
||||
find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return tab[ky];
|
||||
}
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>
|
||||
find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::move(opt);}
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0) {return std::move(opt);}
|
||||
return std::move(tab[ky]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// exact types (return type can be a reference)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
|
||||
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
|
||||
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab[ky], opt);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
|
||||
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::forward<T>(opt);}
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
|
||||
return get_or(std::move(tab[ky]), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// std::string (return type can be a reference)
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
|
||||
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
|
||||
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
|
||||
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::forward<T>(opt);}
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
|
||||
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// string literal (deduced as std::string)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
|
||||
std::string>
|
||||
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::string(opt);}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return std::string(opt);}
|
||||
return get_or(tab.at(ky), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// others (require type conversion and return type cannot be lvalue reference)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
// T is not an exact toml type
|
||||
detail::negation<detail::is_exact_toml_type<
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
|
||||
basic_value<C, M, V>>>,
|
||||
// T is not std::string
|
||||
detail::negation<std::is_same<std::string,
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
|
||||
// T is not a string literal
|
||||
detail::negation<detail::is_string_literal<
|
||||
typename std::remove_reference<T>::type>>
|
||||
>::value, typename std::remove_cv<typename std::remove_reference<T>::type>::type>
|
||||
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::forward<T>(opt);}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
|
||||
return get_or(tab.at(ky), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// expect
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(get<T>(v));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
result<T, std::string>
|
||||
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(find<T>(v, k));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
template<typename T, typename Table>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::is_map<Table>, detail::is_basic_value<typename Table::mapped_type>
|
||||
>::value, result<T, std::string>>
|
||||
expect(const Table& t, const toml::key& k,
|
||||
std::string tablename = "unknown table") noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(find<T>(t, k, std::move(tablename)));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// find_fuzzy
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// default fuzzy matcher; levenstein distance (all cost is 1)
|
||||
|
||||
struct levenstein_matcher
|
||||
{
|
||||
levenstein_matcher(): tolerance(1) {}
|
||||
levenstein_matcher(const std::uint32_t tol): tolerance(tol) {}
|
||||
~levenstein_matcher() = default;
|
||||
levenstein_matcher(levenstein_matcher const&) = default;
|
||||
levenstein_matcher(levenstein_matcher &&) = default;
|
||||
levenstein_matcher& operator=(levenstein_matcher const&) = default;
|
||||
levenstein_matcher& operator=(levenstein_matcher &&) = default;
|
||||
|
||||
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
|
||||
bool operator()(const std::basic_string<charT, traitsT, Alloc1>& lhs,
|
||||
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
|
||||
{
|
||||
return this->distance(lhs, rhs) <= this->tolerance;
|
||||
}
|
||||
|
||||
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
|
||||
std::uint32_t distance(
|
||||
const std::basic_string<charT, traitsT, Alloc1>& lhs,
|
||||
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
|
||||
{
|
||||
// force `lhs.size() <= rhs.size()`
|
||||
if(lhs.size() > rhs.size()) {return this->distance(rhs, lhs);}
|
||||
|
||||
std::vector<std::uint32_t> matrix(lhs.size() + 1u);
|
||||
std::iota(matrix.begin(), matrix.end(), 0);
|
||||
|
||||
for(const charT r : rhs)
|
||||
{
|
||||
std::uint32_t prev_diag = matrix.front();
|
||||
matrix.front() += 1;
|
||||
|
||||
for(std::size_t i=0; i<lhs.size(); ++i)
|
||||
{
|
||||
const charT l = lhs[i];
|
||||
if(traitsT::eq(l, r))
|
||||
{
|
||||
std::swap(matrix[i+1], prev_diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto tmp = matrix[i+1];
|
||||
matrix[i+1] = std::min(prev_diag, std::min(matrix[i], matrix[i+1])) + 1;
|
||||
prev_diag = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix.back();
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint32_t tolerance;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// toml::find_fuzzy<T>(v, "tablename", FuzzyMatcher);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename Iterator, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
Iterator find_unique(
|
||||
Iterator iter, const Iterator end, const basic_value<C, M, V>& v,
|
||||
const toml::key& k, const FuzzyMatcher& match)
|
||||
{
|
||||
Iterator found = end;
|
||||
for(; iter != end; ++iter)
|
||||
{
|
||||
if(match(iter->first, k))
|
||||
{
|
||||
if(found != end)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(
|
||||
concat_to_string("[error] key \"", k, "\" not found."),
|
||||
{
|
||||
{std::addressof(detail::get_region(v)),"in this table"},
|
||||
{std::addressof(detail::get_region(found->second)),
|
||||
"did you mean this here?"},
|
||||
{std::addressof(detail::get_region(iter->second)),
|
||||
"or this?"}
|
||||
}));
|
||||
}
|
||||
found = iter;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
} // detail
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
auto find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
-> decltype(find<T>(std::declval<const basic_value<C, M, V>&>(), ky))
|
||||
{
|
||||
try
|
||||
{
|
||||
return find<T>(v, ky);
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
const auto& t = v.as_table();
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return get<T>(found->second);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
auto find_fuzzy(basic_value<C, M, V>& v, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
-> decltype(find<T>(std::declval<basic_value<C, M, V>&>(), ky))
|
||||
{
|
||||
try
|
||||
{
|
||||
return find<T>(v, ky);
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
auto& t = v.as_table();
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return get<T>(found->second);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
auto find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
-> decltype(find<T>(std::declval<basic_value<C, M, V>&&>(), ky))
|
||||
{
|
||||
basic_value<C, M, V> v = v_; // to re-use later, store it once
|
||||
try
|
||||
{
|
||||
return std::move(find<T>(v, ky)); // pass lref, move later
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
auto& t = v.as_table(); // because v is used here
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return get<T>(std::move(found->second));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// no-template-argument case (by default, return toml::value).
|
||||
// toml::find_fuzzy(v, "tablename", FuzzyMatcher);
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
basic_value<C, M, V> const&
|
||||
find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
{
|
||||
try
|
||||
{
|
||||
return find(v, ky);
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
const auto& t = v.as_table();
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
basic_value<C, M, V>&
|
||||
find_fuzzy(basic_value<C, M, V>& v, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
{
|
||||
try
|
||||
{
|
||||
return find(v, ky);
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
auto& t = v.as_table();
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher = levenstein_matcher>
|
||||
basic_value<C, M, V>&&
|
||||
find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
|
||||
const FuzzyMatcher match = levenstein_matcher(1))
|
||||
{
|
||||
basic_value<C, M, V> v = v_; // to re-use later, store it once
|
||||
try
|
||||
{
|
||||
return std::move(find(v, ky));
|
||||
}
|
||||
catch(const std::out_of_range& oor)
|
||||
{
|
||||
auto& t = v.as_table();
|
||||
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
|
||||
if(found != t.end())
|
||||
{
|
||||
return std::move(found->second);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// find(v, k, matcher)
|
||||
//
|
||||
// when matcher is passed, check a key that matches exists or not. if it exists,
|
||||
// suggest that in the error message
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
basic_value<C, M, V> const&
|
||||
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
const auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
basic_value<C, M, V>&
|
||||
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
basic_value<C, M, V>&&
|
||||
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return std::move(tab.at(ky));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// find<T>(value, key, fuzzy_matcher);
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
detail::enable_if_t<
|
||||
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))>
|
||||
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this here?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
detail::enable_if_t<
|
||||
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))>
|
||||
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this here?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename FuzzyMatcher>
|
||||
detail::enable_if_t<
|
||||
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))>
|
||||
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
|
||||
{
|
||||
auto tab = v.as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
for(const auto& kv : tab)
|
||||
{
|
||||
if(match(kv.first, ky))
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found."), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"},
|
||||
{std::addressof(detail::get_region(kv.second)),
|
||||
"did you mean this here?"}
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(std::move(tab.at(ky)));
|
||||
}
|
||||
|
||||
} // toml
|
||||
#endif// TOML11_FIND_HPP
|
||||
331
toml/get.hpp
331
toml/get.hpp
@@ -420,161 +420,31 @@ T get(const basic_value<C, M, V>& v)
|
||||
return ::toml::from<T>::from_toml(v);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// find and get
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// these overloads do not require to set T. and returns value itself.
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
const auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return tab.at(ky);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>&& find(basic_value<C, M, V>&& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return std::move(tab.at(ky));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// find<T>(value, key);
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
|
||||
find(const basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
const auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
|
||||
find(basic_value<C, M, V>& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(tab.at(ky));
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
|
||||
find(basic_value<C, M, V>&& v, const key& ky)
|
||||
{
|
||||
auto& tab = v.template cast<value_t::table>();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"[error] key \"", ky, "\" not found"), {
|
||||
{std::addressof(detail::get_region(v)), "in this table"}
|
||||
}));
|
||||
}
|
||||
return ::toml::get<T>(std::move(tab.at(ky)));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// toml::find(toml::value, toml::key, Ts&& ... keys)
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
const basic_value<C, M, V>&
|
||||
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
basic_value<C, M, V>&
|
||||
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
basic_value<C, M, V>&&
|
||||
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
|
||||
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
|
||||
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
typename ... Ts>
|
||||
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
|
||||
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
|
||||
{
|
||||
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// get_or(value, fallback)
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V> const&
|
||||
get_or(const basic_value<C, M, V>& v, const basic_value<C, M, V>&)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>&
|
||||
get_or(basic_value<C, M, V>& v, basic_value<C, M, V>&)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
basic_value<C, M, V>
|
||||
get_or(basic_value<C, M, V>&& v, basic_value<C, M, V>&&)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// specialization for the exact toml types (return type becomes lvalue ref)
|
||||
|
||||
@@ -723,162 +593,5 @@ get_or(const basic_value<C, M, V>& v, T&& opt)
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// find_or(value, key, fallback)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// exact types (return type can be a reference)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
|
||||
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
|
||||
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab[ky], opt);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
|
||||
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(std::move(tab[ky]), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// std::string (return type can be a reference)
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
|
||||
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
|
||||
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), opt);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
|
||||
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return std::forward<T>(opt);}
|
||||
auto tab = std::move(v).as_table();
|
||||
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
|
||||
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// string literal (deduced as std::string)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<
|
||||
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
|
||||
std::string>
|
||||
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return std::string(opt);}
|
||||
return get_or(tab.at(ky), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// others (require type conversion and return type cannot be lvalue reference)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::negation<detail::is_exact_toml_type<
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
|
||||
basic_value<C, M, V>>>,
|
||||
detail::negation<std::is_same<std::string,
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
|
||||
detail::negation<detail::is_string_literal<
|
||||
typename std::remove_reference<T>::type>>
|
||||
>::value, T>
|
||||
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
|
||||
{
|
||||
if(!v.is_table()) {return opt;}
|
||||
const auto& tab = v.as_table();
|
||||
if(tab.count(ky) == 0) {return opt;}
|
||||
return get_or(tab.at(ky), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// expect
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(get<T>(v));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
result<T, std::string>
|
||||
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(find<T>(v, k));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
template<typename T, typename Table>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::is_map<Table>, detail::is_basic_value<typename Table::mapped_type>
|
||||
>::value, result<T, std::string>>
|
||||
expect(const Table& t, const toml::key& k,
|
||||
std::string tablename = "unknown table") noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return ok(find<T>(t, k, std::move(tablename)));
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return err(e.what());
|
||||
}
|
||||
}
|
||||
} // toml
|
||||
#endif// TOML11_GET
|
||||
|
||||
@@ -115,7 +115,8 @@ using lex_local_time = lex_partial_time;
|
||||
// ===========================================================================
|
||||
|
||||
using lex_quotation_mark = character<'"'>;
|
||||
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x1F>,
|
||||
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
|
||||
in_range<0x0a, 0x1F>, // is allowed
|
||||
character<0x22>, character<0x5C>,
|
||||
character<0x7F>>>;
|
||||
using lex_escape = character<'\\'>;
|
||||
@@ -137,7 +138,8 @@ using lex_basic_string = sequence<lex_quotation_mark,
|
||||
lex_quotation_mark>;
|
||||
|
||||
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
|
||||
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x1F>,
|
||||
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
|
||||
in_range<0x0a, 0x1F>, // is tab
|
||||
character<0x5C>,
|
||||
character<0x7F>,
|
||||
lex_ml_basic_string_delim>>;
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include "lexer.hpp"
|
||||
#include "types.hpp"
|
||||
#include "value.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
namespace toml
|
||||
@@ -1750,6 +1751,7 @@ parse_ml_table(location<Container>& loc)
|
||||
using skip_line = repeat<
|
||||
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>, at_least<1>>;
|
||||
skip_line::invoke(loc);
|
||||
lex_ws::invoke(loc);
|
||||
|
||||
table_type tab;
|
||||
while(loc.iter() != loc.end())
|
||||
@@ -1973,7 +1975,7 @@ parse(std::istream& is, const std::string& fname = "unknown file")
|
||||
template<typename Comment = ::toml::discard_comments,
|
||||
template<typename ...> class Table = std::unordered_map,
|
||||
template<typename ...> class Array = std::vector>
|
||||
inline basic_value<Comment, Table, Array> parse(const std::string& fname)
|
||||
basic_value<Comment, Table, Array> parse(const std::string& fname)
|
||||
{
|
||||
std::ifstream ifs(fname.c_str(), std::ios_base::binary);
|
||||
if(!ifs.good())
|
||||
|
||||
@@ -10,6 +10,45 @@
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// This function serialize a key. It checks a string is a bare key and
|
||||
// escapes special characters if the string is not compatible to a bare key.
|
||||
// ```cpp
|
||||
// std::string k("non.bare.key"); // the key itself includes `.`s.
|
||||
// std::string formatted = toml::format_key(k);
|
||||
// assert(formatted == "\"non.bare.key\"");
|
||||
// ```
|
||||
//
|
||||
// This function is exposed to make it easy to write a user-defined serializer.
|
||||
// Since toml restricts characters available in a bare key, generally a string
|
||||
// should be escaped. But checking whether a string needs to be surrounded by
|
||||
// a `"` and escaping some special character is boring.
|
||||
inline std::string format_key(const toml::key& key)
|
||||
{
|
||||
detail::location<toml::key> loc(key, key);
|
||||
detail::lex_unquoted_key::invoke(loc);
|
||||
if(loc.iter() == loc.end())
|
||||
{
|
||||
return key; // all the tokens are consumed. the key is unquoted-key.
|
||||
}
|
||||
std::string token("\"");
|
||||
for(const char c : key)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {token += "\\\\"; break;}
|
||||
case '\"': {token += "\\\""; break;}
|
||||
case '\b': {token += "\\b"; break;}
|
||||
case '\t': {token += "\\t"; break;}
|
||||
case '\f': {token += "\\f"; break;}
|
||||
case '\n': {token += "\\n"; break;}
|
||||
case '\r': {token += "\\r"; break;}
|
||||
default : {token += c; break;}
|
||||
}
|
||||
}
|
||||
token += "\"";
|
||||
return token;
|
||||
}
|
||||
|
||||
template<typename Comment,
|
||||
template<typename ...> class Table,
|
||||
template<typename ...> class Array>
|
||||
@@ -32,9 +71,10 @@ struct serializer
|
||||
serializer(const std::size_t w = 80u,
|
||||
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
|
||||
const bool can_be_inlined = false,
|
||||
const bool no_comment = false,
|
||||
std::vector<toml::key> ks = {})
|
||||
: can_be_inlined_(can_be_inlined), float_prec_(float_prec), width_(w),
|
||||
keys_(std::move(ks))
|
||||
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
|
||||
float_prec_(float_prec), width_(w), keys_(std::move(ks))
|
||||
{}
|
||||
~serializer() = default;
|
||||
|
||||
@@ -217,12 +257,15 @@ struct serializer
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
if(!no_comment_)
|
||||
{
|
||||
for(const auto& c : item.comments())
|
||||
{
|
||||
token += '#';
|
||||
token += c;
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
const auto t = this->make_inline_table(item.as_table());
|
||||
|
||||
@@ -245,6 +288,8 @@ struct serializer
|
||||
|
||||
std::string token;
|
||||
for(const auto& item : v)
|
||||
{
|
||||
if(!no_comment_)
|
||||
{
|
||||
for(const auto& c : item.comments())
|
||||
{
|
||||
@@ -252,6 +297,7 @@ struct serializer
|
||||
token += c;
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
token += "[[";
|
||||
token += this->serialize_dotted_key(keys_);
|
||||
token += "]]\n";
|
||||
@@ -287,7 +333,7 @@ struct serializer
|
||||
token += "[\n";
|
||||
for(const auto& item : v)
|
||||
{
|
||||
if(!item.comments().empty())
|
||||
if(!item.comments().empty() && !no_comment_)
|
||||
{
|
||||
// if comment exists, the element must be the only element in the line.
|
||||
// e.g. the following is not allowed.
|
||||
@@ -369,7 +415,8 @@ struct serializer
|
||||
token += " = ";
|
||||
}
|
||||
token += this->make_inline_table(v);
|
||||
if(token.size() < this->width_)
|
||||
if(token.size() < this->width_ &&
|
||||
token.end() == std::find(token.begin(), token.end(), '\n'))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
@@ -390,16 +437,7 @@ struct serializer
|
||||
|
||||
std::string serialize_key(const toml::key& key) const
|
||||
{
|
||||
detail::location<toml::key> loc(key, key);
|
||||
detail::lex_unquoted_key::invoke(loc);
|
||||
if(loc.iter() == loc.end())
|
||||
{
|
||||
return key; // all the tokens are consumed. the key is unquoted-key.
|
||||
}
|
||||
std::string token("\"");
|
||||
token += this->escape_basic_string(key);
|
||||
token += "\"";
|
||||
return token;
|
||||
return ::toml::format_key(key);
|
||||
}
|
||||
|
||||
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
|
||||
@@ -472,6 +510,9 @@ struct serializer
|
||||
// if an element of a table or an array has a comment, it cannot be inlined.
|
||||
bool has_comment_inside(const array_type& a) const noexcept
|
||||
{
|
||||
// if no_comment is set, comments would not be written.
|
||||
if(this->no_comment_) {return false;}
|
||||
|
||||
for(const auto& v : a)
|
||||
{
|
||||
if(!v.comments().empty()) {return true;}
|
||||
@@ -480,6 +521,9 @@ struct serializer
|
||||
}
|
||||
bool has_comment_inside(const table_type& t) const noexcept
|
||||
{
|
||||
// if no_comment is set, comments would not be written.
|
||||
if(this->no_comment_) {return false;}
|
||||
|
||||
for(const auto& kv : t)
|
||||
{
|
||||
if(!kv.second.comments().empty()) {return true;}
|
||||
@@ -536,7 +580,7 @@ struct serializer
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!kv.second.comments().empty())
|
||||
if(!kv.second.comments().empty() && !no_comment_)
|
||||
{
|
||||
for(const auto& c : kv.second.comments())
|
||||
{
|
||||
@@ -575,8 +619,8 @@ struct serializer
|
||||
std::vector<toml::key> ks(this->keys_);
|
||||
ks.push_back(kv.first);
|
||||
|
||||
auto tmp = visit(serializer(
|
||||
this->width_, this->float_prec_, !multiline_table_printed, ks),
|
||||
auto tmp = visit(serializer(this->width_, this->float_prec_,
|
||||
!multiline_table_printed, this->no_comment_, ks),
|
||||
kv.second);
|
||||
|
||||
if((!multiline_table_printed) &&
|
||||
@@ -590,7 +634,7 @@ struct serializer
|
||||
tmp += '\n';
|
||||
}
|
||||
|
||||
if(!kv.second.comments().empty())
|
||||
if(!kv.second.comments().empty() && !no_comment_)
|
||||
{
|
||||
for(const auto& c : kv.second.comments())
|
||||
{
|
||||
@@ -614,6 +658,7 @@ struct serializer
|
||||
private:
|
||||
|
||||
bool can_be_inlined_;
|
||||
bool no_comment_;
|
||||
int float_prec_;
|
||||
std::size_t width_;
|
||||
std::vector<toml::key> keys_;
|
||||
@@ -624,7 +669,7 @@ template<typename C,
|
||||
std::string
|
||||
format(const basic_value<C, M, V>& v, std::size_t w = 80u,
|
||||
int fprec = std::numeric_limits<toml::floating>::max_digits10,
|
||||
bool force_inline = false)
|
||||
bool no_comment = false, bool force_inline = false)
|
||||
{
|
||||
// if value is a table, it is considered to be a root object.
|
||||
// the root object can't be an inline table.
|
||||
@@ -633,18 +678,43 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
|
||||
std::ostringstream oss;
|
||||
if(!v.comments().empty())
|
||||
{
|
||||
for(const auto& c : v.comments())
|
||||
{
|
||||
oss << '#' << c << '\n';
|
||||
oss << v.comments();
|
||||
oss << '\n'; // to split the file comment from the first element
|
||||
}
|
||||
oss << '\n';
|
||||
}
|
||||
oss << visit(serializer<C, M, V>(w, fprec, false), v);
|
||||
oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v);
|
||||
return oss.str();
|
||||
}
|
||||
return visit(serializer<C, M, V>(w, fprec, force_inline), v);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename charT, typename traits>
|
||||
int comment_index(std::basic_ostream<charT, traits>&)
|
||||
{
|
||||
static const int index = std::ios_base::xalloc();
|
||||
return index;
|
||||
}
|
||||
} // detail
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
nocomment(std::basic_ostream<charT, traits>& os)
|
||||
{
|
||||
// by default, it is zero. and by defalut, it shows comments.
|
||||
os.iword(detail::comment_index(os)) = 1;
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
showcomment(std::basic_ostream<charT, traits>& os)
|
||||
{
|
||||
// by default, it is zero. and by defalut, it shows comments.
|
||||
os.iword(detail::comment_index(os)) = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename charT, typename traits, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
std::basic_ostream<charT, traits>&
|
||||
@@ -655,16 +725,40 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
|
||||
const int fprec = static_cast<int>(os.precision());
|
||||
os.width(0);
|
||||
|
||||
if(!v.comments().empty())
|
||||
// by defualt, iword is initialized byl 0. And by default, toml11 outputs
|
||||
// comments. So `0` means showcomment. 1 means nocommnet.
|
||||
const bool no_comment = (1 == os.iword(detail::comment_index(os)));
|
||||
|
||||
if(!no_comment && v.is_table() && !v.comments().empty())
|
||||
{
|
||||
for(const auto& c : v.comments())
|
||||
{
|
||||
os << '#' << c << '\n';
|
||||
}
|
||||
os << '\n';
|
||||
os << v.comments();
|
||||
os << '\n'; // to split the file comment from the first element
|
||||
}
|
||||
// the root object can't be an inline table. so pass `false`.
|
||||
os << visit(serializer<C, M, V>(w, fprec, false), v);
|
||||
os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v);
|
||||
|
||||
// if v is a non-table value, and has only one comment, then
|
||||
// put a comment just after a value. in the following way.
|
||||
//
|
||||
// ```toml
|
||||
// key = "value" # comment.
|
||||
// ```
|
||||
//
|
||||
// Since the top-level toml object is a table, one who want to put a
|
||||
// non-table toml value must use this in a following way.
|
||||
//
|
||||
// ```cpp
|
||||
// toml::value v;
|
||||
// std::cout << "user-defined-key = " << v << std::endl;
|
||||
// ```
|
||||
//
|
||||
// In this case, it is impossible to put comments before key-value pair.
|
||||
// The only way to preserve comments is to put all of them after a value.
|
||||
if(!no_comment && !v.is_table() && !v.comments().empty())
|
||||
{
|
||||
os << " #";
|
||||
for(const auto& c : v.comments()) {os << c;}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,9 +139,74 @@ operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.s
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const string& str)
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const string& s)
|
||||
{
|
||||
os << str.str;
|
||||
if(s.kind == string_t::basic)
|
||||
{
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
|
||||
{
|
||||
// it contains newline. make it multiline string.
|
||||
os << "\"\"\"\n";
|
||||
for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i)
|
||||
{
|
||||
switch(*i)
|
||||
{
|
||||
case '\\': {os << "\\\\"; break;}
|
||||
case '\"': {os << "\\\""; break;}
|
||||
case '\b': {os << "\\b"; break;}
|
||||
case '\t': {os << "\\t"; break;}
|
||||
case '\f': {os << "\\f"; break;}
|
||||
case '\n': {os << '\n'; break;}
|
||||
case '\r':
|
||||
{
|
||||
// since it is a multiline string,
|
||||
// CRLF is not needed to be escaped.
|
||||
if(std::next(i) != e && *std::next(i) == '\n')
|
||||
{
|
||||
os << "\r\n";
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "\\r";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {os << *i; break;}
|
||||
}
|
||||
}
|
||||
os << "\\\n\"\"\"";
|
||||
return os;
|
||||
}
|
||||
// no newline. make it inline.
|
||||
os << "\"";
|
||||
for(const auto c : s.str)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {os << "\\\\"; break;}
|
||||
case '\"': {os << "\\\""; break;}
|
||||
case '\b': {os << "\\b"; break;}
|
||||
case '\t': {os << "\\t"; break;}
|
||||
case '\f': {os << "\\f"; break;}
|
||||
case '\n': {os << "\\n"; break;}
|
||||
case '\r': {os << "\\r"; break;}
|
||||
default : {os << c; break;}
|
||||
}
|
||||
}
|
||||
os << "\"";
|
||||
return os;
|
||||
}
|
||||
// the string `s` is literal-string.
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
|
||||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
|
||||
{
|
||||
// contains newline or single quote. make it multiline.
|
||||
os << "'''\n" << s.str << "'''";
|
||||
return os;
|
||||
}
|
||||
// normal literal string
|
||||
os << '\'' << s.str << '\'';
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -1569,7 +1569,7 @@ class basic_value
|
||||
|
||||
source_location location() const
|
||||
{
|
||||
return source_location(this->region_info_);
|
||||
return source_location(this->region_info_.get());
|
||||
}
|
||||
|
||||
comment_type const& comments() const noexcept {return this->comments_;}
|
||||
|
||||
Reference in New Issue
Block a user