1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16/*
17 * Copyright (c) 2015, Facebook, Inc.
18 * All rights reserved.
19 *
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.
23 *
24 */
25#pragma once
26
27#include <boost/function_types/is_member_pointer.hpp>
28#include <boost/function_types/parameter_types.hpp>
29#include <boost/mpl/equal.hpp>
30#include <boost/mpl/pop_front.hpp>
31#include <boost/mpl/transform.hpp>
32#include <boost/mpl/vector.hpp>
33
34#include <folly/Conv.h>
35
36namespace folly {
37
38// Auto-conversion of key/value based on callback signature, documented in
39// DynamicParser.h.
40namespace detail {
41class IdentifyCallable {
42 public:
43 enum class Kind { Function, MemberFunction };
44 template <typename Fn>
45 constexpr static Kind getKind() {
46 return test<Fn>(nullptr);
47 }
48
49 private:
50 template <typename Fn>
51 using IsMemFn =
52 typename boost::function_types::template is_member_pointer<decltype(
53 &Fn::operator())>;
54 template <typename Fn>
55 constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type test(
56 IsMemFn<Fn>*) {
57 return IdentifyCallable::Kind::MemberFunction;
58 }
59 template <typename>
60 constexpr static Kind test(...) {
61 return IdentifyCallable::Kind::Function;
62 }
63};
64
65template <IdentifyCallable::Kind, typename Fn>
66struct ArgumentTypesByKind {};
67template <typename Fn>
68struct ArgumentTypesByKind<IdentifyCallable::Kind::MemberFunction, Fn> {
69 using type = typename boost::mpl::template pop_front<
70 typename boost::function_types::template parameter_types<decltype(
71 &Fn::operator())>::type>::type;
72};
73template <typename Fn>
74struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> {
75 using type = typename boost::function_types::template parameter_types<Fn>;
76};
77
78template <typename Fn>
79using ArgumentTypes =
80 typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type;
81
82// At present, works for lambdas or plain old functions, but can be
83// extended. The comparison deliberately strips cv-qualifieers and
84// reference, leaving that choice up to the caller.
85template <typename Fn, typename... Args>
86struct HasArgumentTypes
87 : boost::mpl::template equal<
88 typename boost::mpl::template transform<
89 typename boost::mpl::template transform<
90 ArgumentTypes<Fn>,
91 typename std::template remove_reference<boost::mpl::_1>>::
92 type,
93 typename std::template remove_cv<boost::mpl::_1>>::type,
94 boost::mpl::vector<Args...>>::type {};
95template <typename... Args>
96using EnableForArgTypes =
97 typename std::enable_if<HasArgumentTypes<Args...>::value, void>::type;
98
99// No arguments
100template <typename Fn>
101EnableForArgTypes<Fn>
102invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) {
103 f();
104}
105
106// 1 argument -- pass only the value
107//
108// folly::dynamic (no conversion)
109template <typename Fn>
110EnableForArgTypes<Fn, folly::dynamic>
111invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
112 fn(v);
113}
114// int64_t
115template <typename Fn>
116EnableForArgTypes<Fn, int64_t>
117invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
118 fn(v.asInt());
119}
120// bool
121template <typename Fn>
122EnableForArgTypes<Fn, bool>
123invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
124 fn(v.asBool());
125}
126// double
127template <typename Fn>
128EnableForArgTypes<Fn, double>
129invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
130 fn(v.asDouble());
131}
132// std::string
133template <typename Fn>
134EnableForArgTypes<Fn, std::string>
135invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
136 fn(v.asString());
137}
138
139//
140// 2 arguments -- pass both the key and the value.
141//
142
143// Pass the key as folly::dynamic, without conversion
144//
145// folly::dynamic, folly::dynamic (no conversion of value, either)
146template <typename Fn>
147EnableForArgTypes<Fn, folly::dynamic, folly::dynamic>
148invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
149 fn(k, v);
150}
151// folly::dynamic, int64_t
152template <typename Fn>
153EnableForArgTypes<Fn, folly::dynamic, int64_t>
154invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
155 fn(k, v.asInt());
156}
157// folly::dynamic, bool
158template <typename Fn>
159EnableForArgTypes<Fn, folly::dynamic, bool>
160invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
161 fn(k, v.asBool());
162}
163// folly::dynamic, double
164template <typename Fn>
165EnableForArgTypes<Fn, folly::dynamic, double>
166invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
167 fn(k, v.asDouble());
168}
169// folly::dynamic, std::string
170template <typename Fn>
171EnableForArgTypes<Fn, folly::dynamic, std::string>
172invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
173 fn(k, v.asString());
174}
175
176// Convert the key to std::string.
177//
178// std::string, folly::dynamic (no conversion of value)
179template <typename Fn>
180EnableForArgTypes<Fn, std::string, folly::dynamic>
181invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
182 fn(k.asString(), v);
183}
184// std::string, int64_t
185template <typename Fn>
186EnableForArgTypes<Fn, std::string, int64_t>
187invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
188 fn(k.asString(), v.asInt());
189}
190// std::string, bool
191template <typename Fn>
192EnableForArgTypes<Fn, std::string, bool>
193invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
194 fn(k.asString(), v.asBool());
195}
196// std::string, double
197template <typename Fn>
198EnableForArgTypes<Fn, std::string, double>
199invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
200 fn(k.asString(), v.asDouble());
201}
202// std::string, std::string
203template <typename Fn>
204EnableForArgTypes<Fn, std::string, std::string>
205invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
206 fn(k.asString(), v.asString());
207}
208
209// Convert the key to int64_t (good for arrays).
210//
211// int64_t, folly::dynamic (no conversion of value)
212template <typename Fn>
213EnableForArgTypes<Fn, int64_t, folly::dynamic>
214invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
215 fn(k.asInt(), v);
216}
217// int64_t, int64_t
218template <typename Fn>
219EnableForArgTypes<Fn, int64_t, int64_t>
220invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
221 fn(k.asInt(), v.asInt());
222}
223// int64_t, bool
224template <typename Fn>
225EnableForArgTypes<Fn, int64_t, bool>
226invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
227 fn(k.asInt(), v.asBool());
228}
229// int64_t, double
230template <typename Fn>
231EnableForArgTypes<Fn, int64_t, double>
232invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
233 fn(k.asInt(), v.asDouble());
234}
235// int64_t, std::string
236template <typename Fn>
237EnableForArgTypes<Fn, int64_t, std::string>
238invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
239 fn(k.asInt(), v.asString());
240}
241} // namespace detail
242
243template <typename Fn>
244void DynamicParser::optional(const folly::dynamic& key, Fn fn) {
245 wrapError(&key, [&]() {
246 if (auto vp = value().get_ptr(key)) {
247 parse(key, *vp, fn);
248 }
249 });
250}
251
252//
253// Implementation of DynamicParser template & inline methods.
254//
255
256template <typename Fn>
257void DynamicParser::required(const folly::dynamic& key, Fn fn) {
258 wrapError(&key, [&]() {
259 auto vp = value().get_ptr(key);
260 if (!vp) {
261 throw std::runtime_error(folly::to<std::string>(
262 "Couldn't find key ",
263 detail::toPseudoJson(key),
264 " in dynamic object"));
265 }
266 parse(key, *vp, fn);
267 });
268}
269
270template <typename Fn>
271void DynamicParser::objectItems(Fn fn) {
272 wrapError(nullptr, [&]() {
273 for (const auto& kv : value().items()) { // .items() can throw
274 parse(kv.first, kv.second, fn);
275 }
276 });
277}
278
279template <typename Fn>
280void DynamicParser::arrayItems(Fn fn) {
281 wrapError(nullptr, [&]() {
282 size_t i = 0;
283 for (const auto& v : value()) { // Iteration can throw
284 parse(i, v, fn); // i => dynamic cannot throw
285 ++i;
286 }
287 });
288}
289
290template <typename Fn>
291void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) {
292 try {
293 fn();
294 } catch (DynamicParserLogicError&) {
295 // When the parser is misused, we throw all the way up to the user,
296 // instead of reporting it as if the input is invalid.
297 throw;
298 } catch (DynamicParserParseError&) {
299 // We are just bubbling up a parse error for OnError::THROW.
300 throw;
301 } catch (const std::exception& ex) {
302 reportError(lookup_k, ex);
303 }
304}
305
306template <typename Fn>
307void DynamicParser::parse(
308 const folly::dynamic& k,
309 const folly::dynamic& v,
310 Fn fn) {
311 auto guard = stack_.push(k, v); // User code can nest parser calls.
312 wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); });
313}
314
315inline const folly::dynamic& DynamicParser::ParserStack::key() const {
316 if (!key_) {
317 throw DynamicParserLogicError("Only call key() inside parsing callbacks.");
318 }
319 return *key_;
320}
321
322inline const folly::dynamic& DynamicParser::ParserStack::value() const {
323 if (!value_) {
324 throw DynamicParserLogicError(
325 "Parsing nullptr, or parsing after releaseErrors()");
326 }
327 return *value_;
328}
329
330} // namespace folly
331