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