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 | |
36 | namespace folly { |
37 | |
38 | // Auto-conversion of key/value based on callback signature, documented in |
39 | // DynamicParser.h. |
40 | namespace detail { |
41 | class 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 | |
65 | template <IdentifyCallable::Kind, typename Fn> |
66 | struct ArgumentTypesByKind {}; |
67 | template <typename Fn> |
68 | struct 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 | }; |
73 | template <typename Fn> |
74 | struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> { |
75 | using type = typename boost::function_types::template parameter_types<Fn>; |
76 | }; |
77 | |
78 | template <typename Fn> |
79 | using 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. |
85 | template <typename Fn, typename... Args> |
86 | struct 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 {}; |
95 | template <typename... Args> |
96 | using EnableForArgTypes = |
97 | typename std::enable_if<HasArgumentTypes<Args...>::value, void>::type; |
98 | |
99 | // No arguments |
100 | template <typename Fn> |
101 | EnableForArgTypes<Fn> |
102 | invokeForKeyValue(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) |
109 | template <typename Fn> |
110 | EnableForArgTypes<Fn, folly::dynamic> |
111 | invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { |
112 | fn(v); |
113 | } |
114 | // int64_t |
115 | template <typename Fn> |
116 | EnableForArgTypes<Fn, int64_t> |
117 | invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { |
118 | fn(v.asInt()); |
119 | } |
120 | // bool |
121 | template <typename Fn> |
122 | EnableForArgTypes<Fn, bool> |
123 | invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { |
124 | fn(v.asBool()); |
125 | } |
126 | // double |
127 | template <typename Fn> |
128 | EnableForArgTypes<Fn, double> |
129 | invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { |
130 | fn(v.asDouble()); |
131 | } |
132 | // std::string |
133 | template <typename Fn> |
134 | EnableForArgTypes<Fn, std::string> |
135 | invokeForKeyValue(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) |
146 | template <typename Fn> |
147 | EnableForArgTypes<Fn, folly::dynamic, folly::dynamic> |
148 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
149 | fn(k, v); |
150 | } |
151 | // folly::dynamic, int64_t |
152 | template <typename Fn> |
153 | EnableForArgTypes<Fn, folly::dynamic, int64_t> |
154 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
155 | fn(k, v.asInt()); |
156 | } |
157 | // folly::dynamic, bool |
158 | template <typename Fn> |
159 | EnableForArgTypes<Fn, folly::dynamic, bool> |
160 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
161 | fn(k, v.asBool()); |
162 | } |
163 | // folly::dynamic, double |
164 | template <typename Fn> |
165 | EnableForArgTypes<Fn, folly::dynamic, double> |
166 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
167 | fn(k, v.asDouble()); |
168 | } |
169 | // folly::dynamic, std::string |
170 | template <typename Fn> |
171 | EnableForArgTypes<Fn, folly::dynamic, std::string> |
172 | invokeForKeyValue(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) |
179 | template <typename Fn> |
180 | EnableForArgTypes<Fn, std::string, folly::dynamic> |
181 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
182 | fn(k.asString(), v); |
183 | } |
184 | // std::string, int64_t |
185 | template <typename Fn> |
186 | EnableForArgTypes<Fn, std::string, int64_t> |
187 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
188 | fn(k.asString(), v.asInt()); |
189 | } |
190 | // std::string, bool |
191 | template <typename Fn> |
192 | EnableForArgTypes<Fn, std::string, bool> |
193 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
194 | fn(k.asString(), v.asBool()); |
195 | } |
196 | // std::string, double |
197 | template <typename Fn> |
198 | EnableForArgTypes<Fn, std::string, double> |
199 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
200 | fn(k.asString(), v.asDouble()); |
201 | } |
202 | // std::string, std::string |
203 | template <typename Fn> |
204 | EnableForArgTypes<Fn, std::string, std::string> |
205 | invokeForKeyValue(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) |
212 | template <typename Fn> |
213 | EnableForArgTypes<Fn, int64_t, folly::dynamic> |
214 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
215 | fn(k.asInt(), v); |
216 | } |
217 | // int64_t, int64_t |
218 | template <typename Fn> |
219 | EnableForArgTypes<Fn, int64_t, int64_t> |
220 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
221 | fn(k.asInt(), v.asInt()); |
222 | } |
223 | // int64_t, bool |
224 | template <typename Fn> |
225 | EnableForArgTypes<Fn, int64_t, bool> |
226 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
227 | fn(k.asInt(), v.asBool()); |
228 | } |
229 | // int64_t, double |
230 | template <typename Fn> |
231 | EnableForArgTypes<Fn, int64_t, double> |
232 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
233 | fn(k.asInt(), v.asDouble()); |
234 | } |
235 | // int64_t, std::string |
236 | template <typename Fn> |
237 | EnableForArgTypes<Fn, int64_t, std::string> |
238 | invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { |
239 | fn(k.asInt(), v.asString()); |
240 | } |
241 | } // namespace detail |
242 | |
243 | template <typename Fn> |
244 | void 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 | |
256 | template <typename Fn> |
257 | void 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 | |
270 | template <typename Fn> |
271 | void 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 | |
279 | template <typename Fn> |
280 | void 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 | |
290 | template <typename Fn> |
291 | void 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 | |
306 | template <typename Fn> |
307 | void 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 | |
315 | inline 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 | |
322 | inline 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 | |