Compare commits

...

34 Commits

Author SHA1 Message Date
ToruNiina
0df1dd6fea refactor: remove extraneous whitespaces 2024-07-04 02:57:22 +09:00
ToruNiina
a9fb557d70 feat: add error_kind to error_infos 2024-07-04 02:56:52 +09:00
ToruNiina
746abd73db feat: enable to get error_kind from error_info
and add serialization_error for future update
2024-07-04 02:55:47 +09:00
ToruNiina
7a6cb02293 feat: add error_kind to distinguish error_info 2024-07-04 00:57:50 +09:00
ToruNiina
00d6124f59 Merge branch 'v4_1_0' into try-get 2024-07-03 01:59:52 +09:00
ToruNiina
3a14cb5083 Merge branch 'main' into v4_1_0 2024-07-03 01:59:11 +09:00
ToruNiina
edbea2f483 Merge branch 'add-test-case' 2024-07-03 01:58:17 +09:00
ToruNiina
973ecee32d fix: add missing include file 2024-07-03 00:24:45 +09:00
ToruNiina
b9b2ee02ff feat [skip ci]: update single_include 2024-07-02 15:23:57 +00:00
ToruNiina
d77caa94d0 chore: Merge 'fix-make-error-info' 2024-07-03 00:22:55 +09:00
ToruNiina
65e722da43 test: add minimum cases of serializer 2024-07-03 00:00:50 +09:00
ToruNiina
37d0391b9d test: add test to make error_info
check if it compiles
2024-07-02 23:35:16 +09:00
ToruNiina
d4742334ce fix: add detail::make_error_info_rec overload
that converts basic_value to location
2024-07-02 23:34:49 +09:00
ToruNiina
ccd941dc5b fix: remove default arg from fwd decl 2024-07-02 00:03:10 +09:00
ToruNiina
fc493afb4e chore: update version 4.0.2 2024-06-30 23:59:23 +09:00
ToruNiina
1d3abc9718 ci: remove verbose message from ci build 2024-06-30 17:40:40 +09:00
ToruNiina
9f59c591f0 feat [skip ci]: update single_include 2024-06-29 15:56:19 +00:00
ToruNiina
de092e5457 fix: cast fsize to avoid sign-conv 2024-06-30 00:55:45 +09:00
ToruNiina
bcd8a6d1e4 doc: update notice about open mode
in overload of toml::parse that takes file stream or FILE* itself
2024-06-30 00:47:44 +09:00
ToruNiina
5b0ea5e95c fix #249: make sure all the file content is read 2024-06-30 00:29:34 +09:00
ToruNiina
d00c0c1b15 doc: explain a macro to link pre-built lib 2024-06-28 23:52:58 +09:00
ToruNiina
dc562ae5cb refactor: remove name of unused arg in func 2024-06-28 00:48:20 +09:00
ToruNiina
83f37a18b1 fix: incorrect function argument 2024-06-28 00:48:06 +09:00
ToruNiina
dc7e443be4 test: add try_get/find 2024-06-28 00:47:37 +09:00
ToruNiina
59921c1eb7 feat: add try_find 2024-06-28 00:19:00 +09:00
ToruNiina
a4d0189df3 refactor: move key_cast to utility 2024-06-28 00:18:46 +09:00
ToruNiina
3320d25abb feat: add try_get 2024-06-28 00:13:07 +09:00
ToruNiina
8efb305e8b feat: add try_from to return result<T, E> 2024-06-28 00:09:29 +09:00
ToruNiina
2baa47ef3d Merge branch 'main' into v4_1_0 2024-06-28 00:08:44 +09:00
ToruNiina
5d23f4b8a8 feat [skip ci]: update single_include 2024-06-27 15:08:22 +00:00
ToruNiina
42734ea642 fix: typo in type-trait impl 2024-06-28 00:07:26 +09:00
ToruNiina
8596419e24 feat [skip ci]: update single_include 2024-06-26 15:31:25 +00:00
ToruNiina
932acba7f1 fix #250: bump patch version macro in version.hpp 2024-06-27 00:30:38 +09:00
ToruNiina
b2a93eb267 chore: auto extract version string in cmake 2024-06-27 00:28:40 +09:00
28 changed files with 2720 additions and 244 deletions

View File

@@ -53,7 +53,7 @@ jobs:
sudo apt-get install clang-${{ matrix.compiler }}
- name: Configure
run: |
cmake -B build/ -DCMAKE_VERBOSE_MAKEFILE=1 -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }}
cmake -B build/ -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }}
- name: Build
run: |
cmake --build build/

View File

@@ -1,5 +1,18 @@
cmake_minimum_required(VERSION 3.16)
project(toml11 LANGUAGES CXX VERSION 4.0.0)
# project_source_dir has not been set yet
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MAJOR_VERSION_STRING
REGEX "#define TOML11_VERSION_MAJOR ([0-9]+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MINOR_VERSION_STRING
REGEX "#define TOML11_VERSION_MINOR ([0-9]+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_PATCH_VERSION_STRING
REGEX "#define TOML11_VERSION_PATCH ([0-9]+)")
string(REGEX REPLACE "#define TOML11_VERSION_MAJOR ([0-9]+)" "\\1" TOML11_VERSION_MAJOR "${TOML11_MAJOR_VERSION_STRING}")
string(REGEX REPLACE "#define TOML11_VERSION_MINOR ([0-9]+)" "\\1" TOML11_VERSION_MINOR "${TOML11_MINOR_VERSION_STRING}")
string(REGEX REPLACE "#define TOML11_VERSION_PATCH ([0-9]+)" "\\1" TOML11_VERSION_PATCH "${TOML11_PATCH_VERSION_STRING}")
project(toml11 LANGUAGES CXX VERSION "${TOML11_VERSION_MAJOR}.${TOML11_VERSION_MINOR}.${TOML11_VERSION_PATCH}")
include(CMakeDependentOption)
include(CTest)

View File

