2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * Copyright (c) 2015, Facebook, Inc.
18 * All rights reserved.
20 * This source code is licensed under the BSD-style license found in the
21 * LICENSE file in the root directory of this source tree. An additional grant
22 * of patent rights can be found in the PATENTS file in the same directory.
27 #include <folly/Conv.h>
28 #include <boost/function_types/is_member_pointer.hpp>
29 #include <boost/function_types/parameter_types.hpp>
30 #include <boost/mpl/equal.hpp>
31 #include <boost/mpl/pop_front.hpp>
32 #include <boost/mpl/transform.hpp>
33 #include <boost/mpl/vector.hpp>
37 // Auto-conversion of key/value based on callback signature, documented in
40 class IdentifyCallable {
42 enum class Kind { Function, MemberFunction };
43 template <typename Fn>
44 constexpr static Kind getKind() { return test<Fn>(nullptr); }
46 template <typename Fn>
47 using IsMemFn = typename boost::function_types::template is_member_pointer<
48 decltype(&Fn::operator())
50 template <typename Fn>
51 constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type
52 test(IsMemFn<Fn>*) { return IdentifyCallable::Kind::MemberFunction; }
54 constexpr static Kind test(...) { return IdentifyCallable::Kind::Function; }
57 template <IdentifyCallable::Kind, typename Fn>
58 struct ArgumentTypesByKind {};
59 template <typename Fn>
60 struct ArgumentTypesByKind<IdentifyCallable::Kind::MemberFunction, Fn> {
61 using type = typename boost::mpl::template pop_front<
62 typename boost::function_types::template parameter_types<
63 decltype(&Fn::operator())
67 template <typename Fn>
68 struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> {
69 using type = typename boost::function_types::template parameter_types<Fn>;
72 template <typename Fn>
74 typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type;
76 // At present, works for lambdas or plain old functions, but can be
77 // extended. The comparison deliberately strips cv-qualifieers and
78 // reference, leaving that choice up to the caller.
79 template <typename Fn, typename... Args>
80 struct HasArgumentTypes
81 : boost::mpl::template equal<
82 typename boost::mpl::template transform<
83 typename boost::mpl::template transform<
85 typename std::template remove_reference<boost::mpl::_1>>::
87 typename std::template remove_cv<boost::mpl::_1>>::type,
88 boost::mpl::vector<Args...>>::type {};
89 template <typename... Args>
90 using EnableForArgTypes =
91 typename std::enable_if<HasArgumentTypes<Args...>::value, void>::type;
94 template <typename Fn> EnableForArgTypes<Fn>
95 invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) {
99 // 1 argument -- pass only the value
101 // folly::dynamic (no conversion)
102 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic>
103 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
107 template <typename Fn> EnableForArgTypes<Fn, int64_t>
108 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
112 template <typename Fn> EnableForArgTypes<Fn, bool>
113 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
117 template <typename Fn> EnableForArgTypes<Fn, double>
118 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
122 template <typename Fn> EnableForArgTypes<Fn, std::string>
123 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
128 // 2 arguments -- pass both the key and the value.
131 // Pass the key as folly::dynamic, without conversion
133 // folly::dynamic, folly::dynamic (no conversion of value, either)
134 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, folly::dynamic>
135 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
138 // folly::dynamic, int64_t
139 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, int64_t>
140 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
143 // folly::dynamic, bool
144 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, bool>
145 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
148 // folly::dynamic, double
149 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, double>
150 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
153 // folly::dynamic, std::string
154 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, std::string>
155 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
159 // Convert the key to std::string.
161 // std::string, folly::dynamic (no conversion of value)
162 template <typename Fn> EnableForArgTypes<Fn, std::string, folly::dynamic>
163 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
166 // std::string, int64_t
167 template <typename Fn> EnableForArgTypes<Fn, std::string, int64_t>
168 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
169 fn(k.asString(), v.asInt());
172 template <typename Fn> EnableForArgTypes<Fn, std::string, bool>
173 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
174 fn(k.asString(), v.asBool());
176 // std::string, double
177 template <typename Fn> EnableForArgTypes<Fn, std::string, double>
178 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
179 fn(k.asString(), v.asDouble());
181 // std::string, std::string
182 template <typename Fn> EnableForArgTypes<Fn, std::string, std::string>
183 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
184 fn(k.asString(), v.asString());
187 // Convert the key to int64_t (good for arrays).
189 // int64_t, folly::dynamic (no conversion of value)
190 template <typename Fn> EnableForArgTypes<Fn, int64_t, folly::dynamic>
191 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
195 template <typename Fn> EnableForArgTypes<Fn, int64_t, int64_t>
196 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
197 fn(k.asInt(), v.asInt());
200 template <typename Fn> EnableForArgTypes<Fn, int64_t, bool>
201 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
202 fn(k.asInt(), v.asBool());
205 template <typename Fn> EnableForArgTypes<Fn, int64_t, double>
206 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
207 fn(k.asInt(), v.asDouble());
209 // int64_t, std::string
210 template <typename Fn> EnableForArgTypes<Fn, int64_t, std::string>
211 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
212 fn(k.asInt(), v.asString());
214 } // namespace detail
216 template <typename Fn>
217 void DynamicParser::optional(const folly::dynamic& key, Fn fn) {
218 wrapError(&key, [&]() {
219 if (auto vp = value().get_ptr(key)) {
226 // Implementation of DynamicParser template & inline methods.
229 template <typename Fn>
230 void DynamicParser::required(const folly::dynamic& key, Fn fn) {
231 wrapError(&key, [&]() {
232 auto vp = value().get_ptr(key);
234 throw std::runtime_error(folly::to<std::string>(
235 "Couldn't find key ", detail::toPseudoJson(key), " in dynamic object"
242 template <typename Fn>
243 void DynamicParser::objectItems(Fn fn) {
244 wrapError(nullptr, [&]() {
245 for (const auto& kv : value().items()) { // .items() can throw
246 parse(kv.first, kv.second, fn);
251 template <typename Fn>
252 void DynamicParser::arrayItems(Fn fn) {
253 wrapError(nullptr, [&]() {
255 for (const auto& v : value()) { // Iteration can throw
256 parse(i, v, fn); // i => dynamic cannot throw
262 template <typename Fn>
263 void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) {
266 } catch (DynamicParserLogicError&) {
267 // When the parser is misused, we throw all the way up to the user,
268 // instead of reporting it as if the input is invalid.
270 } catch (DynamicParserParseError&) {
271 // We are just bubbling up a parse error for OnError::THROW.
273 } catch (const std::exception& ex) {
274 reportError(lookup_k, ex);
278 template <typename Fn>
279 void DynamicParser::parse(
280 const folly::dynamic& k, const folly::dynamic& v, Fn fn) {
281 auto guard = stack_.push(k, v); // User code can nest parser calls.
282 wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); });
285 inline const folly::dynamic& DynamicParser::ParserStack::key() const {
287 throw DynamicParserLogicError("Only call key() inside parsing callbacks.");
292 inline const folly::dynamic& DynamicParser::ParserStack::value() const{
294 throw DynamicParserLogicError(
295 "Parsing nullptr, or parsing after releaseErrors()"