// 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 class M, template class V> detail::enable_if_t>::value, T> & get(basic_value& v) { return v.template cast>::value>(); } template class M, template class V> detail::enable_if_t>::value, T> const& get(const basic_value& v) { return v.template cast>::value>(); } template class M, template class V> detail::enable_if_t>::value, T> && get(basic_value&& v) { return std::move(v).template cast>::value>(); } // ============================================================================ // T == toml::value; identity transformation. template class M, template class V> inline detail::enable_if_t>::value, T>& get(basic_value& v) { return v; } template class M, template class V> inline detail::enable_if_t>::value, T> const& get(const basic_value& v) { return v; } template class M, template class V> inline detail::enable_if_t>::value, T> && get(basic_value&& v) { return std::move(v); } // ============================================================================ // T == toml::basic_value; basic_value -> basic_value template class M, template class V> inline detail::enable_if_t::value, T> get(const basic_value& v) { return T(v); } // ============================================================================ // integer convertible from toml::Integer template class M, template class V> inline detail::enable_if_t, // T is integral detail::negation>, // but not bool detail::negation< // but not toml::integer detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { return static_cast(v.template cast()); } // ============================================================================ // floating point convertible from toml::Float template class M, template class V> inline detail::enable_if_t, // T is floating_point detail::negation< // but not toml::floating detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { return static_cast(v.template cast()); } // ============================================================================ // std::string; toml uses its own toml::string, but it should be convertible to // std::string seamlessly template class M, template class V> inline detail::enable_if_t::value, std::string>& get(basic_value& v) { return v.template cast().str; } template class M, template class V> inline detail::enable_if_t::value, std::string> const& get(const basic_value& v) { return v.template cast().str; } template class M, template class V> inline detail::enable_if_t::value, std::string> const& get(basic_value&& v) { return std::move(v.template cast().str); } // ============================================================================ // std::string_view #if __cplusplus >= 201703L template class M, template class V> inline detail::enable_if_t::value, std::string_view> get(const basic_value& v) { return std::string_view(v.template cast().str); } #endif // ============================================================================ // std::chrono::duration from toml::local_time. template class M, template class V> inline detail::enable_if_t::value, T> get(const basic_value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.template cast())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template class M, template class V> inline detail::enable_if_t< std::is_same::value, T> get(const basic_value& v) { switch(v.type()) { case value_t::local_date: { return std::chrono::system_clock::time_point( v.template cast()); } case value_t::local_datetime: { return std::chrono::system_clock::time_point( v.template cast()); } case value_t::offset_datetime: { return std::chrono::system_clock::time_point( v.template cast()); } default: { throw type_error(detail::format_underline("[error] toml::value " "bad_cast to std::chrono::system_clock::time_point", { {std::addressof(detail::get_region(v)), concat_to_string("the actual type is ", v.type())} })); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. // array-like type with resize(N) method template class M, template class V> detail::enable_if_t, // T is container detail::has_resize_method, // T::resize(N) works detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // array-like type with resize(N) method template class M, template class V> detail::enable_if_t, // T is container detail::negation>, // no T::resize() exists detail::negation< // not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // std::pair template class M, template class V> detail::enable_if_t::value, T> get(const basic_value&); // std::tuple template class M, template class V> detail::enable_if_t::value, T> get(const basic_value&); // map-like classes template class M, template class V> detail::enable_if_t, // T is map detail::negation< // but not toml::table detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // T.from_toml(v) template class M, template class V> detail::enable_if_t>>, detail::has_from_toml_method, // but has from_toml(toml::value) std::is_default_constructible // and default constructible >::value, T> get(const basic_value&); // toml::from::from_toml(v) template class M, template class V, std::size_t S = sizeof(::toml::into)> T get(const basic_value&); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template class M, template class V> detail::enable_if_t, // T is container detail::has_resize_method, // T::resize(N) works detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& ar = v.template 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 class M, template class V> detail::enable_if_t, // T is container detail::negation>, // no T::resize() exists detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& ar = v.template 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 class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.template 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 Array& a, index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { const auto& ar = v.template 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 class M, template class V> detail::enable_if_t, // T is map detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_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.template cast()) { map[key_type(kv.first)] = ::toml::get(kv.second); } return map; } // ============================================================================ // user-defined, but compatible types. template class M, template class V> detail::enable_if_t>>, detail::has_from_toml_method, // but has from_toml(toml::value) memfn std::is_default_constructible // and default constructible >::value, T> get(const basic_value& v) { T ud; ud.from_toml(v); return ud; } template class M, template class V, std::size_t> T get(const basic_value& v) { return ::toml::from::from_toml(v); } // ============================================================================ // find and get // for toml::table. template detail::enable_if_t, detail::is_basic_value>::value, decltype(::toml::get(std::declval()))> find(Table& tab, const toml::key& ky, std::string tn = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string( "[error] key \"", ky, "\" not found in ", tn)); } return ::toml::get(tab.at(ky)); } template detail::enable_if_t, detail::is_basic_value>::value, decltype(::toml::get(std::declval()))> find(Table const& tab, const toml::key& ky, std::string tn = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string( "[error] key \"", ky, "\" not found in ", tn)); } return ::toml::get(tab.at(ky)); } template detail::enable_if_t, detail::is_basic_value>::value, decltype(::toml::get(std::declval()))> find(typename std::remove_reference&& tab, const toml::key& ky, std::string tn = "unknown table") { if(tab.count(ky) == 0) { throw std::out_of_range(concat_to_string( "[error] key \"", ky, "\" not found in ", tn)); } return ::toml::get(std::move(tab.at(ky))); } // ---------------------------------------------------------------------------- // these overloads do not require to set T. and returns value itself. template class M, template class V> basic_value const& find(const basic_value& v, const key& ky) { const auto& tab = v.template cast(); 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 tab.at(ky); } template class M, template class V> basic_value& find(basic_value& v, const key& ky) { auto& tab = v.template cast(); 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 tab.at(ky); } template class M, template class V> basic_value&& find(basic_value&& v, const key& ky) { auto& tab = v.template cast(); 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 tab.at(ky); } // ---------------------------------------------------------------------------- // find(value, key); template class M, template class V> decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const key& ky) { const auto& tab = v.template cast(); 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 class M, template class V> decltype(::toml::get(std::declval&>())) find(basic_value& v, const key& ky) { auto& tab = v.template cast(); 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 class M, template class V> decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const key& ky) { auto& tab = v.template cast(); 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.at(ky))); } // -------------------------------------------------------------------------- // toml::find(toml::value, toml::key, Ts&& ... keys) template class M, template class V, typename ... Ts> const basic_value& find(const basic_value& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(v, ky), std::forward(keys)...); } template class M, template class V, typename ... Ts> basic_value& find(basic_value& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(v, ky), std::forward(keys)...); } template class M, template class V, typename ... Ts> basic_value&& find(basic_value&& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(std::move(v), ky), std::forward(keys)...); } template class M, template class V, typename ... Ts> decltype(::toml::get(std::declval&>())) find(const basic_value& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(v, ky), std::forward(keys)...); } template class M, template class V, typename ... Ts> decltype(::toml::get(std::declval&>())) find(basic_value& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(v, ky), std::forward(keys)...); } template class M, template class V, typename ... Ts> decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const ::toml::key& ky, Ts&& ... keys) { return ::toml::find(::toml::find(std::move(v), ky), std::forward(keys)...); } // ============================================================================ // get_or(value, fallback) // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T> const& get_or(const basic_value& v, const T& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>& get_or(basic_value& v, T& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>&& get_or(basic_value&& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return opt; } } // ---------------------------------------------------------------------------- // specialization for std::string (return type becomes lvalue ref) template class M, template class V> detail::enable_if_t::type>::type, std::string>::value, std::string> const& get_or(const basic_value& v, T&& opt) { try { return v.template cast().str; } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t::value, std::string>& get_or(basic_value& v, T& opt) { try { return v.template cast().str; } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t::type>::type, std::string>::value, std::string> get_or(basic_value&& v, T&& opt) { try { return std::move(v.template cast().str); } catch(...) { return std::forward(opt); } } // ---------------------------------------------------------------------------- // specialization for string literal template class M, template class V> detail::enable_if_t::type>::value, std::string> get_or(const basic_value& v, T&& opt) { try { return std::move(v.template cast().str); } catch(...) { return std::string(opt); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template class M, template class V> detail::enable_if_t>>, detail::negation::type>::type>>, detail::negation::type>> >::value, typename std::remove_reference::type> get_or(const basic_value& v, T&& opt) { try { return get::type>::type>(v); } catch(...) { return std::forward(opt); } } // =========================================================================== // find_or(value, key, fallback) // --------------------------------------------------------------------------- // exact types (return type can be a reference) template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T> const& find_or(const basic_value& v, const key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>& find_or(basic_value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>&& find_or(basic_value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} auto tab = std::move(v).as_table(); 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 class M, template class V> detail::enable_if_t::value, std::string> const& find_or(const basic_value& v, const key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t::value, std::string>& find_or(basic_value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t::value, std::string> find_or(basic_value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::forward(opt);} auto tab = toml::get(std::move(v)); if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // string literal (deduced as std::string) template class M, template class V> detail::enable_if_t< detail::is_string_literal::type>::value, std::string> find_or(const basic_value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); 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 class M, template class V> detail::enable_if_t>>, detail::negation>, detail::negation::type>> >::value, T> find_or(const basic_value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); 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 detail::enable_if_t, detail::is_basic_value, detail::is_exact_toml_type >::value, T> const& find_or(const Table& tab, const key& ky, const T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template detail::enable_if_t, detail::is_basic_value, detail::is_exact_toml_type >::value, T>& find_or(Table& tab, const key& ky, T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template detail::enable_if_t, detail::is_basic_value, detail::is_exact_toml_type >::value, T>&& find_or(typename std::remove_reference
::type&& tab, const 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 detail::enable_if_t, detail::is_basic_value, std::is_same >::value, std::string> const& find_or(const Table& tab, const key& ky, const T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template detail::enable_if_t, detail::is_basic_value, std::is_same >::value, std::string>& find_or(Table& tab, const key& ky, T& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab[ky], opt); } template detail::enable_if_t, detail::is_basic_value, std::is_same >::value, std::string> find_or(Table&& tab, const key& ky, T&& opt) { if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(std::move(tab[ky]), std::forward(opt)); } // --------------------------------------------------------------------------- // string literal (deduced as std::string) template detail::enable_if_t, detail::is_basic_value, detail::is_string_literal::type> >::value, std::string> find_or(const Table& tab, const 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::enable_if_t, detail::is_basic_value, detail::negation>, detail::negation>, detail::negation::type>> >::value, T> find_or(const Table& tab, const key& ky, T&& opt) { if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), std::forward(opt)); } // ============================================================================ // expect template class M, template class V> result expect(const basic_value& v) noexcept { try { return ok(get(v)); } catch(const std::exception& e) { return err(e.what()); } } template class M, template class V> result expect(const basic_value& v, const toml::key& k) noexcept { try { return ok(find(v, k)); } catch(const std::exception& e) { return err(e.what()); } } template detail::enable_if_t, detail::is_basic_value >::value, result> expect(const 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