// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_GET #define TOML11_GET #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::chrono::duration from toml::local_time. template::value, std::nullptr_t>::type = nullptr> inline T get(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(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); // ============================================================================ // 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."), 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."), 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."), 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; } // ============================================================================ // 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"), 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"), 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"), detail::get_region(v), "in this table")); } return ::toml::get(std::move(tab[ky])); } // ============================================================================ // get_or template decltype(::toml::get::type>::type>( std::declval())) get_or(const toml::value& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return std::forward(opt); } } template decltype(::toml::get::type>::type>( std::declval())) get_or(toml::value& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return std::forward(opt); } } template decltype(::toml::get::type>::type>( std::declval())) get_or(toml::value&& v, T&& opt) { try { return get::type>::type>(std::move(v)); } catch(...) { return std::forward(opt); } } template auto get_or(const toml::table& tab, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(tab.at(ky), std::forward(opt)); } template auto get_or(toml::table& tab, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(tab[ky], std::forward(opt)); } template auto get_or(toml::table&& tab, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); } template auto get_or(const toml::value& v, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(v.type() != toml::value_t::Table){return std::forward(opt);} const auto& tab = toml::get(v); if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(tab.at(ky), std::forward(opt)); } template auto get_or(toml::value& v, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(v.type() != toml::value_t::Table){return std::forward(opt);} auto& tab = toml::get(v); if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(tab[ky], std::forward(opt)); } template auto get_or(toml::value&& v, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) { if(v.type() != toml::value_t::Table){return std::forward(opt);} auto tab = toml::get(std::move(v)); if(tab.count(ky) == 0) {return std::forward(opt);} return ::toml::get_or(std::move(tab[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