@@ -8,7 +8,7 @@
[日本語版](https://github.com/ToruNiina/toml11/blob/main/README_ja.md)
toml11 is a feature-rich TOML language library for C++.
toml11 is a feature-rich TOML language library for C++11/14/17/20.
- It complies with [the latest TOML language specification](https://toml.io/en/v1.0.0).
- It passes all the standard TOML language [test cases](https://github.com/toml-lang/toml-test).
@@ -124,6 +124,14 @@ $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20
$ cmake --build ./build/
```
When linking the library, use `target_link_libraries` in CMake
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
or pass `-DTOML11_COMPILE_SOURCES` to the compiler.
### Building Example
To compile the examples in the `examples/` directory, set `-DTOML11_BUILD_EXAMPLES=ON`.

View File

@@ -5,7 +5,7 @@
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
toml11は、C++のための豊富な機能を持つTOML言語ライブラリです。
toml11は、C++11,14,17,20のための豊富な機能を持つTOML言語ライブラリです。
- [TOML言語の最新規格](https://toml.io/ja/v1.0.0)に準拠しています。
- TOML言語標準のテストケースすべてにパスしています。
@@ -122,6 +122,14 @@ $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20
$ cmake --build ./build/
```
ライブラリをリンクする場合は、CMakeで
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
とするか、コンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。
### Building example
`-DTOML11_BUILD_EXAMPLES=ON`とすることで、`examples/`をコンパイルできます。

View File

@@ -42,10 +42,18 @@ int main()
}
```
#### Specifying a File with `std::filesystem::path`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can accept a `std::filesystem::path`.
This requires C++17 or later, as it relies on the `<filesystem>` support.
#### Specifying an Input Stream with `std::istream`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept an `std::istream`.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
Without the filename information, error messages will display `"unknown file"`. To avoid this, you can pass the filename as a `std::string` in the second argument when using `std::istream`.
You can use streams other than `std::ifstream`, such as `std::istringstream`. Note that the entire content is readable at the time of the call.
@@ -64,16 +72,12 @@ int main()
}
```
#### Specifying a File with `std::filesystem::path`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can accept a `std::filesystem::path`.
This requires C++17 or later, as it relies on the `<filesystem>` support.
#### Specifying a File with `FILE*`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept a `FILE*`.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
As with `std::istream`, you need to provide the filename as a string in the second argument.
When passing a `FILE*`, if the file read fails, `errno` will be reported.

View File

@@ -54,6 +54,14 @@ By defining `-DTOML11_PRECOMPILE=ON` when running cmake, you can precompile some
$ cmake -B ./build/ -DTOML11_PRECOMPILE=ON
```
When linking the library, use `target_link_libraries` in CMake
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
or pass `-DTOML11_COMPILE_SOURCES` to the compiler to suppress header-only features.
However, since toml11 supports multiple C++ versions and may switch types based on the value of `__cplusplus`,
there is a possibility of link failures if the version used during build differs from the version used during usage.
If you encounter issues, set the required version using `CMAKE_CXX_STANDARD` during compilation.

View File

@@ -17,23 +17,6 @@ In case of failure, `toml::syntax_error` is thrown.
The type information of `basic_value` is provided by a `template`, and the TOML language version is specified by `toml::spec`.
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Parses the content of the given `std::istream&`.
The filename information is taken as the third argument. If the filename is not provided, it defaults to `"unknown file"`.
### `parse(std::string filename, toml::spec)`
```cpp
@@ -90,6 +73,25 @@ If reading the file fails, `toml::file_io_error` is thrown.
If parsing fails, `toml::syntax_error` is thrown.
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Parses the content of the given `std::istream&`.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
The filename information is taken as the third argument. If the filename is not provided, it defaults to `"unknown file"`.
### `parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -105,6 +107,8 @@ parse(FILE* fp,
Parses the content of the file pointed to by `FILE*`.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
If reading the file fails, `file_io_error` containing `errno` is thrown.
If parsing fails, `syntax_error` is thrown.
@@ -166,27 +170,6 @@ For instance, errors occurring internally within `std::ifstream` or memory exhau
{{< /hint >}}
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Takes a `std::istream&` and parses its content.
The file name information is taken as the second argument. If a file name is not provided, it defaults to `"unknown file"`.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(std::string filename, toml::spec)`
```cpp
@@ -241,6 +224,29 @@ If parsing fails, a `result` holding the error type `std::vector<error_info>` is
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Takes a `std::istream&` and parses its content.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
The file name information is taken as the second argument. If a file name is not provided, it defaults to `"unknown file"`.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -256,6 +262,8 @@ try_parse(FILE* fp,
Takes a `FILE*` and parses its content.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.

View File

@@ -44,11 +44,21 @@ int main()
}
```
#### `std::filesystem::path`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::filesystem::path`を渡すことも可能です。
当然ですが、`<filesystem>`がサポートされるC++17以降でなければ使用できません。
#### `std::istream`で入力ストリームを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::istream`を渡すことも可能です。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
その際、ファイル名の情報がなくなるため、エラーメッセージ中では `"unknown file"` となります。
これを避けるため、 `std::istream` を取る場合は第二引数に `std::string` でファイル名を取ることもできます。
@@ -70,18 +80,14 @@ int main()
}
```
#### `std::filesystem::path`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::filesystem::path`を渡すことも可能です。
当然ですが、`<filesystem>`がサポートされるC++17以降でなければ使用できません。
#### `FILE*`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`FILE*`を渡すことも可能です。
標準ライブラリによる改行文字の自動変換によるファイルサイズと文字数との不整合を避けるため、
`fopen("example.toml", "rb")`のようにしてバイナリモードで開いてください。
この場合も、`std::istream`のときと同様に、第二引数に文字列でファイル名を与える必要があります。
`FILE*`を渡した場合、ファイルの読み込みに失敗した際には`errno`が報告されます。

View File

@@ -54,6 +54,14 @@ target_link_libraries(main PRIVATE toml11::toml11)
$ cmake -B ./build/ -DTOML11_PRECOMPILE=ON
```
ライブラリをリンクする場合は、CMakeで
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
とするか、ヘッダ内の関数の`inline`化を避けるためにコンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。
ただし、toml11は複数のC++バージョンに対応するため、`__cplusplus`の値などによって型を切り替えることがあります。
そのため、ビルドした際のバージョンと使用時のバージョンが異なる場合、リンクに失敗する可能性があります。
問題が生じた場合は`CMAKE_CXX_STANDARD`によって必要なバージョンを設定してコンパイルしてください。

View File

@@ -17,23 +17,6 @@ type = "docs"
`basic_value`の持つ型情報は`template`で、TOML言語のバージョンは`toml::spec`で指定します。
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
ファイル名の情報は第三引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
### `parse(std::string filename, toml::spec)`
```cpp
@@ -90,6 +73,25 @@ parse(const std::filesystem::path& fpath,
パースに失敗した場合、`syntax_error`が送出されます。
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
ファイル名の情報は第三引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
### `parse(FILE*, std::string filename, toml::spec)`
@@ -106,6 +108,9 @@ parse(FILE* fp,
`FILE*`が指すファイルを読み込んでパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`fopen`には`"rb"`などを渡してバイナリモードで開いてください。
ファイルの読み込みに失敗した場合、`errno`が含まれた`file_io_error`が送出されます。
パースに失敗した場合、`syntax_error`が送出されます。
@@ -167,28 +172,6 @@ parse_str(std::string content,
{{< /hint >}}
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
ファイル名の情報は第二引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(std::string filename, toml::spec)`
```cpp
@@ -244,6 +227,30 @@ try_parse(const std::filesystem::path& fpath,
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
ファイル名の情報は第二引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -259,6 +266,9 @@ try_parse(FILE* fp,
`FILE*`を受け取って、そのファイルの内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`fopen`には`"rb"`などを渡してバイナリモードで開いてください。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "get.hpp"
#include "utility.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
@@ -101,54 +102,7 @@ find(basic_value<TC>&& v, const std::size_t idx)
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // detail
// ----------------------------------------------------------------------------
// find(v, keys...)
// toml::find(toml::value, toml::key, Ts&& ... keys) w/o conversion
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&

View File

@@ -7,7 +7,18 @@ namespace toml
template<typename T>
struct from;
// {
// static T from_toml(const toml::value& v)
// template<typename TC>
// static T from_toml(const toml::basic_value<TC>& v)
// {
// // User-defined conversions ...
// }
// };
template<typename T>
struct try_from;
// {
// template<typename TC>
// static result<T, error_info> try_from_toml(const toml::basic_value<TC>& v) noexcept
// {
// // User-defined conversions ...
// }

View File

@@ -7,19 +7,47 @@
namespace toml
{
enum class error_kind : std::uint8_t
{
runtime_error,
out_of_range,
type_error,
syntax_error,
file_io_error,
serialization_error
};
// error info returned from parser.
struct error_info
{
error_info(std::string t, source_location l, std::string m, std::string s = "")
: title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))},
: kind_(error_kind::runtime_error), title_(std::move(t)),
locations_{std::make_pair(std::move(l), std::move(m))},
suffix_(std::move(s))
{}
error_info(std::string t, std::vector<std::pair<source_location, std::string>> l,
std::string s = "")
: title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s))
: kind_(error_kind::runtime_error), title_(std::move(t)),
locations_(std::move(l)), suffix_(std::move(s))
{}
error_info(error_kind k, std::string t, source_location l, std::string m, std::string s = "")
: kind_(k), title_(std::move(t)),
locations_{std::make_pair(std::move(l), std::move(m))},
suffix_(std::move(s))
{}
error_info(error_kind k, std::string t,
std::vector<std::pair<source_location, std::string>> l,
std::string s = "")
: kind_(k), title_(std::move(t)),
locations_(std::move(l)), suffix_(std::move(s))
{}
error_kind& kind() noexcept {return kind_;}
error_kind kind() const noexcept {return kind_;}
std::string const& title() const noexcept {return title_;}
std::string & title() noexcept {return title_;}
@@ -36,11 +64,16 @@ struct error_info
private:
error_kind kind_;
std::string title_;
std::vector<std::pair<source_location, std::string>> locations_;
std::string suffix_; // hint or something like that
};
// forward decl
template<typename TypeConfig>
class basic_value;
namespace detail
{
inline error_info make_error_info_rec(error_info e)
@@ -53,6 +86,10 @@ inline error_info make_error_info_rec(error_info e, std::string s)
return e;
}
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
template<typename ... Ts>
error_info make_error_info_rec(error_info e,
source_location loc, std::string msg, Ts&& ... tail)
@@ -67,7 +104,14 @@ template<typename ... Ts>
error_info make_error_info(
std::string title, source_location loc, std::string msg, Ts&& ... tail)
{
error_info ei(std::move(title), std::move(loc), std::move(msg));
error_info ei(error_kind::runtime_error, std::move(title), std::move(loc), std::move(msg));
return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... );
}
template<typename ... Ts>
error_info make_error_info(error_kind k,
std::string title, source_location loc, std::string msg, Ts&& ... tail)
{
error_info ei(k, std::move(title), std::move(loc), std::move(msg));
return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... );
}

View File

