mirror of
https://github.com/ToruNiina/toml11.git
synced 2024-11-09 22:30:07 +00:00
feat: add try_get
This commit is contained in:
parent
8efb305e8b
commit
3320d25abb
472
include/toml11/try_get.hpp
Normal file
472
include/toml11/try_get.hpp
Normal file
@ -0,0 +1,472 @@
|
||||
#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("toml::try_get(): bad_cast to "
|
||||
"std::chrono::system_clock::time_point", loc,
|
||||
"the actual type is " + to_string(v.type())), loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 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("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("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& a, 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("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
|
Loading…
Reference in New Issue
Block a user