Compare commits

...

19 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
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
a0ae1a6bfd fix: access members directly 2024-06-26 22:42:32 +09:00
ToruNiina
044a66210d doc: add reference about try_at 2024-06-26 00:34:54 +09:00
ToruNiina
c4fb41b812 doc: add try_at to docs/features 2024-06-26 00:24:25 +09:00
ToruNiina
a610e75df0 test: add test of try_at for array and table 2024-06-26 00:09:08 +09:00
ToruNiina
0e84d0591b feat: add toml::basic_value::try_at 2024-06-25 23:50:28 +09:00
19 changed files with 2691 additions and 152 deletions

View File

@@ -92,6 +92,32 @@ std::cout << v.at(1);
If the stored type is not `array_type`, a `type_error` is thrown.
### `try_at(std::size_t i)`
Performs the same operation as `at(i)`, but instead of throwing an exception on failure, it always returns a [`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}}). It does not throw an exception on failure.
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
std::cout << res1.unwrap() << std::endl;
auto res5 = v.try_at(5);
assert(res1.is_err());
std::cout << toml::format_error(res1.unwrap_err()) << std::endl;
```
Additionally, since this `toml::result` holds a reference, it is possible to update the value.
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
res1.unwrap() = 42;
```
#### `at(std::string key)`, `operator[](std::string key)`
These are equivalent to `as_table().at(key)` and `as_table()[key]`.
@@ -105,6 +131,32 @@ v["a"] = 42;
If the stored type is not `table_type`, a `type_error` is thrown.
### `try_at(std::string key)`
Performs the same operation as `at(key)`, but instead of throwing an exception on failure, it always returns a [`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}}). It does not throw an exception on failure.
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
std::cout << res_a.unwrap() << std::endl;
auto res_c = v.try_at("c");
assert(res_c.is_err());
std::cout << toml::format_error(res_c.unwrap_err()) << std::endl;
```
Additionally, since this `toml::result` holds a reference, it is possible to update the value.
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
res_a.unwrap() = 6 * 9;
```
#### `size()`
Returns the length.

View File

