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