From 3320d25abbdb1643094fd65dcdb05a9493d35d24 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 28 Jun 2024 00:13:07 +0900 Subject: [PATCH] feat: add try_get --- include/toml11/try_get.hpp | 472 +++++++++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 include/toml11/try_get.hpp diff --git a/include/toml11/try_get.hpp b/include/toml11/try_get.hpp new file mode 100644 index 0000000..09e5a37 --- /dev/null +++ b/include/toml11/try_get.hpp @@ -0,0 +1,472 @@ +#ifndef TOML11_TRY_GET_HPP +#define TOML11_TRY_GET_HPP + +#include + +#include "error_info.hpp" +#include "from.hpp" +#include "types.hpp" +#include "value.hpp" + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif // string_view + +namespace toml +{ + +// ============================================================================ +// T is toml::value; identity transformation. + +template +cxx::enable_if_t>::value, + result, error_info>> +try_get(basic_value& v) noexcept +{ + return ok(std::ref(v)); +} + +template +cxx::enable_if_t>::value, + result, error_info>> +try_get(const basic_value& v) noexcept +{ + return ok(std::cref(v)); +} + +template +cxx::enable_if_t>::value, + result> +try_get(basic_value&& v) noexcept +{ + return ok(basic_value(std::move(v))); +} + +// ============================================================================ +// exact toml::* type + +template +cxx::enable_if_t>::value, + result, error_info>> +try_get(basic_value& v) noexcept +{ + constexpr auto ty = detail::type_to_enum>::value; + if(v.type() == ty) + { + return ok(std::ref(detail::getter::get_nothrow(v))); + } + else + { + return err(detail::make_type_error(v, "toml::try_get()", ty)); + } +} + +template +cxx::enable_if_t>::value, + result, error_info>> +try_get(const basic_value& v) noexcept +{ + constexpr auto ty = detail::type_to_enum>::value; + if(v.type() == ty) + { + return ok(std::cref(detail::getter::get_nothrow(v))); + } + else + { + return err(detail::make_type_error(v, "toml::try_get()", ty)); + } +} + +template +cxx::enable_if_t>::value, + result> +try_get(basic_value&& v) noexcept +{ + constexpr auto ty = detail::type_to_enum>::value; + if(v.type() == ty) + { + return ok(detail::getter::get_nothrow(std::move(v))); + } + else + { + return err(detail::make_type_error(v, "toml::try_get()", ty)); + } +} + +// ============================================================================ +// T is toml::basic_value + +template +cxx::enable_if_t, + cxx::negation>> + >::value, result> +try_get(basic_value v) noexcept +{ + return ok(T(std::move(v))); +} + +// ============================================================================ +// integer convertible from toml::value::integer_type + +template +cxx::enable_if_t, + cxx::negation>, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, result> +try_get(const basic_value& v) noexcept +{ + if(v.is_integer()) + { + return ok(static_cast(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 +cxx::enable_if_t, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, result> +try_get(const basic_value& v) noexcept +{ + if(v.is_floating()) + { + return ok(static_cast(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 +cxx::enable_if_t::value, + result> +try_get(const basic_value& 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 +cxx::enable_if_t::value, + result> +try_get(const basic_value& v) noexcept +{ + if(v.is_local_time()) + { + return ok(std::chrono::duration_cast( + 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 +cxx::enable_if_t::value, + result> +try_get(const basic_value& 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 +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, result> +try_get(const basic_value& 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(elem); + if(converted.is_err()) + { + return err(converted.as_err()); + } + container.push_back(std::move(converted.as_ok())); + } + return ok(container); +} + +// ============================================================================ +// std::array + +template +cxx::enable_if_t::value, result> +try_get(const basic_value& 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[i]); + if(converted.is_err()) + { + return err(converted.as_err()); + } + container[i] = std::move(converted.as_ok()); + } + return ok(container); +} + +// ============================================================================ +// std::forward_list + +template +cxx::enable_if_t::value, result> +try_get(const basic_value& 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(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 +cxx::enable_if_t::value, result> +try_get(const basic_value& 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(ar[0]); + if(first_result.is_err()) + { + return err(first_result.as_err()); + } + auto second_result = try_get(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 +struct try_get_tuple_impl +{ + template + static result invoke(const Array& a, U curr) noexcept + { + assert(I < a.size()); + using value_type = typename std::tuple_element::type; + + auto converted = try_get(a[I]); + if(converted.is_err()) + { + return err(converted.as_err()); + } + return try_get_tuple_impl::invoke(a, std::tuple_cat( + std::move(curr), std::make_tuple(std::move(converted.as_ok())))); + } +}; +template +struct try_get_tuple_impl +{ + template + static result invoke(const Array& a, T x) noexcept + { + return ok(std::move(x)); + } +}; +} // detail + +template +cxx::enable_if_t::value, result> +try_get(const basic_value& 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::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::value) + " elements in toml array.", + loc, "here")); + } + return detail::try_get_tuple_impl::value>::invoke( + ar, std::make_tuple()); +} + +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, result> +try_get(const basic_value& v) noexcept +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert( + std::is_convertible::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(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`. + +template +cxx::enable_if_t::value, result> +try_get(const basic_value& v) noexcept +{ + return ::toml::try_from::try_from_toml(v); +} + +} // toml +#endif // TOML11_TRY_GET_HPP