proxygen
DynamicParser.h
Go to the documentation of this file.
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 <folly/CPortability.h>
28 #include <folly/ScopeGuard.h>
29 #include <folly/dynamic.h>
30 
31 namespace folly {
32 
187 namespace detail {
188 // Why do DynamicParser error messages use folly::dynamic pseudo-JSON?
189 // Firstly, the input dynamic need not correspond to valid JSON. Secondly,
190 // wrapError() uses integer-keyed objects to report arrary-indexing errors.
192 } // namespace detail
193 
198 struct FOLLY_EXPORT DynamicParserParseError : public std::runtime_error {
200  : std::runtime_error(folly::to<std::string>(
201  "DynamicParserParseError: ",
202  detail::toPseudoJson(error))),
203  error_(std::move(error)) {}
210  const folly::dynamic& error() const {
211  return error_;
212  }
213 
214  private:
216 };
217 
223 struct FOLLY_EXPORT DynamicParserLogicError : public std::logic_error {
224  template <typename... Args>
225  explicit DynamicParserLogicError(Args&&... args)
226  : std::logic_error(folly::to<std::string>(std::forward<Args>(args)...)) {}
227 };
228 
230  public:
231  enum class OnError {
232  // After parsing, releaseErrors() reports all parse errors.
233  // Throws DynamicParserLogicError on programmer errors.
234  RECORD,
235  // Throws DynamicParserParseError on the first parse error, or
236  // DynamicParserLogicError on programmer errors.
237  THROW,
238  };
239 
240  // You MUST NOT destroy `d` before the parser.
242  : onError_(on_error), stack_(d) {} // Always access input through stack_
243 
251  return stack_.releaseErrors();
252  }
253 
258  template <typename Fn>
259  void optional(const folly::dynamic& key, Fn);
260 
261  // Like optional(), but reports an error if d[key] does not exist.
262  template <typename Fn>
263  void required(const folly::dynamic& key, Fn);
264 
269  template <typename Fn>
270  void objectItems(Fn);
271 
276  template <typename Fn>
277  void arrayItems(Fn);
278 
283  inline const folly::dynamic& key() const {
284  return stack_.key();
285  }
290  inline const folly::dynamic& value() const {
291  return stack_.value();
292  }
293 
303  allowNonStringKeyErrors_ = b;
304  return *this;
305  }
306 
307  private:
318  template <typename Fn>
319  void wrapError(const folly::dynamic* lookup_key, Fn);
320 
321  void reportError(const folly::dynamic* lookup_k, const std::exception& ex);
322 
323  template <typename Fn>
324  void parse(const folly::dynamic& key, const folly::dynamic& value, Fn fn);
325 
326  // All of the above business logic obtains the part of the folly::dynamic
327  // it is examining (and the location for reporting errors) via this class,
328  // which lets it correctly handle nesting.
329  struct ParserStack {
330  struct Pop {
331  explicit Pop(ParserStack* sp)
332  : key_(sp->key_), value_(sp->value_), stackPtr_(sp) {}
333  void operator()() noexcept; // ScopeGuard requires noexcept
334  private:
338  };
339  struct PopGuard {
340  explicit PopGuard(ParserStack* sp) : pop_(in_place, sp) {}
342  pop_ && ((*pop_)(), true);
343  }
344 
345  private:
347  };
348 
349  explicit ParserStack(const folly::dynamic* input)
350  : value_(input),
351  errors_(folly::dynamic::object()),
352  subErrors_({&errors_}) {}
353 
354  // Not copiable or movable due to numerous internal pointers
355  ParserStack(const ParserStack&) = delete;
356  ParserStack& operator=(const ParserStack&) = delete;
357  ParserStack(ParserStack&&) = delete;
358  ParserStack& operator=(ParserStack&&) = delete;
359 
360  // Lets user code nest parser calls by recording current key+value and
361  // returning an RAII guard to restore the old one. `noexcept` since it
362  // is used unwrapped.
363  PopGuard push(const folly::dynamic& k, const folly::dynamic& v) noexcept;
364 
365  // Throws DynamicParserLogicError if used outside of a parsing function.
366  inline const folly::dynamic& key() const;
367  // Throws DynamicParserLogicError if used after releaseErrors().
368  inline const folly::dynamic& value() const;
369 
370  // Lazily creates new "nested" sub-objects in errors_.
371  folly::dynamic& errors(bool allow_non_string_keys) noexcept;
372 
373  // The user invokes this at most once after the parse is done.
374  folly::dynamic releaseErrors();
375 
376  // Invoked on error when using OnError::THROW.
377  [[noreturn]] void throwErrors();
378 
379  private:
380  friend struct Pop;
381 
382  folly::dynamic releaseErrorsImpl(); // for releaseErrors() & throwErrors()
383 
384  // Null outside of a parsing function.
385  const folly::dynamic* key_{nullptr};
386  // Null on errors: when the input was nullptr, or after releaseErrors().
388 
389  // An object containing some of these keys:
390  // "key_errors" -- {"key": "description of error looking up said key"}
391  // "error" -- why did we fail to parse this value?
392  // "value" -- a copy of the input causing the error, and
393  // "nested" -- {"key" or integer for arrays: <another errors_ object>}
394  //
395  // "nested" will contain identically structured objects with keys (array
396  // indices) identifying the origin of the errors. Of course, "input"
397  // would no longer refer to the whole input, but to a part.
399  // We only materialize errors_ sub-objects when needed. This stores keys
400  // for unmaterialized errors, from outermost to innermost.
401  std::vector<const folly::dynamic*> unmaterializedSubErrorKeys_;
402  // Materialized errors, from outermost to innermost
403  std::vector<folly::dynamic*> subErrors_; // Point into errors_
404  };
405 
408  bool allowNonStringKeyErrors_{false}; // See the setter's docblock.
409 };
410 
411 } // namespace folly
412 
size_t parse(const char *buf, size_t len)
Definition: test.c:1591
void * object
Definition: AtFork.cpp:32
char b
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
DynamicParser(OnError on_error, const folly::dynamic *d)
STL namespace.
auto on_error(Fns...fns) -> on_error_fn< Fns... >
Definition: boosters.h:273
ParserStack(const folly::dynamic *input)
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define FOLLY_EXPORT
Definition: CPortability.h:133
in_place_tag in_place(in_place_tag={})
Definition: Utility.h:235
requires E e noexcept(noexcept(s.error(std::move(e))))
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
DynamicParser & setAllowNonStringKeyErrors(bool b)
const folly::dynamic * value_
const folly::dynamic & value() const
std::enable_if< detail::is_chrono_conversion< Tgt, Src >::value, Tgt >::type to(const Src &value)
Definition: Conv.h:677
const folly::dynamic & error() const
DynamicParserLogicError(Args &&...args)
std::string toPseudoJson(const folly::dynamic &d)
std::vector< folly::dynamic * > subErrors_
std::unique_ptr< AltStackBuffer > stack_
const char * string
Definition: Conv.cpp:212
std::vector< const folly::dynamic * > unmaterializedSubErrorKeys_
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
KeyT k
const folly::dynamic & key() const
DynamicParserParseError(folly::dynamic error)
folly::dynamic releaseErrors()