@@ -585,6 +585,27 @@ Throws `std::out_of_range` if the `table` does not contain the specified element
-----
### `try_at(key)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const key_type& key) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const key_type& key) const noexcept;
```
#### Return Value
After casting the current `value` to a `table`, it returns the element specified by the `key`.
If successful, it returns a `reference_wrapper` holding a reference to that element.
If unsuccessful, it returns an `error_info` corresponding to `type_error` or `out_of_range`.
#### Exceptions
Does not throw.
-----
#### `operator[](key)`
```cpp
@@ -652,6 +673,26 @@ Throws `toml::type_error` if the stored value is not an `array`.
Throws `std::out_of_range` if the specified element does not exist in the `array`.
-----
### `try_at(idx)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const std::size_t idx) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const std::size_t idx) const noexcept;
```
#### Return Value
After casting the current `value` to an `array`, it returns the element specified by the `idx`.
If successful, it returns a `reference_wrapper` holding a reference to that element.
If unsuccessful, it returns an `error_info` corresponding to `type_error` or `out_of_range`.
#### Exceptions
Does not throw.
-----

View File

@@ -96,6 +96,32 @@ std::cout << v.at(1);
格納している型が `array_type` ではなかった場合、 `type_error` を送出します。
#### `try_at(std::size_t i)`
`at(i)`と同様の操作をしますが、失敗時に例外を投げる代わりに、常に[`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}})を返します。失敗時に例外は投げません。
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
std::cout << res1.unwrap() << std::endl;
auto res5 = v.try_at(5);
assert(res1.is_err());
std::cout << toml::format_error(res1.unwrap_err()) << std::endl;
```
また、この`toml::result`は成功値として参照を持つので、値を更新することも可能です。
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
res1.unwrap() = 42;
```
#### `at(std::string key)`, `operator[](std::string key)`
`as_table().at(key)`, `as_table()[key]` と同等です。
@@ -111,6 +137,32 @@ v["a"] = 42;
格納している型が `table_type` ではなかった場合、 `type_error` を送出します。
#### `try_at(std::string key)`
`at(key)`と同様の操作をしますが、失敗時に例外を投げる代わりに、常に[`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}})を返します。失敗時に例外は投げません。
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
std::cout << res_a.unwrap() << std::endl;
auto res_c = v.try_at("c");
assert(res_c.is_err());
std::cout << toml::format_error(res_c.unwrap_err()) << std::endl;
```
また、この`toml::result`は成功値として参照を持つので、値を更新することも可能です。
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
res_a.unwrap() = 6 * 9;
```
#### `size()`
長さを返します。

View File

@@ -583,6 +583,27 @@ value_type const& at(const key_type& key) const;
-----
### `try_at(key)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const key_type& key) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const key_type& key) const noexcept;
```
#### 戻り値
今の`value``table`にキャストしたあと、`key`によって指定される要素を返します。
成功した場合、その要素への参照を持つ`reference_wrapper`を返します。
失敗した場合、 `type_error` または `out_of_range` に対応する `error_info` を返します。
#### 例外
投げません。
-----
#### `operator[](key)`
```cpp
@@ -652,6 +673,27 @@ value_type const& at(const std::size_t idx) const;
-----
### `try_at(idx)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const std::size_t idx) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const std::size_t idx) const noexcept;
```
#### 戻り値
今の`value``array`にキャストしたあと、`idx`によって指定される要素を返します。
成功した場合、その要素への参照を持つ`reference_wrapper`を返します。
失敗した場合、 `type_error` または `out_of_range` に対応する `error_info` を返します。
#### 例外
投げません。
-----
### `operator[](idx)`
```cpp

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,6 +64,7 @@ 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
@@ -75,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"));
}
@@ -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), {}
)});
@@ -3717,7 +3719,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
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(
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"), {}

View File

@@ -244,13 +244,13 @@ struct result
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.as_ok());
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.as_err());
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
assert(tmp == std::addressof(this->fail_));
(void)tmp;
}
@@ -259,13 +259,13 @@ struct result
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
assert(tmp == std::addressof(this->fail_));
(void)tmp;
}
@@ -276,13 +276,13 @@ struct result
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.as_ok());
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.as_err());
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
assert(tmp == std::addressof(this->fail_));
(void)tmp;
}
@@ -294,13 +294,13 @@ struct result
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
assert(tmp == std::addressof(this->fail_));
(void)tmp;
}

View File

@@ -90,6 +90,13 @@ 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>
@@ -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

@@ -50,6 +50,9 @@ error_info make_type_error(const basic_value<TC>&, const std::string&, const val
template<typename TC>
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const std::string&);
template<typename TC>
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const std::size_t);
template<typename TC>
void change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
@@ -1613,6 +1616,44 @@ class basic_value
assert(found->first == k);
return found->second;
}
result<std::reference_wrapper<value_type>, error_info>
try_at(const key_type& k) noexcept
{
if(!this->is_table())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::table));
}
auto& table = this->as_table(std::nothrow);
const auto found = table.find(k);
if(found == table.end())
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(key_type)", k));
}
assert(found->first == k);
return ok(std::ref(found->second));
}
result<std::reference_wrapper<const value_type>, error_info>
try_at(const key_type& k) const noexcept
{
if(!this->is_table())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::table));
}
const auto& table = this->as_table(std::nothrow);
const auto found = table.find(k);
if(found == table.end())
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(key_type)", k));
}
assert(found->first == k);
return ok(std::cref(found->second));
}
value_type& operator[](const key_type& k)
{
if(this->is_empty())
@@ -1688,6 +1729,41 @@ class basic_value
return ar.at(idx);
}
result<std::reference_wrapper<value_type>, error_info>
try_at(const std::size_t& idx) noexcept
{
if(!this->is_array())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::array));
}
auto& ar = this->as_array(std::nothrow);
if(ar.size() <= idx)
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(idx)", idx));
}
return ok(std::ref(ar[idx]));
}
result<std::reference_wrapper<const value_type>, error_info>
try_at(const std::size_t idx) const noexcept
{
if(!this->is_array())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::array));
}
const auto& ar = this->as_array(std::nothrow);
if(ar.size() <= idx)
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(idx)", idx));
}
return ok(std::cref(ar[idx]));
}
value_type& operator[](const std::size_t idx) noexcept
{
// no check...
@@ -2044,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)
{
@@ -2057,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>
@@ -2069,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)
@@ -2094,7 +2178,21 @@ 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)
{
if( ! v.is_array())
{
return make_type_error(v, fname, toml::value_t::array);
}
std::ostringstream oss;
oss << "actual length (" << v.as_array(std::nothrow).size()
<< ") is shorter than the specified index (" << idx << ").";
return make_error_info(error_kind::out_of_range,
fname + ": no element corresponding to the index",
v, oss.str());
}
#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \

