// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_GET_HPP #define TOML11_GET_HPP #include "from.hpp" #include "result.hpp" #include "value.hpp" #include namespace toml { // ============================================================================ // exact toml::* type template::value, std::nullptr_t>::type = nullptr> inline T& get(value& v) { return v.cast::value>(); } template::value, std::nullptr_t>::type = nullptr> inline T const& get(const value& v) { return v.cast::value>(); } template::value, std::nullptr_t>::type = nullptr> inline T&& get(value&& v) { return std::move(v.cast::value>()); } // ============================================================================ // T == toml::value; identity transformation. template::value, std::nullptr_t>::type = nullptr> inline T& get(value& v) { return v; } template::value, std::nullptr_t>::type = nullptr> inline T const& get(const value& v) { return v; } template::value, std::nullptr_t>::type = nullptr> inline T&& get(value&& v) { return std::move(v); } // ============================================================================ // integer convertible from toml::Integer template, // T is integral detail::negation>, // but not bool detail::negation> // but not toml::integer >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } // ============================================================================ // floating point convertible from toml::Float template, // T is floating_point detail::negation> // but not toml::Float >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } // ============================================================================ // std::string; toml uses its own toml::string, but it should be convertible to // std::string seamlessly template::value, std::nullptr_t>::type = nullptr> inline std::string& get(value& v) { return v.cast().str; } template::value, std::nullptr_t>::type = nullptr> inline std::string const& get(const value& v) { return v.cast().str; } template::value, std::nullptr_t>::type = nullptr> inline std::string get(value&& v) { return std::move(v.cast().str); } // ============================================================================ // std::string_view #if __cplusplus >= 201703L template::value, std::nullptr_t>::type = nullptr> inline std::string_view get(const value& v) { return std::string_view(v.cast().str); } #endif // ============================================================================ // std::chrono::duration from toml::local_time. template::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.cast())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { switch(v.type()) { case value_t::LocalDate: { return std::chrono::system_clock::time_point( v.cast()); } case value_t::LocalDatetime: { return std::chrono::system_clock::time_point( v.cast()); } default: { return std::chrono::system_clock::time_point( v.cast()); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. template, // T is container detail::has_resize_method, // T::resize(N) works detail::negation> // but not toml::array >::value, std::nullptr_t>::type = nullptr> T get(const value& v); template, // T is container detail::negation>, // no T::resize() exists detail::negation> // not toml::array >::value, std::nullptr_t>::type = nullptr> T get(const value& v); template::value, std::nullptr_t>::type = nullptr> T get(const value& v); template::value, std::nullptr_t>::type = nullptr> T get(const value& v); template, // T is map detail::negation> // but not toml::table >::value, std::nullptr_t>::type = nullptr> T get(const toml::value& v); template>, // not a toml::value detail::has_from_toml_method, // but has from_toml(toml::value) memfn std::is_default_constructible // and default constructible >::value, std::nullptr_t>::type = nullptr> T get(const toml::value& v); template> // not a toml::value >::value, std::nullptr_t>::type = nullptr, std::size_t = sizeof(::toml::from) // and has from specialization > T get(const toml::value& v); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template, // T is container detail::has_resize_method, // T::resize(N) works detail::negation> // but not toml::array >::value, std::nullptr_t>::type> T get(const value& v) { using value_type = typename T::value_type; const auto& ar = v.cast(); T container; container.resize(ar.size()); std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); return container; } // ============================================================================ // array-like types; but does not have resize(); most likely std::array. template, // T is container detail::negation>, // no T::resize() exists detail::negation> // not toml::array >::value, std::nullptr_t>::type> T get(const value& v) { using value_type = typename T::value_type; const auto& ar = v.cast(); T container; if(ar.size() != container.size()) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified container size is ", container.size(), " but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); } std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); return container; } // ============================================================================ // std::pair. template::value, std::nullptr_t>::type> T get(const value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.cast(); if(ar.size() != 2) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::pair but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); } // ============================================================================ // std::tuple. namespace detail { template T get_tuple_impl(const toml::array& a, index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template::value, std::nullptr_t>::type> T get(const value& v) { const auto& ar = v.cast(); if(ar.size() != std::tuple_size::value) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::tuple with ", std::tuple_size::value, "elements, but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); } // ============================================================================ // map-like types; most likely STL map, like std::map or std::unordered_map. template, // T is map detail::negation> // but not toml::table >::value, std::nullptr_t>::type> T get(const toml::value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; static_assert(std::is_convertible::value, "toml::get only supports map type of which key_type is " "convertible from std::string."); T map; for(const auto& kv : v.cast()) { map[key_type(kv.first)] = ::toml::get(kv.second); } return map; } // ============================================================================ // user-defined, but compatible types. template>, // not a toml::value detail::has_from_toml_method, // but has from_toml(toml::value) memfn std::is_default_constructible // and default constructible >::value, std::nullptr_t>::type> T get(const toml::value& v) { T ud; ud.from_toml(v); return ud; } template> // not a toml::value >::value, std::nullptr_t>::type, std::size_t> // and has from T get(const toml::value& v) { return ::toml::from::from_toml(v); } // ============================================================================ // find and get template decltype(::toml::get(std::declval())) find(const toml::table& tab, const toml::key& ky, std::string tablename = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string("[error] key \"", ky, "\" not found in ", tablename)); } return ::toml::get(tab.at(ky)); } template decltype(::toml::get(std::declval<::toml::value&>())) find(toml::table& tab, const toml::key& ky, std::string tablename = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string("[error] key \"", ky, "\" not found in ", tablename)); } return ::toml::get(tab[ky]); } template decltype(::toml::get(std::declval<::toml::value&&>())) find(toml::table&& tab, const toml::key& ky, std::string tablename = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string("[error] key \"", ky, "\" not found in ", tablename)); } return ::toml::get(std::move(tab[ky])); } template decltype(::toml::get(std::declval())) find(const toml::value& v, const toml::key& ky) { const auto& tab = ::toml::get(v); if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( "[error] key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } return ::toml::get(tab.at(ky)); } template decltype(::toml::get(std::declval<::toml::value&>())) find(toml::value& v, const toml::key& ky) { auto& tab = ::toml::get(v); if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( "[error] key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } return ::toml::get(tab.at(ky)); } template decltype(::toml::get(std::declval<::toml::value&&>())) find(toml::value&& v, const toml::key& ky) { auto tab = ::toml::get(std::move(v)); if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( "[error] key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } return ::toml::get(std::move(tab[ky])); } // ============================================================================ // get_or(value, fallback) // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template::value, std::nullptr_t>::type = nullptr> T const& get_or(const toml::value& v, const T& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } template::value, std::nullptr_t>::type = nullptr> T& get_or(toml::value& v, T& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } template::value, std::nullptr_t>::type = nullptr> T&& get_or(toml::value&& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } // ---------------------------------------------------------------------------- // specialization for std::string (return type becomes lvalue ref) template::value, std::nullptr_t>::type = nullptr> std::string const& get_or(const toml::value& v, const T& opt) { try { return get(v); } catch(...) { return opt; } } template::value, std::nullptr_t>::type = nullptr> std::string& get_or(toml::value& v, T& opt) { try { return get(v); } catch(...) { return opt; } } template::value, std::nullptr_t>::type = nullptr> std::string get_or(toml::value&& v, T&& opt) { try { return get(v); } catch(...) { return opt; } } template::type>::value, std::nullptr_t>::type = nullptr> std::string get_or(const toml::value& v, T&& opt) { try { return get(v); } catch(...) { return std::string(opt); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template>, detail::negation>, detail::negation::type>> >::value, std::nullptr_t>::type = nullptr> T get_or(const toml::value& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } // =========================================================================== // find_or(value, key, fallback) // --------------------------------------------------------------------------- // exact types (return type can be a reference) template::value, std::nullptr_t>::type = nullptr> T const& find_or(const toml::value& v, const toml::key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = toml::get(v); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template::value, std::nullptr_t>::type = nullptr> T& find_or(toml::value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = toml::get(v); if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template::value, std::nullptr_t>::type = nullptr> T&& find_or(toml::value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} auto tab = toml::get(std::move(v)); if(tab.count(ky) == 0) {return opt;} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // std::string (return type can be a reference) template::value, std::nullptr_t>::type = nullptr> std::string const& find_or(const toml::value& v, const toml::key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = toml::get(v); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template::value, std::nullptr_t>::type = nullptr> std::string& find_or(toml::value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = toml::get(v); if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template::value, std::nullptr_t>::type = nullptr> std::string find_or(toml::value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} auto tab = toml::get(std::move(v)); if(tab.count(ky) == 0) {return opt;} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // string literal (deduced as std::string) template::type>::value, std::nullptr_t>::type = nullptr> std::string find_or(const toml::value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} const auto& tab = toml::get(v); if(tab.count(ky) == 0) {return std::string(opt);} return get_or(tab.at(ky), std::forward(opt)); } // --------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template>, detail::negation>, detail::negation::type>> >::value, std::nullptr_t>::type = nullptr> T find_or(const toml::value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} const auto& tab = toml::get(v); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), std::forward(opt)); } // =========================================================================== // find_or(table, key, opt) // --------------------------------------------------------------------------- // exact types (return type can be a reference) template::value, std::nullptr_t>::type = nullptr> T const& find_or(const toml::table& tab, const toml::key& ky, const T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template::value, std::nullptr_t>::type = nullptr> T& find_or(toml::table& tab, const toml::key& ky, T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template::value, std::nullptr_t>::type = nullptr> T&& find_or(toml::table&& tab, const toml::key& ky, T&& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // std::string (return type can be a reference) template::value, std::nullptr_t>::type = nullptr> std::string const& find_or(const toml::table& tab, const toml::key& ky, const T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template::value, std::nullptr_t>::type = nullptr> std::string& find_or(toml::table& tab, const toml::key& ky, T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template::value, std::nullptr_t>::type = nullptr> std::string find_or(toml::table&& tab, const toml::key& ky, T&& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // string literal (deduced as std::string) template::type>::value, std::nullptr_t>::type = nullptr> std::string find_or(const toml::table& tab, const toml::key& ky, T&& opt) { if(tab.count(ky) == 0) {return std::string(opt);} return get_or(tab.at(ky), std::forward(opt)); } // --------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template>, detail::negation>, detail::negation::type>> >::value, std::nullptr_t>::type = nullptr> T find_or(const toml::table& tab, const toml::key& ky, T&& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), std::forward(opt)); } // ============================================================================ // expect template result expect(const toml::value& v) noexcept { try { return ok(get(v)); } catch(const std::exception& e) { return err(e.what()); } } template result expect(const toml::value& v, const toml::key& k) noexcept { try { return ok(find(v, k)); } catch(const std::exception& e) { return err(e.what()); } } template result expect(const toml::table& t, const toml::key& k, std::string tablename = "unknown table") noexcept { try { return ok(find(t, k, std::move(tablename))); } catch(const std::exception& e) { return err(e.what()); } } } // toml #endif// TOML11_GET