Make most implicit integer truncations and sign conversions explicit
[folly.git] / folly / experimental / DynamicParser-inl.h
1 /*
2  * Copyright 2017 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 <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>
34
35 namespace folly {
36
37 // Auto-conversion of key/value based on callback signature, documented in
38 // DynamicParser.h.
39 namespace detail {
40 class IdentifyCallable {
41 public:
42   enum class Kind { Function, MemberFunction };
43   template <typename Fn>
44   constexpr static Kind getKind() { return test<Fn>(nullptr); }
45 private:
46   template <typename Fn>
47   using IsMemFn = typename boost::function_types::template is_member_pointer<
48     decltype(&Fn::operator())
49   >;
50   template <typename Fn>
51   constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type
52   test(IsMemFn<Fn>*) { return IdentifyCallable::Kind::MemberFunction; }
53   template <typename>
54   constexpr static Kind test(...) { return IdentifyCallable::Kind::Function; }
55 };
56
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())
64     >::type
65   >::type;
66 };
67 template <typename Fn>
68 struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> {
69   using type = typename boost::function_types::template parameter_types<Fn>;
70 };
71
72 template <typename Fn>
73 using ArgumentTypes =
74   typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type;
75
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<
84                   ArgumentTypes<Fn>,
85                   typename std::template remove_reference<boost::mpl::_1>>::
86                   type,
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;
92
93 // No arguments
94 template <typename Fn> EnableForArgTypes<Fn>
95 invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) {
96   f();
97 }
98
99 // 1 argument -- pass only the value
100 //
101 // folly::dynamic (no conversion)
102 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic>
103 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
104   fn(v);
105 }
106 // int64_t
107 template <typename Fn> EnableForArgTypes<Fn, int64_t>
108 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
109   fn(v.asInt());
110 }
111 // bool
112 template <typename Fn> EnableForArgTypes<Fn, bool>
113 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
114   fn(v.asBool());
115 }
116 // double
117 template <typename Fn> EnableForArgTypes<Fn, double>
118 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
119   fn(v.asDouble());
120 }
121 // std::string
122 template <typename Fn> EnableForArgTypes<Fn, std::string>
123 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
124   fn(v.asString());
125 }
126
127 //
128 // 2 arguments -- pass both the key and the value.
129 //
130
131 // Pass the key as folly::dynamic, without conversion
132 //
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) {
136   fn(k, v);
137 }
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) {
141   fn(k, v.asInt());
142 }
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) {
146   fn(k, v.asBool());
147 }
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) {
151   fn(k, v.asDouble());
152 }
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) {
156   fn(k, v.asString());
157 }
158
159 // Convert the key to std::string.
160 //
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) {
164   fn(k.asString(), v);
165 }
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());
170 }
171 // std::string, bool
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());
175 }
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());
180 }
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());
185 }
186
187 // Convert the key to int64_t (good for arrays).
188 //
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) {
192   fn(k.asInt(), v);
193 }
194 // int64_t, int64_t
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());
198 }
199 // int64_t, bool
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());
203 }
204 // int64_t, double
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());
208 }
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());
213 }
214 }  // namespace detail
215
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)) {
220       parse(key, *vp, fn);
221     }
222   });
223 }
224
225 //
226 // Implementation of DynamicParser template & inline methods.
227 //
228
229 template <typename Fn>
230 void DynamicParser::required(const folly::dynamic& key, Fn fn) {
231   wrapError(&key, [&]() {
232     auto vp = value().get_ptr(key);
233     if (!vp) {
234       throw std::runtime_error(folly::to<std::string>(
235         "Couldn't find key ", detail::toPseudoJson(key), " in dynamic object"
236       ));
237     }
238     parse(key, *vp, fn);
239   });
240 }
241
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);
247     }
248   });
249 }
250
251 template <typename Fn>
252 void DynamicParser::arrayItems(Fn fn) {
253   wrapError(nullptr, [&]() {
254     size_t i = 0;
255     for (const auto& v : value()) {  // Iteration can throw
256       parse(i, v, fn);  // i => dynamic cannot throw
257       ++i;
258     }
259   });
260 }
261
262 template <typename Fn>
263 void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) {
264   try {
265     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.
269     throw;
270   } catch (DynamicParserParseError&) {
271     // We are just bubbling up a parse error for OnError::THROW.
272     throw;
273   } catch (const std::exception& ex) {
274     reportError(lookup_k, ex);
275   }
276 }
277
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); });
283 }
284
285 inline const folly::dynamic& DynamicParser::ParserStack::key() const {
286   if (!key_) {
287     throw DynamicParserLogicError("Only call key() inside parsing callbacks.");
288   }
289   return *key_;
290 }
291
292 inline const folly::dynamic& DynamicParser::ParserStack::value() const{
293   if (!value_) {
294     throw DynamicParserLogicError(
295       "Parsing nullptr, or parsing after releaseErrors()"
296     );
297   }
298   return *value_;
299 }
300
301 }  // namespace folly