View File

@@ -3,11 +3,13 @@ set(TOML11_TEST_NAMES
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

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);
}
}

View File

@@ -1062,21 +1062,89 @@ TEST_CASE("testing array-accessors")
{
toml::value x({true, 42, "hoge"});
// at
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_string());
CHECK_EQ(x.at(0).as_boolean(), true);
CHECK_EQ(x.at(1).as_integer(), 42);
CHECK_EQ(x.at(2).as_string(), std::string("hoge"));
CHECK_THROWS_AS(x.at(3), std::out_of_range);
CHECK_UNARY(as_const(x).at(0).is_boolean());
CHECK_UNARY(as_const(x).at(1).is_integer());
CHECK_UNARY(as_const(x).at(2).is_string());
CHECK_EQ(x.at(0).as_boolean(), true);
CHECK_EQ(x.at(1).as_integer(), 42);
CHECK_EQ(x.at(2).as_string(), std::string("hoge"));
CHECK_EQ(as_const(x).at(0).as_boolean(), true);
CHECK_EQ(as_const(x).at(1).as_integer(), 42);
CHECK_EQ(as_const(x).at(2).as_string(), std::string("hoge"));
CHECK_THROWS_AS(as_const(x).at(3), std::out_of_range);
// update through at
x.at(0) = false;
x.at(1) = 6 * 9;
x.at(2) = 3.14;
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_floating());
CHECK_EQ(x.at(0).as_boolean(), false);
CHECK_EQ(x.at(1).as_integer(), 6*9);
CHECK_EQ(x.at(2).as_floating(), 3.14);
x.at(0) = true;
x.at(1) = 42;
x.at(2) = "hoge";
// try_at
CHECK_UNARY(x.try_at(0).is_ok());
CHECK_UNARY(x.try_at(1).is_ok());
CHECK_UNARY(x.try_at(2).is_ok());
CHECK_UNARY(x.try_at(0).as_ok().is_boolean());
CHECK_UNARY(x.try_at(1).as_ok().is_integer());
CHECK_UNARY(x.try_at(2).as_ok().is_string());
CHECK_UNARY_FALSE(x.try_at(0).is_err());
CHECK_UNARY_FALSE(x.try_at(1).is_err());
CHECK_UNARY_FALSE(x.try_at(2).is_err());
CHECK_EQ(x.try_at(0).as_ok().as_boolean(), true);
CHECK_EQ(x.try_at(1).as_ok().as_integer(), 42);
CHECK_EQ(x.try_at(2).as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(x.try_at(3).is_err());
CHECK_UNARY(as_const(x).try_at(0).is_ok());
CHECK_UNARY(as_const(x).try_at(1).is_ok());
CHECK_UNARY(as_const(x).try_at(2).is_ok());
CHECK_UNARY(as_const(x).try_at(0).as_ok().is_boolean());
CHECK_UNARY(as_const(x).try_at(1).as_ok().is_integer());
CHECK_UNARY(as_const(x).try_at(2).as_ok().is_string());
CHECK_UNARY_FALSE(as_const(x).try_at(0).is_err());
CHECK_UNARY_FALSE(as_const(x).try_at(1).is_err());
CHECK_UNARY_FALSE(as_const(x).try_at(2).is_err());
CHECK_EQ(as_const(x).try_at(0).as_ok().as_boolean(), true);
CHECK_EQ(as_const(x).try_at(1).as_ok().as_integer(), 42);
CHECK_EQ(as_const(x).try_at(2).as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(as_const(x).try_at(3).is_err());
// update through try_at
x.try_at(0).as_ok() = false;
x.try_at(1).as_ok() = 6 * 9;
x.try_at(2).as_ok() = 3.14;
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_floating());
CHECK_EQ(x.at(0).as_boolean(), false);
CHECK_EQ(x.at(1).as_integer(), 6*9);
CHECK_EQ(x.at(2).as_floating(), 3.14);
x.try_at(0).as_ok() = true;
x.try_at(1).as_ok() = 42;
x.try_at(2).as_ok() = "hoge";
// operator[]
CHECK_UNARY(x[0].is_boolean());
CHECK_UNARY(x[1].is_integer());
@@ -1086,6 +1154,8 @@ TEST_CASE("testing array-accessors")
CHECK_EQ(x[1].as_integer(), 42);
CHECK_EQ(x[2].as_string(), std::string("hoge"));
// -----------------------------------------------------------------------
const toml::value v1(3.14);
toml::value v2(2.71);
@@ -1143,13 +1213,83 @@ TEST_CASE("testing table-accessors")
CHECK_EQ(x.count("d"), 0);
CHECK_EQ(x.count("e"), 0);
// at
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_string());
CHECK_EQ(x.at("a").as_boolean(), true);
CHECK_EQ(x.at("b").as_integer(), 42);
CHECK_EQ(x.at("c").as_string(), std::string("hoge"));
CHECK_THROWS_AS(x.at("d"), std::out_of_range);
CHECK_UNARY(as_const(x).at("a").is_boolean());
CHECK_UNARY(as_const(x).at("b").is_integer());
CHECK_UNARY(as_const(x).at("c").is_string());
CHECK_EQ(as_const(x).at("a").as_boolean(), true);
CHECK_EQ(as_const(x).at("b").as_integer(), 42);
CHECK_EQ(as_const(x).at("c").as_string(), std::string("hoge"));
CHECK_THROWS_AS(as_const(x).at("d"), std::out_of_range);
// rewrite using at
x.at("a") = false;
x.at("b") = 6*9;
x.at("c") = 3.14;
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_floating());
CHECK_EQ(x.at("a").as_boolean(), false);
CHECK_EQ(x.at("b").as_integer(), 6*9);
CHECK_EQ(x.at("c").as_floating(), 3.14);
x.at("a") = true;
x.at("b") = 42;
x.at("c") = "hoge";
// try_at
CHECK_UNARY(x.try_at("a").is_ok());
CHECK_UNARY(x.try_at("b").is_ok());
CHECK_UNARY(x.try_at("c").is_ok());
CHECK_UNARY(x.try_at("a").as_ok().is_boolean());
CHECK_UNARY(x.try_at("b").as_ok().is_integer());
CHECK_UNARY(x.try_at("c").as_ok().is_string());
CHECK_EQ(x.try_at("a").as_ok().as_boolean(), true);
CHECK_EQ(x.try_at("b").as_ok().as_integer(), 42);
CHECK_EQ(x.try_at("c").as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(x.try_at("d").is_err());
CHECK_UNARY(as_const(x).try_at("a").is_ok());
CHECK_UNARY(as_const(x).try_at("b").is_ok());
CHECK_UNARY(as_const(x).try_at("c").is_ok());
CHECK_UNARY(as_const(x).try_at("a").as_ok().is_boolean());
CHECK_UNARY(as_const(x).try_at("b").as_ok().is_integer());
CHECK_UNARY(as_const(x).try_at("c").as_ok().is_string());
CHECK_EQ(as_const(x).try_at("a").as_ok().as_boolean(), true);
CHECK_EQ(as_const(x).try_at("b").as_ok().as_integer(), 42);
CHECK_EQ(as_const(x).try_at("c").as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(as_const(x).try_at("d").is_err());
// rewrite using try_at
x.try_at("a").as_ok() = false;
x.try_at("b").as_ok() = 6*9;
x.try_at("c").as_ok() = 3.14;
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_floating());
CHECK_EQ(x.at("a").as_boolean(), false);
CHECK_EQ(x.at("b").as_integer(), 6*9);
CHECK_EQ(x.at("c").as_floating(), 3.14);
x.try_at("a").as_ok() = true;
x.try_at("b").as_ok() = 42;
x.try_at("c").as_ok() = "hoge";
// operator[]
CHECK_UNARY(x["a"].is_boolean());
CHECK_UNARY(x["b"].is_integer());