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