/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include namespace folly { // Auto-conversion of key/value based on callback signature, documented in // DynamicParser.h. namespace detail { class IdentifyCallable { public: enum class Kind { Function, MemberFunction }; template constexpr static Kind getKind() { return test(nullptr); } private: template using IsMemFn = std::is_member_pointer; template constexpr static typename std::enable_if::value, Kind>::type test( IsMemFn*) { return IdentifyCallable::Kind::MemberFunction; } template constexpr static Kind test(...) { return IdentifyCallable::Kind::Function; } }; template struct ArgumentTypesByKind {}; template struct ArgumentTypesByKind { using type = typename boost::mpl::template pop_front< typename boost::function_types::template parameter_types::type>::type; }; template struct ArgumentTypesByKind { using type = typename boost::function_types::template parameter_types; }; template using ArgumentTypes = typename ArgumentTypesByKind(), Fn>::type; // At present, works for lambdas or plain old functions, but can be // extended. The comparison deliberately strips cv-qualifiers and // reference, leaving that choice up to the caller. template struct HasArgumentTypes : boost::mpl::template equal< typename boost::mpl:: transform, remove_cvref>::type, boost::mpl::vector>::type {}; template using EnableForArgTypes = typename std::enable_if::value, void>::type; // No arguments template EnableForArgTypes invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) { f(); } // 1 argument -- pass only the value // // folly::dynamic (no conversion) template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { fn(v); } // int64_t template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { fn(v.asInt()); } // bool template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { fn(v.asBool()); } // double template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { fn(v.asDouble()); } // std::string template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { fn(v.asString()); } // // 2 arguments -- pass both the key and the value. // // Pass the key as folly::dynamic, without conversion // // folly::dynamic, folly::dynamic (no conversion of value, either) template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k, v); } // folly::dynamic, int64_t template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k, v.asInt()); } // folly::dynamic, bool template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k, v.asBool()); } // folly::dynamic, double template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k, v.asDouble()); } // folly::dynamic, std::string template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k, v.asString()); } // Convert the key to std::string. // // std::string, folly::dynamic (no conversion of value) template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asString(), v); } // std::string, int64_t template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asString(), v.asInt()); } // std::string, bool template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asString(), v.asBool()); } // std::string, double template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asString(), v.asDouble()); } // std::string, std::string template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asString(), v.asString()); } // Convert the key to int64_t (good for arrays). // // int64_t, folly::dynamic (no conversion of value) template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asInt(), v); } // int64_t, int64_t template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asInt(), v.asInt()); } // int64_t, bool template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asInt(), v.asBool()); } // int64_t, double template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asInt(), v.asDouble()); } // int64_t, std::string template EnableForArgTypes invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { fn(k.asInt(), v.asString()); } } // namespace detail template void DynamicParser::optional(const folly::dynamic& key, Fn fn) { wrapError(&key, [&]() { if (auto vp = value().get_ptr(key)) { parse(key, *vp, fn); } }); } // // Implementation of DynamicParser template & inline methods. // template void DynamicParser::required(const folly::dynamic& key, Fn fn) { wrapError(&key, [&]() { auto vp = value().get_ptr(key); if (!vp) { throw std::runtime_error(folly::to( "Couldn't find key ", detail::toPseudoJson(key), " in dynamic object")); } parse(key, *vp, fn); }); } template void DynamicParser::objectItems(Fn fn) { wrapError(nullptr, [&]() { for (const auto& kv : value().items()) { // .items() can throw parse(kv.first, kv.second, fn); } }); } template void DynamicParser::arrayItems(Fn fn) { wrapError(nullptr, [&]() { size_t i = 0; for (const auto& v : value()) { // Iteration can throw parse(i, v, fn); // i => dynamic cannot throw ++i; } }); } template void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) { try { fn(); } catch (DynamicParserLogicError&) { // When the parser is misused, we throw all the way up to the user, // instead of reporting it as if the input is invalid. throw; } catch (DynamicParserParseError&) { // We are just bubbling up a parse error for OnError::THROW. throw; } catch (const std::exception& ex) { reportError(lookup_k, ex); } } template void DynamicParser::parse( const folly::dynamic& k, const folly::dynamic& v, Fn fn) { auto guard = stack_.push(k, v); // User code can nest parser calls. wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); }); } inline const folly::dynamic& DynamicParser::ParserStack::key() const { if (!key_) { throw DynamicParserLogicError("Only call key() inside parsing callbacks."); } return *key_; } inline const folly::dynamic& DynamicParser::ParserStack::value() const { if (!value_) { throw DynamicParserLogicError( "Parsing nullptr, or parsing after releaseErrors()"); } return *value_; } } // namespace folly