@@ -84,7 +84,7 @@ error_info make_syntax_error(std::string title,
{
auto msg = std::string("expected ") + scanner.expected_chars(loc);
auto src = source_location(region(loc));
return make_error_info(
return make_error_info(error_kind::syntax_error,
std::move(title), std::move(src), std::move(msg), std::move(suffix));
}
@@ -116,8 +116,8 @@ parse_comment_line(location& loc, context<TC>& ctx)
loc.advance();
if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; }
}
return err(make_error_info("toml::parse_comment_line: "
"newline (LF / CRLF) or EOF is expected",
return err(make_error_info(error_kind::syntax_error,
"toml::parse_comment_line: newline (LF / CRLF) or EOF is expected",
source_location(region(loc)), "but got this",
"Hint: most of the control characters are not allowed in comments"));
}
@@ -383,7 +383,7 @@ parse_dec_integer(location& loc, const context<TC>& ctx)
if( ! sfx_reg.is_ok())
{
loc = first;
return err(make_error_info("toml::parse_dec_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_dec_integer: "
"invalid suffix: should be `_ non-digit-graph (graph | _graph)`",
source_location(region(loc)), "here"));
}
@@ -433,7 +433,7 @@ parse_integer(location& loc, const context<TC>& ctx)
if(std::isdigit(prefix))
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_integer: "
"leading zero in an decimal integer is not allowed",
std::move(src), "leading zero"));
}
@@ -526,7 +526,7 @@ parse_floating(location& loc, const context<TC>& ctx)
}
else
{
return err(make_error_info("toml::parse_floating: inf value found"
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: inf value found"
" but the current environment does not support inf. Please"
" make sure that the floating-point implementation conforms"
" IEEE 754/ISO 60559 international standard.",
@@ -542,7 +542,7 @@ parse_floating(location& loc, const context<TC>& ctx)
}
else
{
return err(make_error_info("toml::parse_floating: inf value found"
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: inf value found"
" but the current environment does not support inf. Please"
" make sure that the floating-point implementation conforms"
" IEEE 754/ISO 60559 international standard.",
@@ -562,7 +562,7 @@ parse_floating(location& loc, const context<TC>& ctx)
}
else
{
return err(make_error_info("toml::parse_floating: NaN value found"
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: NaN value found"
" but the current environment does not support NaN. Please"
" make sure that the floating-point implementation conforms"
" IEEE 754/ISO 60559 international standard.",
@@ -583,7 +583,7 @@ parse_floating(location& loc, const context<TC>& ctx)
}
else
{
return err(make_error_info("toml::parse_floating: NaN value found"
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: NaN value found"
" but the current environment does not support NaN. Please"
" make sure that the floating-point implementation conforms"
" IEEE 754/ISO 60559 international standard.",
@@ -648,7 +648,7 @@ parse_floating(location& loc, const context<TC>& ctx)
{
auto src = source_location(region(loc));
loc = first;
return err(make_error_info("toml::parse_floating: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: "
"invalid suffix: should be `_ non-digit-graph (graph | _graph)`",
std::move(src), "here"));
}
@@ -702,21 +702,21 @@ parse_local_date_only(location& loc, const context<TC>& ctx)
if(year_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_date: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_date: "
"failed to read year `" + str.substr(0, 4) + "`",
std::move(src), "here"));
}
if(month_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_date: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_date: "
"failed to read month `" + str.substr(5, 2) + "`",
std::move(src), "here"));
}
if(day_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_date: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_date: "
"failed to read day `" + str.substr(8, 2) + "`",
std::move(src), "here"));
}
@@ -751,7 +751,7 @@ parse_local_date_only(location& loc, const context<TC>& ctx)
if((month < 1 || 12 < month) || (day < 1 || max_day < day))
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_date: invalid date.",
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_date: invalid date.",
std::move(src), "month must be 01-12, day must be any of "
"01-28,29,30,31 depending on the month/year."));
}
@@ -822,14 +822,14 @@ parse_local_time_only(location& loc, const context<TC>& ctx)
if(hour_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read hour `" + str.substr(0, 2) + "`",
std::move(src), "here"));
}
if(minute_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read minute `" + str.substr(3, 2) + "`",
std::move(src), "here"));
}
@@ -840,7 +840,7 @@ parse_local_time_only(location& loc, const context<TC>& ctx)
if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute))
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: invalid time.",
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: invalid time.",
std::move(src), "hour must be 00-23, minute must be 00-59."));
}
@@ -866,7 +866,7 @@ parse_local_time_only(location& loc, const context<TC>& ctx)
if(sec_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read second `" + str.substr(6, 2) + "`",
std::move(src), "here"));
}
@@ -875,7 +875,7 @@ parse_local_time_only(location& loc, const context<TC>& ctx)
if(sec < 0 || 60 < sec) // :60 is allowed
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: invalid time.",
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: invalid time.",
std::move(src), "second must be 00-60."));
}
@@ -905,21 +905,21 @@ parse_local_time_only(location& loc, const context<TC>& ctx)
if(ms_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read milliseconds `" + secfrac.substr(0, 3) + "`",
std::move(src), "here"));
}
if(us_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read microseconds`" + str.substr(3, 3) + "`",
std::move(src), "here"));
}
if(ns_r.is_err())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_local_time: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_time: "
"failed to read nanoseconds`" + str.substr(6, 3) + "`",
std::move(src), "here"));
}
@@ -985,7 +985,7 @@ parse_local_datetime(location& loc, const context<TC>& ctx)
else
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_local_datetime: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_local_datetime: "
"expect date-time delimiter `T`, `t` or ` `(space).",
std::move(src), "here"));
}
@@ -1049,7 +1049,7 @@ parse_offset_datetime(location& loc, const context<TC>& ctx)
else
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_offset_datetime: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_offset_datetime: "
"expect date-time delimiter `T` or ` `(space).", std::move(src), "here"
));
}
@@ -1090,13 +1090,13 @@ parse_offset_datetime(location& loc, const context<TC>& ctx)
if(hour_r.is_err())
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_offset_datetime: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_offset_datetime: "
"Failed to read offset hour part", std::move(src), "here"));
}
if(minute_r.is_err())
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_offset_datetime: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_offset_datetime: "
"Failed to read offset minute part", std::move(src), "here"));
}
const auto hour = hour_r.unwrap();
@@ -1119,7 +1119,7 @@ parse_offset_datetime(location& loc, const context<TC>& ctx)
if (offset.hour < -24 || 24 < offset.hour ||
offset.minute < -60 || 60 < offset.minute)
{
return err(make_error_info("toml::parse_offset_datetime: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_offset_datetime: "
"too large offset: |hour| <= 24, |minute| <= 60",
source_location(region(first, loc)), "here"));
}
@@ -1181,7 +1181,7 @@ parse_utf8_codepoint(const region& reg)
if(0xD800 <= codepoint && codepoint <= 0xDFFF)
{
auto src = source_location(reg);
return err(make_error_info("toml::parse_utf8_codepoint: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_utf8_codepoint: "
"[0xD800, 0xDFFF] is not a valid UTF-8",
std::move(src), "here"));
}
@@ -1202,7 +1202,7 @@ parse_utf8_codepoint(const region& reg)
else // out of UTF-8 region
{
auto src = source_location(reg);
return err(make_error_info("toml::parse_utf8_codepoint: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_utf8_codepoint: "
"input codepoint is too large.",
std::move(src), "must be in range [0x00, 0x10FFFF]"));
}
@@ -1243,7 +1243,7 @@ parse_escape_sequence(location& loc, const context<TC>& ctx)
if( ! reg.is_ok())
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_escape_sequence: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_escape_sequence: "
"invalid token found in UTF-8 codepoint \\xhh",
std::move(src), "here"));
}
@@ -1261,7 +1261,7 @@ parse_escape_sequence(location& loc, const context<TC>& ctx)
if( ! reg.is_ok())
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_escape_sequence: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_escape_sequence: "
"invalid token found in UTF-8 codepoint \\uhhhh",
std::move(src), "here"));
}
@@ -1279,7 +1279,7 @@ parse_escape_sequence(location& loc, const context<TC>& ctx)
if( ! reg.is_ok())
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_escape_sequence: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_escape_sequence: "
"invalid token found in UTF-8 codepoint \\Uhhhhhhhh",
std::move(src), "here"));
}
@@ -1304,7 +1304,7 @@ parse_escape_sequence(location& loc, const context<TC>& ctx)
}
escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh";
return err(make_error_info("toml::parse_escape_sequence: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_escape_sequence: "
"unknown escape sequence.", std::move(src), escape_seqs));
}
return ok(retval);
@@ -1612,7 +1612,7 @@ parse_string(location& loc, const context<TC>& ctx)
else
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_string: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_string: "
"not a string", std::move(src), "here"));
}
}
@@ -1624,7 +1624,7 @@ parse_null(location& loc, const context<TC>& ctx)
const auto& spec = ctx.toml_spec();
if( ! spec.ext_null_value)
{
return err(make_error_info("toml::parse_null: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_null: "
"invalid spec: spec.ext_null_value must be true.",
source_location(region(loc)), "here"));
}
@@ -1754,7 +1754,7 @@ parse_key(location& loc, const context<TC>& ctx)
if(keys.empty())
{
auto src = source_location(region(first));
return err(make_error_info("toml::parse_key: expected a new key, "
return err(make_error_info(error_kind::syntax_error, "toml::parse_key: expected a new key, "
"but got nothing", std::move(src), "reached EOF"));
}
@@ -1906,7 +1906,7 @@ parse_array(location& loc, context<TC>& ctx)
if(loc.eof() || loc.current() != '[')
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_array: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_array: "
"The next token is not an array", std::move(src), "here"));
}
loc.advance();
@@ -1940,7 +1940,7 @@ parse_array(location& loc, context<TC>& ctx)
if( ! comma_found)
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_array: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_array: "
"expected value-separator `,` or closing `]`",
std::move(src), "here"));
}
@@ -2033,7 +2033,7 @@ parse_array(location& loc, context<TC>& ctx)
if(loc.current() != ']')
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_array: missing closing bracket `]`",
return err(make_error_info(error_kind::syntax_error, "toml::parse_array: missing closing bracket `]`",
std::move(src), "expected `]`, reached EOF"));
}
else
@@ -2216,7 +2216,7 @@ insert_value(const inserting_value_kind kind,
if(fmt == table_format::oneline || fmt == table_format::multiline_oneline)
{
// foo = {bar = "baz"} or foo = { \n bar = "baz" \n }
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a value: inline table is immutable",
key_loc, "inserting this",
found->second.location(), "to this table"));
@@ -2224,7 +2224,7 @@ insert_value(const inserting_value_kind kind,
// dotted key cannot reopen a table.
if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted)
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"reopening a table using dotted keys",
key_loc, "dotted key cannot reopen a table",
found->second.location(), "this table is already closed"));
@@ -2237,7 +2237,7 @@ insert_value(const inserting_value_kind kind,
// aot = [{this = "type", of = "aot"}] # cannot be reopened
if(found->second.as_array_fmt().fmt != array_format::array_of_tables)
{
return err(make_error_info("toml::insert_value:"
return err(make_error_info(error_kind::syntax_error, "toml::insert_value:"
"inline array of tables are immutable",
key_loc, "inserting this",
found->second.location(), "inline array of tables"));
@@ -2249,7 +2249,7 @@ insert_value(const inserting_value_kind kind,
// [[array.of.tables]]
// [array.of] # reopening supertable is okay
// tables.x = "foo" # appending `x` to the first table
return err(make_error_info("toml::insert_value:"
return err(make_error_info(error_kind::syntax_error, "toml::insert_value:"
"dotted key cannot reopen an array-of-tables",
key_loc, "inserting this",
found->second.location(), "to this array-of-tables."));
@@ -2269,7 +2269,7 @@ insert_value(const inserting_value_kind kind,
}
else
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a value, value already exists",
key_loc, "while inserting this",
found->second.location(), "non-table value already exists"));
@@ -2283,7 +2283,7 @@ insert_value(const inserting_value_kind kind,
{
if(current_table.find(key) != current_table.end())
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a value, value already exists",
key_loc, "inserting this",
current_table.at(key).location(), "but value already exists"));
@@ -2307,7 +2307,7 @@ insert_value(const inserting_value_kind kind,
if( ! target.is_table() || // could be an array-of-tables
target.as_table_fmt().fmt != table_format::implicit)
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a table, table already defined",
key_loc, "inserting this",
target.location(), "this table is explicitly defined"));
@@ -2322,7 +2322,7 @@ insert_value(const inserting_value_kind kind,
// w = "foo"
// [x]
// y = "bar"
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a table, table keys conflict to each other",
key_loc, "inserting this table",
kv.second.location(), "having this value",
@@ -2362,14 +2362,14 @@ insert_value(const inserting_value_kind kind,
{
if( ! found->second.is_array_of_tables())
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert an array of tables, value already exists",
key_loc, "while inserting this",
found->second.location(), "non-table value already exists"));
}
if(found->second.as_array_fmt().fmt != array_format::array_of_tables)
{
return err(make_error_info("toml::insert_value: "
return err(make_error_info(error_kind::syntax_error, "toml::insert_value: "
"failed to insert a table, inline array of tables is immutable",
key_loc, "while inserting this",
found->second.location(), "this is inline array-of-tables"));
@@ -2383,7 +2383,7 @@ insert_value(const inserting_value_kind kind,
}
}
}
return err(make_error_info("toml::insert_key: no keys found",
return err(make_error_info(error_kind::syntax_error, "toml::insert_key: no keys found",
std::move(key_loc), "here"));
}
@@ -2403,7 +2403,7 @@ parse_inline_table(location& loc, context<TC>& ctx)
if(loc.eof() || loc.current() != '{')
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_inline_table: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_inline_table: "
"The next token is not an inline table", std::move(src), "here"));
}
loc.advance();
@@ -2438,7 +2438,7 @@ parse_inline_table(location& loc, context<TC>& ctx)
if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables)
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_inline_table: trailing "
return err(make_error_info(error_kind::syntax_error, "toml::parse_inline_table: trailing "
"comma is not allowed in TOML-v1.0.0)", std::move(src), "here"));
}
@@ -2458,7 +2458,7 @@ parse_inline_table(location& loc, context<TC>& ctx)
if( ! comma_found && ! still_empty)
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_inline_table: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_inline_table: "
"expected value-separator `,` or closing `}`",
std::move(src), "here"));
}
@@ -2586,7 +2586,7 @@ parse_inline_table(location& loc, context<TC>& ctx)
if(loc.current() != '}')
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_inline_table: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_inline_table: "
"missing closing bracket `}`",
std::move(src), "expected `}`, reached line end"));
}
@@ -2702,7 +2702,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
return ok(value_t::floating);
}
auto src = source_location(region(loc));
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad float: `_` must be surrounded by digits",
std::move(src), "invalid underscore",
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
@@ -2723,7 +2723,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
return ok(value_t::floating);
}
auto src = source_location(region(loc));
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad float: `_` must be surrounded by digits",
std::move(src), "invalid underscore",
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
@@ -2750,7 +2750,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
int_reg.as_string() == "-0" || int_reg.as_string() == "+0"))
{
auto src = source_location(region(loc));
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad integer: leading zero is not allowed in decimal int",
std::move(src), "leading zero",
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
@@ -2759,7 +2759,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
else
{
auto src = source_location(region(loc));
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad integer: `_` must be surrounded by digits",
std::move(src), "invalid underscore",
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
@@ -2771,7 +2771,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
if(loc.current() == '0')
{
loc.retrace();
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad integer: leading zero",
source_location(region(loc)), "leading zero is not allowed",
std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
@@ -2780,7 +2780,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
}
else // invalid digits, especially in oct/bin ints.
{
return err(make_error_info(
return err(make_error_info(error_kind::syntax_error,
"bad integer: invalid digit after an integer",
source_location(region(loc)), "this digit is not allowed",
std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
@@ -2791,7 +2791,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
if(c == ':' || c == '-')
{
auto src = source_location(region(loc));
return err(make_error_info("bad datetime: invalid format",
return err(make_error_info(error_kind::syntax_error, "bad datetime: invalid format",
std::move(src), "here",
std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n"
"Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30")
@@ -2800,7 +2800,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
if(c == '.' || c == 'e' || c == 'E')
{
auto src = source_location(region(loc));
return err(make_error_info("bad float: invalid format",
return err(make_error_info(error_kind::syntax_error, "bad float: invalid format",
std::move(src), "here", std::string(
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")));
@@ -2811,7 +2811,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
if( ! loc.eof() && loc.current() == '.')
{
auto src = source_location(region(loc));
return err(make_error_info("bad float: integer part is required before decimal point",
return err(make_error_info(error_kind::syntax_error, "bad float: integer part is required before decimal point",
std::move(src), "missing integer part", std::string(
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")
@@ -2820,7 +2820,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
if( ! loc.eof() && loc.current() == '_')
{
auto src = source_location(region(loc));
return err(make_error_info("bad number: `_` must be surrounded by digits",
return err(make_error_info(error_kind::syntax_error, "bad number: `_` must be surrounded by digits",
std::move(src), "digits required before `_`", std::string(
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
"Hint: invalid: _42, 1__000, 0123\n")
@@ -2828,7 +2828,7 @@ guess_number_type(const location& first, const context<TC>& ctx)
}
auto src = source_location(region(loc));
return err(make_error_info("bad format: unknown value appeared",
return err(make_error_info(error_kind::syntax_error, "bad format: unknown value appeared",
std::move(src), "here"));
}
@@ -2968,7 +2968,7 @@ parse_value(location& loc, context<TC>& ctx)
else
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_value: unknown value appeared",
return err(make_error_info(error_kind::syntax_error, "toml::parse_value: unknown value appeared",
std::move(src), "here"));
}
}
@@ -2985,7 +2985,7 @@ parse_value(location& loc, context<TC>& ctx)
default:
{
auto src = source_location(region(loc));
return err(make_error_info("toml::parse_value: unknown value appeared",
return err(make_error_info(error_kind::syntax_error, "toml::parse_value: unknown value appeared",
std::move(src), "here"));
}
}
@@ -3097,7 +3097,7 @@ parse_table(location& loc, context<TC>& ctx, basic_value<TC>& table)
newline_found = newline_found || (sp.has_value() && sp.value().newline_found);
if( ! newline_found)
{
return err(make_error_info("toml::parse_table: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_table: "
"newline (LF / CRLF) or EOF is expected",
source_location(region(loc)), "here"));
}
@@ -3524,7 +3524,7 @@ try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::d
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
is.read(reinterpret_cast<char*>(letters.data()), fsize);
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
@@ -3560,7 +3560,8 @@ try_parse(std::string fname, spec s = spec::default_version())
if(!ifs.good())
{
std::vector<error_info> e;
e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {}));
e.push_back(error_info(error_kind::file_io_error,
"toml::parse: Error opening file \"" + fname + "\"", {}));
return err(std::move(e));
}
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
@@ -3641,7 +3642,8 @@ try_parse(const FSPATH& fpath, spec s = spec::default_version())
if(!ifs.good())
{
std::vector<error_info> e;
e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {}));
e.push_back(error_info(error_kind::file_io_error,
"toml::parse: Error opening file \"" + fpath.string() + "\"", {}));
return err(std::move(e));
}
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
@@ -3675,7 +3677,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
const long beg = std::ftell(fp);
if (beg == -1L)
{
return err(std::vector<error_info>{error_info(
return err(std::vector<error_info>{error_info(error_kind::file_io_error,
std::string("Failed to access: \"") + filename +
"\", errno = " + std::to_string(errno), {}
)});
@@ -3684,7 +3686,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
const int res_seekend = std::fseek(fp, 0, SEEK_END);
if (res_seekend != 0)
{
return err(std::vector<error_info>{error_info(
return err(std::vector<error_info>{error_info(error_kind::file_io_error,
std::string("Failed to seek: \"") + filename +
"\", errno = " + std::to_string(errno), {}
)});
@@ -3693,7 +3695,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
const long end = std::ftell(fp);
if (end == -1L)
{
return err(std::vector<error_info>{error_info(
return err(std::vector<error_info>{error_info(error_kind::file_io_error,
std::string("Failed to access: \"") + filename +
"\", errno = " + std::to_string(errno), {}
)});
@@ -3704,7 +3706,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET);
if (res_seekbeg != 0)
{
return err(std::vector<error_info>{error_info(
return err(std::vector<error_info>{error_info(error_kind::file_io_error,
std::string("Failed to seek: \"") + filename +
"\", errno = " + std::to_string(errno), {}
)});
@@ -3714,7 +3716,15 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
return err(std::vector<error_info>{error_info(error_kind::file_io_error,
std::string("File size changed: \"") + filename +
std::string("\" make sure that FILE* is in binary mode "
"to avoid LF <-> CRLF conversion"), {}
)});
}
return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
}
@@ -3752,7 +3762,12 @@ parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
throw file_io_error(errno, "File size changed; make sure that "
"FILE* is in binary mode to avoid LF <-> CRLF conversion", filename);
}
auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
if(res.is_ok())

View File

@@ -90,12 +90,19 @@ struct has_specialized_from_impl
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_try_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::try_from<T>)>
static std::true_type check(::toml::try_from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
static std::true_type check(::toml::into<T>*);
};
@@ -130,6 +137,8 @@ template<typename T>
struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_try_from: decltype(has_specialized_try_from_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype

228
include/toml11/try_find.hpp Normal file
View File

@@ -0,0 +1,228 @@
#ifndef TOML11_TRY_FIND_HPP
#define TOML11_TRY_FIND_HPP
#include <algorithm>
#include "try_get.hpp"
#include "utility.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC> const&>()))
try_find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
const auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(std::move(res.as_ok()));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC> const&>()))
try_find(const basic_value<TC>& v, const std::size_t idx) noexcept
{
const auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(std::move(res.as_ok()));
}
// ----------------------------------------------------------------------------
// find(value, key/idx), w/o conversion
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
return v.try_at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky) noexcept
{
return v.try_at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return ok(basic_value<TC>(std::move(res.as_ok())));
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const std::size_t idx) noexcept
{
return v.try_at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(basic_value<TC> const& v, const std::size_t idx) noexcept
{
return v.try_at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(std::move(res.as_err()));
}
return ok(basic_value<TC>(std::move(res.as_ok())));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys) w/o conversion
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(std::move(res.as_ok()), detail::key_cast<TC>(k2), ks...);
}
// ----------------------------------------------------------------------------
// find<T>(v, keys...)
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<const basic_value<TC>&>()))
try_find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(std::move(res.as_ok()), detail::key_cast<TC>(k2), ks...);
}
} // toml
#endif // TOML11_FIND_HPP

473
include/toml11/try_get.hpp Normal file
View File

@@ -0,0 +1,473 @@
#ifndef TOML11_TRY_GET_HPP
#define TOML11_TRY_GET_HPP
#include <algorithm>
#include "error_info.hpp"
#include "from.hpp"
#include "types.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif // string_view
namespace toml
{
// ============================================================================
// T is toml::value; identity transformation.
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<std::reference_wrapper<T>, error_info>>
try_get(basic_value<TC>& v) noexcept
{
return ok(std::ref(v));
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<std::reference_wrapper<const T>, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
return ok(std::cref(v));
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<T, error_info>>
try_get(basic_value<TC>&& v) noexcept
{
return ok(basic_value<TC>(std::move(v)));
}
// ============================================================================
// exact toml::* type
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<std::reference_wrapper<T>, error_info>>
try_get(basic_value<TC>& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(std::ref(detail::getter<TC, ty>::get_nothrow(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<std::reference_wrapper<const T>, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(std::cref(detail::getter<TC, ty>::get_nothrow(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<T, error_info>>
try_get(basic_value<TC>&& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(detail::getter<TC, ty>::get_nothrow(std::move(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
// ============================================================================
// T is toml::basic_value<U>
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_basic_value<T>,
cxx::negation<std::is_same<T, basic_value<TC>>>
>::value, result<T, error_info>>
try_get(basic_value<TC> v) noexcept
{
return ok(T(std::move(v)));
}
// ============================================================================
// integer convertible from toml::value::integer_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_integral<T>,
cxx::negation<std::is_same<T, bool>>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_integer())
{
return ok(static_cast<T>(v.as_integer(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::integer));
}
}
// ============================================================================
// floating point convertible from toml::value::floating_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_floating_point<T>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_floating())
{
return ok(static_cast<T>(v.as_floating(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::floating));
}
}
// ============================================================================
// std::string_view
#if defined(TOML11_HAS_STRING_VIEW)
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, std::string_view>::value,
result<std::string_view, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_string())
{
return ok(std::string_view(v.as_string(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::string));
}
}
#endif // string_view
// ============================================================================
// std::chrono::duration from toml::local_time
template<typename T, typename TC>
cxx::enable_if_t<detail::is_chrono_duration<T>::value,
result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_local_time())
{
return ok(std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.as_local_time(std::nothrow))));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::local_time));
}
}
// ============================================================================
// std::chrono::system_clock::time_point from toml::datetime variants
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<std::chrono::system_clock::time_point, T>::value,
result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
switch(v.type())
{
case value_t::local_date:
{
return ok(std::chrono::system_clock::time_point(v.as_local_date(std::nothrow)));
}
case value_t::local_datetime:
{
return ok(std::chrono::system_clock::time_point(v.as_local_datetime(std::nothrow)));
}
case value_t::offset_datetime:
{
return ok(std::chrono::system_clock::time_point(v.as_offset_datetime(std::nothrow)));
}
default:
{
const auto loc = v.location();
return err(make_error_info(error_kind::type_error, "toml::try_get(): bad_cast to "
"std::chrono::system_clock::time_point", loc,
"the actual type is " + to_string(v.type())));
}
}
}
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // .push_back() works
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
const auto& a = v.as_array(std::nothrow);
T container;
detail::try_reserve(container, a.size()); // if T has .reserve(), call it
for(const auto& elem : a)
{
auto converted = try_get<value_type>(elem);
if(converted.is_err())
{
return err(converted.as_err());
}
container.push_back(std::move(converted.as_ok()));
}
return ok(container);
}
// ============================================================================
// std::array
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_array<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
const auto& a = v.as_array(std::nothrow);
T container;
if(a.size() != container.size())
{
const auto loc = v.location();
return err(make_error_info(error_kind::out_of_range, "toml::try_get: while converting to an array: "
" array size is " + std::to_string(container.size()) +
" but there are " + std::to_string(a.size()) + " elements in toml array.",
loc, "here"));
}
for(std::size_t i=0; i<a.size(); ++i)
{
auto converted = try_get<value_type>(a[i]);
if(converted.is_err())
{
return err(converted.as_err());
}
container[i] = std::move(converted.as_ok());
}
return ok(container);
}
// ============================================================================
// std::forward_list
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_forward_list<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array(std::nothrow))
{
auto converted = try_get<value_type>(elem);
if(converted.is_err())
{
return err(converted.as_err());
}
container.push_front(std::move(converted.as_ok()));
}
container.reverse();
return ok(container);
}
// ============================================================================
// std::pair
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_pair<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using first_type = typename T::first_type;
using second_type = typename T::second_type;
const auto& ar = v.as_array(std::nothrow);
if(ar.size() != 2)
{
const auto loc = v.location();
return err(make_error_info(error_kind::out_of_range, "toml::try_get: while converting std::pair: "
" but there are " + std::to_string(ar.size()) +
" > 2 elements in toml array.",
loc, "here"));
}
auto first_result = try_get<first_type>(ar[0]);
if(first_result.is_err())
{
return err(first_result.as_err());
}
auto second_result = try_get<second_type>(ar[1]);
if(second_result.is_err())
{
return err(second_result.as_err());
}
return ok(std::make_pair(std::move(first_result.as_ok()),
std::move(second_result.as_ok())));
}
// ============================================================================
// std::tuple.
namespace detail
{
template<typename T, std::size_t I, std::size_t N>
struct try_get_tuple_impl
{
template<typename Array, typename U>
static result<T, error_info> invoke(const Array& a, U curr) noexcept
{
assert(I < a.size());
using value_type = typename std::tuple_element<I, T>::type;
auto converted = try_get<value_type>(a[I]);
if(converted.is_err())
{
return err(converted.as_err());
}
return try_get_tuple_impl<T, I+1, N>::invoke(a, std::tuple_cat(
std::move(curr), std::make_tuple(std::move(converted.as_ok()))));
}
};
template<typename T, std::size_t I>
struct try_get_tuple_impl<T, I, I>
{
template<typename Array>
static result<T, error_info> invoke(const Array&, T x) noexcept
{
return ok(std::move(x));
}
};
} // detail
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_tuple<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
const auto& ar = v.as_array(std::nothrow);
if(ar.size() != std::tuple_size<T>::value)
{
const auto loc = v.location();
return err(make_error_info(error_kind::out_of_range,
"toml::try_get: while converting std::tuple: "
" there are " + std::to_string(ar.size()) + " > " +
std::to_string(std::tuple_size<T>::value) + " elements in toml array.",
loc, "here"));
}
return detail::try_get_tuple_impl<T, 0, std::tuple_size<T>::value>::invoke(
ar, std::make_tuple());
}
// ============================================================================
// map-like types; most likely STL map, like std::map or std::unordered_map.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
static_assert(
std::is_convertible<typename basic_value<TC>::key_type, key_type>::value,
"toml::get only supports map type of which key_type is "
"convertible from toml::basic_value::key_type.");
if(v.type() != toml::value_t::table)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::table));
}
T m;
for(const auto& kv : v.as_table(std::nothrow))
{
auto converted = try_get<mapped_type>(kv.second);
if(converted.is_err())
{
return err(converted.as_err());
}
m.emplace(key_type(kv.first), std::move(converted.as_ok()));
}
return ok(m);
}
// ============================================================================
// user-defined type that defines `try_from<T>`.
template<typename T, typename TC>
cxx::enable_if_t<detail::has_specialized_try_from<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
return ::toml::try_from<T>::try_from_toml(v);
}
} // toml
#endif // TOML11_TRY_GET_HPP

View File

@@ -44,7 +44,7 @@ read_dec_int(const std::string& str, const source_location src)
iss >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_dec_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_dec_integer: "
"too large integer: current max digits = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
@@ -63,7 +63,7 @@ read_hex_int(const std::string& str, const source_location src)
iss >> std::hex >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_hex_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_hex_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
@@ -82,7 +82,7 @@ read_oct_int(const std::string& str, const source_location src)
iss >> std::oct >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_oct_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_oct_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
@@ -131,7 +131,7 @@ read_bin_int(const std::string& str, const source_location src)
}
if(base == 0)
{
return err(make_error_info("toml::parse_bin_integer: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_bin_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
@@ -166,7 +166,7 @@ read_hex_float(const std::string& str, const source_location src, float val)
#endif
if(res != 1)
{
return err(make_error_info("toml::parse_floating: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: "
"failed to read hexadecimal floating point value ",
std::move(src), "here"));
}
@@ -182,7 +182,7 @@ read_hex_float(const std::string& str, const source_location src, double val)
#endif
if(res != 1)
{
return err(make_error_info("toml::parse_floating: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: "
"failed to read hexadecimal floating point value ",
std::move(src), "here"));
}
@@ -195,7 +195,7 @@ cxx::enable_if_t<cxx::conjunction<
>::value, result<T, error_info>>
read_hex_float(const std::string&, const source_location src, T)
{
return err(make_error_info("toml::parse_floating: failed to read "
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: failed to read "
"floating point value because of unknown type in type_config",
std::move(src), "here"));
}
@@ -209,7 +209,7 @@ read_dec_float(const std::string& str, const source_location src)
iss >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_floating: "
return err(make_error_info(error_kind::syntax_error, "toml::parse_floating: "
"failed to read floating point value from stream",
std::move(src), "here"));
}

View File

@@ -99,6 +99,47 @@ inline std::string make_string(std::size_t len, char c)
return std::string(len, c);
}
// ---------------------------------------------------------------------------
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // namespace detail
} // namespace toml
#endif // TOML11_UTILITY_HPP

View File

@@ -2102,12 +2102,16 @@ operator>=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
}
// error_info helper
namespace detail
{
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
{
return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward<Ts>(tail)...);
}
} // detail
template<typename TC, typename ... Ts>
error_info make_error_info(
std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail)
@@ -2116,6 +2120,13 @@ error_info make_error_info(
v.location(), std::move(msg), std::forward<Ts>(tail)...);
}
template<typename TC, typename ... Ts>
error_info make_error_info(error_kind k,
std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail)
{
return make_error_info(k, std::move(title),
v.location(), std::move(msg), std::forward<Ts>(tail)...);
}
template<typename TC, typename ... Ts>
std::string format_error(std::string title,
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
{
@@ -2129,7 +2140,8 @@ namespace detail
template<typename TC>
error_info make_type_error(const basic_value<TC>& v, const std::string& fname, const value_t ty)
{
return make_error_info(fname + ": bad_cast to " + to_string(ty),
return make_error_info(error_kind::type_error,
fname + ": bad_cast to " + to_string(ty),
v.location(), "the actual type is " + to_string(v.type()));
}
template<typename TC>
@@ -2141,7 +2153,7 @@ error_info make_not_found_error(const basic_value<TC>& v, const std::string& fna
std::vector<std::pair<source_location, std::string>> locs;
if( ! loc.is_ok())
{
return error_info(title, locs);
return error_info(error_kind::out_of_range, title, locs);
}
if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1)
@@ -2166,7 +2178,7 @@ error_info make_not_found_error(const basic_value<TC>& v, const std::string& fna
{
locs.emplace_back(v.location(), "in this table");
}
return error_info(title, locs);
return error_info(error_kind::out_of_range, title, locs);
}
template<typename TC>
error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const std::size_t idx)
@@ -2178,7 +2190,8 @@ error_info make_not_found_error(const basic_value<TC>& v, const std::string& fna
std::ostringstream oss;
oss << "actual length (" << v.as_array(std::nothrow).size()
<< ") is shorter than the specified index (" << idx << ").";
return make_error_info(fname + ": no element corresponding to the index",
return make_error_info(error_kind::out_of_range,
fname + ": no element corresponding to the index",
v, oss.str());
}

View File

@@ -3,7 +3,7 @@
#define TOML11_VERSION_MAJOR 4
#define TOML11_VERSION_MINOR 0
#define TOML11_VERSION_PATCH 0
#define TOML11_VERSION_PATCH 2
#ifndef __cplusplus
# error "__cplusplus is not defined"

View File

@@ -52,8 +52,7 @@ struct local_time_format_info;
struct array_format_info;
struct table_format_info;
template<typename Key, typename Val, typename Cmp = std::equal_to<Key>,
typename Allocator = std::allocator<std::pair<Key, Val>>>
template<typename Key, typename Val, typename Cmp, typename Allocator>
class ordered_map;
struct syntax_error;

View File

@@ -3,7 +3,7 @@
#define TOML11_VERSION_MAJOR 4
#define TOML11_VERSION_MINOR 0
#define TOML11_VERSION_PATCH 0
#define TOML11_VERSION_PATCH 2
#ifndef __cplusplus
# error "__cplusplus is not defined"
@@ -3584,7 +3584,7 @@ struct has_specialized_into_impl
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
static std::true_type check(::toml::into<T>*);
};
@@ -5447,6 +5447,10 @@ struct error_info
std::string suffix_; // hint or something like that
};
// forward decl
template<typename TypeConfig>
class basic_value;
namespace detail
{
inline error_info make_error_info_rec(error_info e)
@@ -5459,6 +5463,10 @@ inline error_info make_error_info_rec(error_info e, std::string s)
return e;
}
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
template<typename ... Ts>
error_info make_error_info_rec(error_info e,
source_location loc, std::string msg, Ts&& ... tail)
@@ -7588,12 +7596,16 @@ operator>=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
}
// error_info helper
namespace detail
{
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
{
return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward<Ts>(tail)...);
}
} // detail
template<typename TC, typename ... Ts>
error_info make_error_info(
std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail)
@@ -15063,7 +15075,7 @@ try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::d
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
is.read(reinterpret_cast<char*>(letters.data()), fsize);
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
@@ -15253,7 +15265,15 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
return err(std::vector<error_info>{error_info(
std::string("File size changed: \"") + filename +
std::string("\" make sure that FILE* is in binary mode "
"to avoid LF <-> CRLF conversion"), {}
)});
}
return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
}
@@ -15291,7 +15311,12 @@ parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
throw file_io_error(errno, "File size changed; make sure that "
"FILE* is in binary mode to avoid LF <-> CRLF conversion", filename);
}
auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
if(res.is_ok())

View File

@@ -1,12 +1,15 @@
set(TOML11_TEST_NAMES
test_comments
test_datetime
test_error_message
test_find
test_try_find
test_find_or
test_format_integer
test_format_floating
test_format_table
test_get
test_try_get
test_get_or
test_location
test_literal
@@ -22,6 +25,7 @@ set(TOML11_TEST_NAMES
test_parse_table
test_result
test_scanner
test_serialize
test_syntax_boolean
test_syntax_integer
test_syntax_floating

View File

@@ -0,0 +1,79 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include <toml.hpp>
TEST_CASE("testing custom error message using source_location")
{
const toml::value root = toml::parse_str(R"(
range = [0, 42]
val = 54
)");
const auto& lower = root.at("range").at(0);
const auto& upper = root.at("range").at(1);
const auto& val = root.at("val");
const auto err = toml::make_error_info("val not in range",
lower.location(), "lower limit is defined here",
upper.location(), "upper limit is defined here",
val.location(), "this is not in the range",
"Hint: upper limit is inclusive"
);
CHECK_EQ(err.title(), "val not in range");
CHECK_EQ(err.locations().size(), 3);
CHECK_EQ(err.locations().at(0).second, "lower limit is defined here");
CHECK_EQ(err.locations().at(1).second, "upper limit is defined here");
CHECK_EQ(err.locations().at(2).second, "this is not in the range" );
}
TEST_CASE("testing custom error message using value")
{
const toml::value root = toml::parse_str(R"(
range = [0, 42]
val = 54
)");
const auto& lower = root.at("range").at(0);
const auto& upper = root.at("range").at(1);
const auto& val = root.at("val");
const auto err = toml::make_error_info("val not in range",
lower, "lower limit is defined here",
upper, "upper limit is defined here",
val, "this is not in the range",
"Hint: upper limit is inclusive"
);
CHECK_EQ(err.title(), "val not in range");
CHECK_EQ(err.locations().size(), 3);
CHECK_EQ(err.locations().at(0).second, "lower limit is defined here");
CHECK_EQ(err.locations().at(1).second, "upper limit is defined here");
CHECK_EQ(err.locations().at(2).second, "this is not in the range" );
}
TEST_CASE("testing custom error message using source_location and value")
{
const toml::value root = toml::parse_str(R"(
range = [0, 42]
val = 54
)");
const auto& lower = root.at("range").at(0);
const auto& upper = root.at("range").at(1);
const auto& val = root.at("val");
const auto err = toml::make_error_info("val not in range",
lower, "lower limit is defined here",
upper, "upper limit is defined here",
val.location(), "this is not in the range",
"Hint: upper limit is inclusive"
);
CHECK_EQ(err.title(), "val not in range");
CHECK_EQ(err.locations().size(), 3);
CHECK_EQ(err.locations().at(0).second, "lower limit is defined here");
CHECK_EQ(err.locations().at(1).second, "upper limit is defined here");
CHECK_EQ(err.locations().at(2).second, "this is not in the range" );
}

150
tests/test_serialize.cpp Normal file
View File

@@ -0,0 +1,150 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include <toml.hpp>
#include <clocale>
TEST_CASE("testing serialization")
{
using namespace toml::literals::toml_literals;
const auto spec_example = R"(# This is a TOML document
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }
[servers]
[servers.alpha]
ip = "10.0.0.1"
role = "frontend"
[servers.beta]
ip = "10.0.0.2"
role = "backend")"_toml;
// format and parse
const auto v = toml::parse_str(toml::format(spec_example));
CHECK_EQ(v, spec_example);
}
TEST_CASE("testing serialization with complicated keys")
{
using namespace toml::literals::toml_literals;
const auto spec_example = R"(
[keys]
key = "value"
bare_key = "value"
bare-key = "value"
1234 = "value"
"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"
'key2' = "value"
'quoted "value"' = "value"
"" = "blank"
fruits.apple.skin = "thin"
fruits.apple.color = "red"
fruits.orange.skin = "thick"
fruits.orange.color = "orange"
site."google.com" = true
3.14159 = "pi"
)"_toml;
// format and parse
const auto v = toml::parse_str(toml::format(spec_example));
CHECK_EQ(v, spec_example);
}
TEST_CASE("testing serialization with array of tables")
{
using namespace toml::literals::toml_literals;
const auto spec_example = R"(
points = [ { x = 1, y = 2, z = 3 },
{ x = 7, y = 8, z = 9 },
{ x = 2, y = 4, z = 8 } ]
[[products]]
name = "Hammer"
sku = 738594937
[[products]] # empty table within the array
[[products]]
name = "Nail"
sku = 284758393
color = "gray"
[[fruits]]
name = "apple"
[fruits.physical] # subtable
color = "red"
shape = "round"
[[fruits.varieties]] # nested array of tables
name = "red delicious"
[[fruits.varieties]]
name = "granny smith"
[[fruits]]
name = "banana"
[[fruits.varieties]]
name = "plantain"
)"_toml;
// format and parse
const auto v = toml::parse_str(toml::format(spec_example));
CHECK_EQ(v, spec_example);
}
TEST_CASE("testing serialization with locale")
{
std::string current_locale = std::setlocale(LC_ALL, nullptr);
// fr_FR is a one of locales that uses `,` as a decimal separator.
if(const char* try_hyphen = std::setlocale(LC_ALL, "fr_FR.UTF-8"))
{
current_locale = std::string(try_hyphen);
}
else if(const char* try_nohyphen = std::setlocale(LC_ALL, "fr_FR.utf8"))
{
current_locale = std::string(try_nohyphen);
}
MESSAGE("current_locale = ", current_locale);
const auto v1 = toml::parse_str(R"(
pi = 3.1415_9265
large_int = 123_456_789
)");
const auto v2 = toml::parse_str(toml::format(v1));
// actually, it checkl if v1 is serialized correctly under FR locale
// where 3.1415 -> 3,1415
CHECK_EQ(v1, v2);
}

829
tests/test_try_find.cpp Normal file
View File

@@ -0,0 +1,829 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "utility.hpp"
#include <toml11/value.hpp>
#include <toml11/try_find.hpp>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::try_find with toml type")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
{
value_type v(toml::table{{"a", true}});
CHECK_EQ(true, toml::try_find<boolean_type>(v, "a").unwrap());
toml::try_find<boolean_type>(v, "a").unwrap() = false;
CHECK_EQ(false, toml::try_find<boolean_type>(v, "a").unwrap());
boolean_type x = toml::try_find<boolean_type>(std::move(v), "a").unwrap();
CHECK_EQ(false, x);
}
{
value_type v(toml::table{{"a", 42}});
CHECK_EQ(integer_type(42), toml::try_find<integer_type>(v, "a").unwrap());
toml::try_find<integer_type>(v, "a").unwrap() = 54;
CHECK_EQ(integer_type(54), toml::try_find<integer_type>(v, "a").unwrap());
integer_type x = toml::try_find<integer_type>(std::move(v), "a").unwrap();
CHECK_EQ(integer_type(54), x);
}
{
value_type v(toml::table{{"a", 3.14}});
CHECK_EQ(floating_type(3.14), toml::try_find<floating_type>(v, "a").unwrap());
toml::try_find<floating_type>(v, "a").unwrap() = 2.71;
CHECK_EQ(floating_type(2.71), toml::try_find<floating_type>(v, "a").unwrap());
floating_type x = toml::try_find<floating_type>(std::move(v), "a").unwrap();
CHECK_EQ(floating_type(2.71), x);
}
{
value_type v(toml::table{{"a", "foo"}});
CHECK_EQ("foo", toml::try_find<string_type>(v, "a").unwrap());
toml::try_find<string_type>(v, "a").unwrap() += "bar";
CHECK_EQ("foobar", toml::try_find<string_type>(v, "a").unwrap());
string_type x = toml::try_find<string_type>(std::move(v), "a").unwrap();
CHECK_EQ("foobar", x);
}
{
local_date_type d(2018, toml::month_t::Apr, 22);
value_type v(toml::table{{"a", d}});
CHECK_EQ(d, toml::try_find<local_date_type>(v, "a").unwrap());
toml::try_find<local_date_type>(v, "a").unwrap().year = 2017;
d.year = 2017;
CHECK_EQ(d, toml::try_find<local_date_type>(v, "a").unwrap());
local_date_type x = toml::try_find<local_date_type>(std::move(v), "a").unwrap();
CHECK_EQ(d, x);
}
{
local_time_type t(12, 30, 45);
value_type v(toml::table{{"a", t}});
CHECK_EQ(t, toml::try_find<local_time_type>(v, "a").unwrap());
toml::try_find<local_time_type>(v, "a").unwrap().hour = 9;
t.hour = 9;
CHECK_EQ(t, toml::try_find<local_time_type>(v, "a").unwrap());
local_time_type x = toml::try_find<local_time_type>(std::move(v), "a").unwrap();
CHECK_EQ(t, x);
}
{
local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45));
value_type v(toml::table{{"a", dt}});
CHECK_EQ(dt, toml::try_find<local_datetime_type>(v, "a").unwrap());
toml::try_find<local_datetime_type>(v, "a").unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_find<local_datetime_type>(v, "a").unwrap());
toml::local_datetime x = toml::try_find<local_datetime_type>(std::move(v), "a").unwrap();
CHECK_EQ(dt, x);
}
{
offset_datetime_type dt(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45)), toml::time_offset(9, 0));
value_type v(toml::table{{"a", dt}});
CHECK_EQ(dt, toml::try_find<offset_datetime_type>(v, "a").unwrap());
toml::try_find<toml::offset_datetime>(v, "a").unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_find<offset_datetime_type>(v, "a").unwrap());
offset_datetime_type x = toml::try_find<offset_datetime_type>(std::move(v), "a").unwrap();
CHECK_EQ(dt, x);
}
{
array_type vec;
vec.push_back(value_type(42));
vec.push_back(value_type(54));
value_type v(toml::table{{"a", vec}});
CHECK_EQ(vec, toml::try_find<array_type>(v, "a").unwrap());
toml::try_find<array_type>(v, "a").unwrap().push_back(value_type(123));
vec.push_back(value_type(123));
CHECK_EQ(vec, toml::try_find<array_type>(v, "a").unwrap());
array_type x = toml::try_find<array_type>(std::move(v), "a").unwrap();
CHECK_EQ(vec, x);
}
{
table_type tab;
tab["key1"] = value_type(42);
tab["key2"] = value_type(3.14);
value_type v(toml::table{{"a", tab}});
CHECK_EQ(tab, toml::try_find<table_type>(v, "a").unwrap());
toml::try_find<table_type>(v, "a").unwrap()["key3"] = value_type(123);
tab["key3"] = value_type(123);
CHECK_EQ(tab, toml::try_find<table_type>(v, "a").unwrap());
table_type x = toml::try_find<table_type>(std::move(v), "a").unwrap();
CHECK_EQ(tab, x);
}
{
value_type v1(toml::table{{"a", 42}});
CHECK_EQ(toml::value(42), toml::try_find(v1, "a").unwrap());
value_type v2(54);
toml::try_find(v1, "a").unwrap() = v2;
CHECK_EQ(v2, toml::try_find(v1, "a").unwrap());
value_type x = toml::try_find(std::move(v1), "a").unwrap();
CHECK_EQ(v2, x);
}
}
TEST_CASE("testing try_find fails")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
// -----------------------------------------------------------------------
// const-reference version
{
// value is not a table
const toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not the expected type
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not found
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(v, "different_key").is_err());
}
{
// the positive control.
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(v, "key").is_ok());
}
// -----------------------------------------------------------------------
// reference version
{
// value is not a table
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(v, "different_key").is_err());
}
{
// the positive control.
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(v, "key").is_ok());
}
// -----------------------------------------------------------------------
// move version
{
// value is not a table
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), "key").is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), "key").is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(std::move(v), "different_key").is_err());
}
{
// the positive control.
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(std::move(v), "key").is_ok());
}
}
TEST_CASE("testing toml::try_find(v, idx) throws")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
// -----------------------------------------------------------------------
// const-reference version
{
// value is not an array
const toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not the expected type
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not found
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(v, 6).is_err());
}
{
// the positive control.
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(v, 2).is_ok());
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(v, 6).is_err());
}
{
// the positive control.
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(v, 2).is_ok());
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), 0).is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), 0).is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(std::move(v), 6).is_err());
}
{
// the positive control.
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(std::move(v), 2).is_ok());
}
}
TEST_CASE("testing toml::try_find with recursive table/array")
{
using value_type = toml::value;
using integer_type = typename value_type::integer_type ;
// recursively search tables
{
toml::value v = toml::table{
{"a", toml::table{
{"b", toml::table{
{"c", toml::table{
{"d", 42}
}}
}}
}}
};
CHECK_EQ(42, toml::try_find<int>(v, "a", "b", "c", "d").unwrap());
// reference that can be used to modify the content
auto num = toml::try_find<integer_type>(v, "a", "b", "c", "d");
CHECK_UNARY(num.is_ok());
num.unwrap() = 54;
CHECK_EQ(54, toml::try_find<int>(v, "a", "b", "c", "d").unwrap());
const std::string a("a"), b("b"), c("c"), d("d");
auto num2 = toml::try_find<integer_type>(v, a, b, c, d);
CHECK_UNARY(num2.is_ok());
num2.unwrap() = 42;
CHECK_EQ(42, toml::try_find<int>(v, a, b, c, d).unwrap());
auto num3 = toml::try_find<integer_type>(v, a, "b", c, "d");
CHECK_EQ(42, num3.unwrap());
auto num4 = toml::try_find<integer_type>(std::move(v), a, b, c, d);
CHECK_EQ(42, num4.unwrap());
}
// recursively search arrays
{
toml::value v = toml::array{
toml::array{"array", "of", "string"},
toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}}
};
CHECK_EQ("array" , toml::try_find<std::string>(v, 0, 0).unwrap());
CHECK_EQ("of" , toml::try_find<std::string>(v, 0, 1).unwrap());
CHECK_EQ("string", toml::try_find<std::string>(v, 0, 2).unwrap());
CHECK_EQ(1, toml::try_find<int>(v, 1, 0, 0).unwrap());
CHECK_EQ(2, toml::try_find<int>(v, 1, 0, 1).unwrap());
CHECK_EQ(3, toml::try_find<int>(v, 1, 0, 2).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, 1, 1, 0).unwrap());
CHECK_EQ(2.71, toml::try_find<double>(v, 1, 1, 1).unwrap());
// reference that can be used to modify the content
auto num = toml::try_find<integer_type>(v, 1, 0, 2);
num.unwrap() = 42;
CHECK_EQ( 1, toml::try_find<int>(v, 1, 0, 0).unwrap());
CHECK_EQ( 2, toml::try_find<int>(v, 1, 0, 1).unwrap());
CHECK_EQ(42, toml::try_find<int>(v, 1, 0, 2).unwrap());
// move value
auto num2 = toml::try_find<integer_type>(std::move(v), 1, 0, 2);
CHECK_EQ(42, num2.unwrap());
}
// recursively search mixtures
{
toml::value v = toml::table{{"array", toml::array{
toml::array{1, 2, 3},
toml::array{
toml::table{{"foo", "bar"}, {"baz", "qux"}},
toml::table{{"pi", 3.14}, {"e", 2.71}}
}}
}};
CHECK_EQ(1, toml::try_find<int>(v, "array", 0, 0).unwrap());
CHECK_EQ(2, toml::try_find<int>(v, "array", 0, 1).unwrap());
CHECK_EQ(3, toml::try_find<int>(v, "array", 0, 2).unwrap());
CHECK_EQ("bar", toml::try_find<std::string>(v, "array", 1, 0, "foo").unwrap());
CHECK_EQ("qux", toml::try_find<std::string>(v, "array", 1, 0, "baz").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, "pi").unwrap());
CHECK_EQ(2.71, toml::try_find<double>(v, "array", 1, 1, "e" ).unwrap());
const std::string ar("array");
const auto ar_c = "array";
const std::string pi("pi");
const auto pi_c = "pi";
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, "pi").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, pi_c).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, "pi").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, pi_c).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, pi_c).unwrap());
}
}
TEST_CASE("testing toml::try_find integer conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", 42}};
CHECK_EQ(int(42) , toml::try_find<int >(v, "key").unwrap());
CHECK_EQ(short(42) , toml::try_find<short >(v, "key").unwrap());
CHECK_EQ(char(42) , toml::try_find<char >(v, "key").unwrap());
CHECK_EQ(unsigned(42) , toml::try_find<unsigned >(v, "key").unwrap());
CHECK_EQ(long(42) , toml::try_find<long >(v, "key").unwrap());
CHECK_EQ(std::int64_t(42) , toml::try_find<std::int64_t >(v, "key").unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_find<std::uint64_t>(v, "key").unwrap());
CHECK_EQ(std::int16_t(42) , toml::try_find<std::int16_t >(v, "key").unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_find<std::uint16_t>(v, "key").unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_find<std::uint16_t>(std::move(v), "key").unwrap());
}
}
TEST_CASE("testing toml::try_find floating conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", 3.14}};
const double ref(3.14);
CHECK_EQ(static_cast<float >(ref), toml::try_find<float >(v, "key").unwrap());
CHECK_EQ( ref , toml::try_find<double >(v, "key").unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_find<long double>(v, "key").unwrap());
CHECK_EQ(static_cast<float >(ref), toml::try_find<float >(std::move(v), "key").unwrap());
}
}
TEST_CASE("testing toml::try_find string conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", "foo"}};
CHECK_EQ("foo", toml::try_find<std::string>(v, "key").unwrap());
toml::try_find<std::string>(v, "key").unwrap() += "bar";
CHECK_EQ("foobar", toml::try_find<std::string>(v, "key").unwrap());
}
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
{
value_type v = toml::table{{"key", "foo"}};
CHECK_EQ("foo", toml::try_find<std::string_view>(v, "key").unwrap());
}
#endif
}
TEST_CASE("testing toml::try_find array conversion")
{
using value_type = toml::value;
value_type v = toml::table{{"key", toml::array{42, 54, 69, 72}}};
const std::vector<int> vec = toml::try_find<std::vector<int>>(v, "key").unwrap();
const std::list<short> lst = toml::try_find<std::list<short>>(v, "key").unwrap();
const std::deque<std::int64_t> deq = toml::try_find<std::deque<std::int64_t>>(v, "key").unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_find<std::array<int, 4>>(v, "key").unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_find<std::tuple<int, short, unsigned, long>>(v, "key").unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
value_type p = toml::table{{"key", toml::array{3.14, 2.71}}};
std::pair<double, double> pr = toml::try_find<std::pair<double, double> >(p, "key").unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
TEST_CASE("testing toml::try_find array move conversion")
{
using value_type = toml::value;
value_type v1 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v2 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v3 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v4 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v5 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
const std::vector<int> vec = toml::try_find<std::vector<int>>(std::move(v1), "key").unwrap();
const std::list<short> lst = toml::try_find<std::list<short>>(std::move(v2), "key").unwrap();
const std::deque<std::int64_t> deq = toml::try_find<std::deque<std::int64_t>>(std::move(v3), "key").unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_find<std::array<int, 4>>(std::move(v4), "key").unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key").unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
value_type p = toml::table{{"key", toml::array{3.14, 2.71}}};
std::pair<double, double> pr = toml::try_find<std::pair<double, double> >(std::move(p), "key").unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
TEST_CASE("testing toml::try_find array of array conversion")
{
using value_type = toml::value;
value_type v1 = toml::array{42, 54, 69, 72};
value_type v2 = toml::array{"foo", "bar", "baz"};
value_type v = toml::table{{"key", toml::array{v1, v2}}};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_find<std::pair<std::vector<int>, std::vector<std::string>>>(v, "key").unwrap();
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_find<std::tuple<std::vector<int>, std::vector<std::string>>>(v, "key").unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
TEST_CASE("testing toml::try_find array of array move conversion")
{
using value_type = toml::value;
value_type a1 = toml::array{42, 54, 69, 72};
value_type a2 = toml::array{"foo", "bar", "baz"};
value_type v1 = toml::table{{"key", toml::array{a1, a2}}};
value_type v2 = toml::table{{"key", toml::array{a1, a2}}};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_find<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v1), "key").unwrap();
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_find<std::tuple<std::vector<int>, std::vector<std::string>>>(std::move(v2), "key").unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
TEST_CASE("testing toml::try_find table conversion")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::table{
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::try_find<std::map<std::string, int>>(v1, "key").unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
{
value_type v1 = toml::table{{"key", toml::table{
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::try_find<std::map<std::string, int>>(std::move(v1), "key").unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
}
TEST_CASE("testing toml::try_find local_date")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
{
value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
}
TEST_CASE("testing toml::try_find local_time")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::try_find<std::chrono::seconds>(v1, "key").unwrap();
CHECK_EQ(time, std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
{
value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::try_find<std::chrono::seconds>(std::move(v1), "key").unwrap();
CHECK_EQ(time, std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
}
TEST_CASE("testing toml::try_find local_datetime")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45})}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
{
value_type v1 = toml::table{{"key", toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45})}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
}
TEST_CASE("testing toml::try_find offset_datetime")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{9, 0})}};
// 2018-04-01T12:30:00+09:00
//, 2018-04-01T03:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 3);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0})}};
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0})}};
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
}

