DynamicParser-inl.h 9.7 KB

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