// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_FIND_HPP #define TOML11_FIND_HPP #include "get.hpp" #include namespace toml { // ---------------------------------------------------------------------------- // 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 = std::move(v).as_table(); 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 std::move(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.as_table(); 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.as_table(); 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 = std::move(v).as_table(); 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> detail::enable_if_t... >::value, 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> detail::enable_if_t... >::value, 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> detail::enable_if_t... >::value, 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> detail::enable_if_t... >::value, decltype(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> detail::enable_if_t... >::value, decltype(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> detail::enable_if_t... >::value, decltype(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)...); } // =========================================================================== // find_or(value, key, fallback) template class M, template class V> basic_value const& find_or(const basic_value& v, const key& ky, const basic_value& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return tab.at(ky); } template class M, template class V> basic_value& find_or(basic_value& v, const toml::key& ky, basic_value& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return tab[ky]; } template class M, template class V> basic_value find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) { if(!v.is_table()) {return std::move(opt);} auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) {return std::move(opt);} return std::move(tab[ky]); } // --------------------------------------------------------------------------- // 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 std::forward(opt);} auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) {return std::forward(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 = std::move(v).as_table(); if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(std::move(tab.at(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 std::string(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::type>::type, basic_value>>, // T is not std::string detail::negation::type>::type>>, // T is not a string literal detail::negation::type>> >::value, typename std::remove_cv::type>::type> find_or(const basic_value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::forward(opt);} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return std::forward(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()); } } // =========================================================================== // find_fuzzy // --------------------------------------------------------------------------- // default fuzzy matcher; levenstein distance (all cost is 1) struct levenstein_matcher { levenstein_matcher(): tolerance(1) {} levenstein_matcher(const std::uint32_t tol): tolerance(tol) {} ~levenstein_matcher() = default; levenstein_matcher(levenstein_matcher const&) = default; levenstein_matcher(levenstein_matcher &&) = default; levenstein_matcher& operator=(levenstein_matcher const&) = default; levenstein_matcher& operator=(levenstein_matcher &&) = default; template bool operator()(const std::basic_string& lhs, const std::basic_string& rhs) const { return this->distance(lhs, rhs) <= this->tolerance; } template std::uint32_t distance( const std::basic_string& lhs, const std::basic_string& rhs) const { // force `lhs.size() <= rhs.size()` if(lhs.size() > rhs.size()) {return this->distance(rhs, lhs);} std::vector matrix(lhs.size() + 1u); std::iota(matrix.begin(), matrix.end(), 0); for(const charT r : rhs) { std::uint32_t prev_diag = matrix.front(); matrix.front() += 1; for(std::size_t i=0; i(v, "tablename", FuzzyMatcher); template class M, template class V, typename FuzzyMatcher = levenstein_matcher> auto find_fuzzy(const basic_value& v, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) -> decltype(find(std::declval&>(), ky)) { try { return find(v, ky); } catch(const std::out_of_range& oor) { const auto& tab = v.as_table(); for(const auto& kv : tab) { if(match(kv.first, ky)) { return get(kv.second); } } throw; } } template class M, template class V, typename FuzzyMatcher = levenstein_matcher> auto find_fuzzy(basic_value& v, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) -> decltype(find(std::declval&>(), ky)) { try { return find(v, ky); } catch(const std::out_of_range& oor) { auto& tab = v.as_table(); for(auto& kv : tab) { if(match(kv.first, ky)) { return get(kv.second); } } throw; } } template class M, template class V, typename FuzzyMatcher = levenstein_matcher> auto find_fuzzy(basic_value&& v_, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) -> decltype(find(std::declval&&>(), ky)) { basic_value v = v_; // to re-use later, store it once try { return std::move(find(v, ky)); // pass lref, move later } catch(const std::out_of_range& oor) { auto& tab = v.as_table(); // because v is used here for(auto& kv : tab) { if(match(kv.first, ky)) { return get(std::move(kv.second)); } } throw; } } // --------------------------------------------------------------------------- // no-template-argument case (by default, return toml::value). // toml::find_fuzzy(v, "tablename", FuzzyMatcher); template class M, template class V, typename FuzzyMatcher = levenstein_matcher> basic_value const& find_fuzzy(const basic_value& v, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) { try { return find(v, ky); } catch(const std::out_of_range& oor) { const auto& tab = v.as_table(); for(const auto& kv : tab) { if(match(kv.first, ky)) { return kv.second; } } throw; } } template class M, template class V, typename FuzzyMatcher = levenstein_matcher> basic_value& find_fuzzy(basic_value& v, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) { try { return find(v, ky); } catch(const std::out_of_range& oor) { auto& tab = v.as_table(); for(auto& kv : tab) { if(match(kv.first, ky)) { return kv.second; } } throw; } } template class M, template class V, typename FuzzyMatcher = levenstein_matcher> basic_value&& find_fuzzy(basic_value&& v_, const key& ky, const FuzzyMatcher match = levenstein_matcher(1)) { basic_value v = v_; // to re-use later, store it once try { return std::move(find(v, ky)); } catch(const std::out_of_range& oor) { auto& tab = v.as_table(); for(auto& kv : tab) { if(match(kv.first, ky)) { return std::move(kv.second); } } throw; } } // =========================================================================== // find(v, k, matcher) // // when matcher is passed, check a key that matches exists or not. if it exists, // suggest that in the error message template class M, template class V, typename FuzzyMatcher> basic_value const& find(const basic_value& v, const key& ky, FuzzyMatcher match) { const auto& tab = v.template cast(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this?"} })); } } 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, typename FuzzyMatcher> basic_value& find(basic_value& v, const key& ky, FuzzyMatcher match) { auto& tab = v.template cast(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this?"} })); } } 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, typename FuzzyMatcher> basic_value&& find(basic_value&& v, const key& ky, FuzzyMatcher match) { auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this?"} })); } } 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 std::move(tab.at(ky)); } // ---------------------------------------------------------------------------- // find(value, key, fuzzy_matcher); template class M, template class V, typename FuzzyMatcher> detail::enable_if_t< detail::negation>::value, decltype(::toml::get(std::declval const&>()))> find(const basic_value& v, const key& ky, FuzzyMatcher match) { const auto& tab = v.as_table(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this here?"} })); } } 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, typename FuzzyMatcher> detail::enable_if_t< detail::negation>::value, decltype(::toml::get(std::declval&>()))> find(basic_value& v, const key& ky, FuzzyMatcher match) { auto& tab = v.as_table(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this here?"} })); } } 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, typename FuzzyMatcher> detail::enable_if_t< detail::negation>::value, decltype(::toml::get(std::declval&&>()))> find(basic_value&& v, const key& ky, FuzzyMatcher match) { auto tab = v.as_table(); if(tab.count(ky) == 0) { for(const auto& kv : tab) { if(match(kv.first, ky)) { 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"}, {std::addressof(detail::get_region(kv.second)), "did you mean this here?"} })); } } 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 #endif// TOML11_FIND_HPP