529
tests/test_try_get.cpp Normal file
View File

@@ -0,0 +1,529 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "utility.hpp"
#include <toml11/value.hpp>
#include <toml11/try_get.hpp>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::try_get with toml types")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
{
value_type v(true);
CHECK_EQ(true, toml::try_get<boolean_type>(v).unwrap());
toml::try_get<boolean_type>(v).unwrap() = false;
CHECK_EQ(false, toml::try_get<boolean_type>(v).unwrap());
boolean_type x = toml::try_get<boolean_type>(std::move(v)).unwrap();
CHECK_EQ(false, x);
}
{
value_type v(42);
CHECK_EQ(integer_type(42), toml::try_get<integer_type>(v).unwrap());
toml::try_get<integer_type>(v).unwrap() = 54;
CHECK_EQ(integer_type(54), toml::try_get<integer_type>(v).unwrap());
integer_type x = toml::try_get<integer_type>(std::move(v)).unwrap();
CHECK_EQ(integer_type(54), x);
}
{
value_type v(3.14);
CHECK_EQ(floating_type(3.14), toml::try_get<floating_type>(v).unwrap());
toml::try_get<floating_type>(v).unwrap() = 2.71;
CHECK_EQ(floating_type(2.71), toml::try_get<floating_type>(v).unwrap());
floating_type x = toml::try_get<floating_type>(std::move(v)).unwrap();
CHECK_EQ(floating_type(2.71), x);
}
{
value_type v("foo");
CHECK_EQ("foo", toml::try_get<string_type>(v).unwrap());
toml::try_get<string_type>(v).unwrap() += "bar";
CHECK_EQ("foobar", toml::try_get<string_type>(v).unwrap());
string_type x = toml::try_get<string_type>(std::move(v)).unwrap();
CHECK_EQ("foobar", x);
}
{
local_date_type d(2018, toml::month_t::Apr, 22);
value_type v(d);
CHECK_EQ(d, toml::try_get<local_date_type>(v).unwrap());
toml::try_get<local_date_type>(v).unwrap().year = 2017;
d.year = 2017;
CHECK_EQ(d, toml::try_get<local_date_type>(v).unwrap());
local_date_type x = toml::try_get<local_date_type>(std::move(v)).unwrap();
CHECK_EQ(d, x);
}
{
local_time_type t(12, 30, 45);
value_type v(t);
CHECK_EQ(t, toml::try_get<local_time_type>(v).unwrap());
toml::try_get<local_time_type>(v).unwrap().hour = 9;
t.hour = 9;
CHECK_EQ(t, toml::try_get<local_time_type>(v).unwrap());
local_time_type x = toml::try_get<local_time_type>(std::move(v)).unwrap();
CHECK_EQ(t, x);
}
{
local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45));
value_type v(dt);
CHECK_EQ(dt, toml::try_get<local_datetime_type>(v).unwrap());
toml::try_get<local_datetime_type>(v).unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_get<local_datetime_type>(v).unwrap());
toml::local_datetime x = toml::try_get<local_datetime_type>(std::move(v)).unwrap();
CHECK_EQ(dt, x);
}
{
offset_datetime_type dt(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45)), toml::time_offset(9, 0));
value_type v(dt);
CHECK_EQ(dt, toml::try_get<offset_datetime_type>(v).unwrap());
toml::try_get<toml::offset_datetime>(v).unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_get<offset_datetime_type>(v).unwrap());
offset_datetime_type x = toml::try_get<offset_datetime_type>(std::move(v)).unwrap();
CHECK_EQ(dt, x);
}
{
array_type vec;
vec.push_back(value_type(42));
vec.push_back(value_type(54));
value_type v(vec);
CHECK_EQ(vec, toml::try_get<array_type>(v).unwrap());
toml::try_get<array_type>(v).unwrap().push_back(value_type(123));
vec.push_back(value_type(123));
CHECK_EQ(vec, toml::try_get<array_type>(v).unwrap());
array_type x = toml::try_get<array_type>(std::move(v)).unwrap();
CHECK_EQ(vec, x);
}
{
table_type tab;
tab["key1"] = value_type(42);
tab["key2"] = value_type(3.14);
value_type v(tab);
CHECK_EQ(tab, toml::try_get<table_type>(v).unwrap());
toml::try_get<table_type>(v).unwrap()["key3"] = value_type(123);
tab["key3"] = value_type(123);
CHECK_EQ(tab, toml::try_get<table_type>(v).unwrap());
table_type x = toml::try_get<table_type>(std::move(v)).unwrap();
CHECK_EQ(tab, x);
}
{
value_type v1(42);
CHECK_EQ(v1, toml::try_get<value_type>(v1).unwrap());
value_type v2(54);
toml::try_get<value_type>(v1).unwrap() = v2;
CHECK_EQ(v2, toml::try_get<value_type>(v1).unwrap());
value_type x = toml::try_get<value_type>(std::move(v1)).unwrap();
CHECK_EQ(v2, x);
}
}
TEST_CASE("testing toml::try_get<integer-like>")
{
using value_type = toml::value;
{
value_type v(42);
CHECK_EQ(int(42), toml::try_get<int >(v).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(v).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(v).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(v).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(v).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(v).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(v).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(v).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(v).unwrap());
CHECK_EQ(int(42), toml::try_get<int >(as_const(v)).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(as_const(v)).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(as_const(v)).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(as_const(v)).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(as_const(v)).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(as_const(v)).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(as_const(v)).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(as_const(v)).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(as_const(v)).unwrap());
value_type v1(v);
value_type v2(v);
value_type v3(v);
value_type v4(v);
value_type v5(v);
value_type v6(v);
value_type v7(v);
value_type v8(v);
value_type v9(v);
CHECK_EQ(int(42), toml::try_get<int >(v1).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(v2).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(v3).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(v4).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(v5).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(v6).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(v7).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(v8).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(v9).unwrap());
}
}
TEST_CASE("testing toml::try_get<floating-like>")
{
using value_type = toml::value;
{
const double ref(3.14);
value_type v(ref);
CHECK_EQ(static_cast<float >(ref), toml::try_get<float >(v).unwrap());
CHECK_EQ( ref , toml::try_get<double >(v).unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_get<long double>(v).unwrap());
value_type v1(ref);
value_type v2(ref);
value_type v3(ref);
CHECK_EQ(static_cast<float >(ref), toml::try_get<float >(std::move(v1)).unwrap());
CHECK_EQ( ref , toml::try_get<double >(std::move(v2)).unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_get<long double>(std::move(v3)).unwrap());
}
}
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
TEST_CASE("testing toml::try_get<string-like>")
{
using value_type = toml::value;
{
value_type v("foo");
CHECK_EQ("foo", toml::try_get<std::string_view>(v).unwrap());
}
}
#endif
TEST_CASE("testing toml::try_get<array-like>")
{
using value_type = toml::value;
{
const value_type v(toml::array{42, 54, 69, 72});
const std::vector<int> vec = toml::try_get<std::vector<int>>(v).unwrap();
const std::list<short> lst = toml::try_get<std::list<short>>(v).unwrap();
const std::deque<std::int64_t> deq = toml::try_get<std::deque<std::int64_t>>(v).unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_get<std::array<int, 4>>(v).unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_get<std::tuple<int, short, unsigned, long>>(v).unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
const value_type p(toml::array{3.14, 2.71});
std::pair<double, double> pr = toml::try_get<std::pair<double, double> >(p).unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::vector<int> vec = toml::try_get<std::vector<int>>(std::move(v)).unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::deque<int> deq = toml::try_get<std::deque<int>>(std::move(v)).unwrap();
CHECK_EQ(42, deq.at(0));
CHECK_EQ(54, deq.at(1));
CHECK_EQ(69, deq.at(2));
CHECK_EQ(72, deq.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::list<int> lst = toml::try_get<std::list<int>>(std::move(v)).unwrap();
std::list<int>::const_iterator iter = lst.begin();
CHECK_EQ(42, *(iter++));
CHECK_EQ(54, *(iter++));
CHECK_EQ(69, *(iter++));
CHECK_EQ(72, *(iter++));
}
{
value_type v(toml::array{42, 54, 69, 72});
std::array<int, 4> ary = toml::try_get<std::array<int, 4>>(std::move(v)).unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
std::tuple<int, short, unsigned, long> tpl =
toml::try_get<std::tuple<int, short, unsigned, long>>(std::move(v)).unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
}
}
TEST_CASE("testing toml::try_get<array-of-arrays>")
{
using value_type = toml::value;
{
const value_type v1(toml::array{42, 54, 69, 72});
const value_type v2(toml::array{"foo", "bar", "baz"});
const value_type v (toml::array{v1, v2});
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_get<std::pair<std::vector<int>, std::vector<std::string>>>(v).unwrap();
CHECK_EQ(p.first.size(), 4u);
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.size(), 3u);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_get<std::tuple<std::vector<int>, std::vector<std::string>>>(v).unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
{
const value_type v1(toml::array{42, 54, 69, 72});
const value_type v2(toml::array{"foo", "bar", "baz"});
value_type v (toml::array{v1, v2});
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_get<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v)).unwrap();
CHECK_EQ(p.first.size(), 4u);
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.size(), 3u);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
}
}
TEST_CASE("testing toml::try_get<table-like>")
{
using value_type = toml::value;
{
const value_type v1(toml::table{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
});
const auto v = toml::try_get<std::map<std::string, int>>(v1).unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
{
value_type v1(toml::table{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
});
const auto v = toml::try_get<std::map<std::string, int>>(std::move(v1)).unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
}
TEST_CASE("testing toml::try_get<time_point>(local_date)")
{
using value_type = toml::value;
value_type v1(toml::local_date{2018, toml::month_t::Apr, 1});
const auto date = std::chrono::system_clock::to_time_t(
toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
TEST_CASE("testing toml::try_get<duration>")
{
using value_type = toml::value;
value_type v1(toml::local_time{12, 30, 45});
const auto time = toml::try_get<std::chrono::seconds>(v1).unwrap();
const bool result = time == std::chrono::hours(12) +
std::chrono::minutes(30) +
std::chrono::seconds(45);
CHECK_UNARY(result);
}
TEST_CASE("testing toml::try_get<time_point>(local_datetime)")
{
using value_type = toml::value;
value_type v1(toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45}));
const auto date = std::chrono::system_clock::to_time_t(
toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
TEST_CASE("testing toml::try_get<time_point>(offset_datetime)")
{
using value_type = toml::value;
{
value_type v1(toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{9, 0}));
// 2018-04-01T12:30:00+09:00
// == 2018-04-01T03:30:00Z
const auto date = toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 3);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1(toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0}));
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
}