12#include <fmt/format.h> 
   13#include <boost/container/small_vector.hpp> 
   15#include <userver/compiler/demangle.hpp> 
   16#include <userver/formats/common/type.hpp> 
   17#include <userver/utils/assert.hpp> 
   19USERVER_NAMESPACE_BEGIN
 
   46template <
typename ValueFrom, 
typename ValueToBuilder>
 
   47class ConversionStack 
final {
 
   51    stack_.emplace_back(std::move(value));
 
   54  ConversionStack(ConversionStack&&) = 
delete;
 
   55  ConversionStack& operator=(ConversionStack&&) = 
delete;
 
   58  bool IsParsed() 
const { 
return stack_.front().is_parsed; }
 
   63    return std::move(stack_.front().to).value();
 
   68    if (stack_.front().is_parsed) {
 
   69      return stack_.front().from;
 
   70    } 
else if (!stack_.back().is_parsed) {
 
   71      return stack_.back().from;
 
   73      return stack_[stack_.size() - 2].from;
 
   82    UASSERT(!stack_.front().is_parsed && !stack_.back().is_parsed);
 
   83    SetCurrent(stack_.back().from.
template As<T>());
 
   93    stack_.back().to.emplace(std::forward<T>(value));
 
   94    stack_.back().is_parsed = 
true;
 
   99    if (!stack_.back().is_parsed) {
 
  100      if (!stack_.back().to) {
 
  101        if (stack_.back().from.IsObject()) {
 
  102          stack_.back().to.emplace(common::Type::kObject);
 
  103        } 
else if (stack_.back().from.IsArray()) {
 
  104          stack_.back().to.emplace(common::Type::kArray);
 
  109      if (stack_.back().current_parsing_elem) {
 
  110        ++*stack_.back().current_parsing_elem;
 
  112        stack_.back().current_parsing_elem = stack_.back().from.begin();
 
  114      if (stack_.back().current_parsing_elem.value() ==
 
  115          stack_.back().from.end()) {
 
  116        stack_.back().is_parsed = 
true;
 
  119      auto new_from = *stack_.back().current_parsing_elem.value();
 
  120      stack_.emplace_back(std::move(new_from));
 
  124    auto& current_docs = stack_[stack_.size() - 2];
 
  125    if (current_docs.from.IsObject()) {
 
  126      current_docs.to.value()[current_docs.current_parsing_elem->GetName()] =
 
  127          std::move(stack_.back().to.value());
 
  128    } 
else if (current_docs.from.IsArray()) {
 
  129      current_docs.to->PushBack(std::move(stack_.back().to.value()));
 
  137  struct StackFrame 
final {
 
  138    explicit StackFrame(ValueFrom&& from) : from(std::move(from)) {}
 
  139    explicit StackFrame(
const ValueFrom& from) : from(from) {}
 
  141    const ValueFrom from;
 
  142    std::optional<ValueToBuilder> to{};
 
  143    std::optional<
typename ValueFrom::const_iterator> current_parsing_elem{};
 
  144    bool is_parsed{
false};
 
  147  boost::container::small_vector<StackFrame, 10> stack_;
 
  155template <
typename ValueTo, 
typename ValueFrom>
 
  157  if (value.IsMissing()) {
 
  158    throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
 
  159        "Failed to convert value at '{}' from {} to {}: missing value",
 
  160        value.GetPath(), compiler::GetTypeName<std::decay_t<ValueFrom>>(),
 
  161        compiler::GetTypeName<ValueTo>()));
 
  163  formats::common::ConversionStack<std::decay_t<ValueFrom>,
 
  164                                   typename ValueTo::Builder>
 
  165      conversion_stack(std::forward<ValueFrom>(value));
 
  166  while (!conversion_stack.IsParsed()) {
 
  167    const auto& from = conversion_stack.GetNextFrom();
 
  169      conversion_stack.
template CastCurrentPrimitive<
bool>();
 
  170    } 
else if (from.IsInt()) {
 
  171      conversion_stack.
template CastCurrentPrimitive<
int>();
 
  172    } 
else if (from.IsInt64()) {
 
  173      conversion_stack.
template CastCurrentPrimitive<std::int64_t>();
 
  174    } 
else if (from.IsUInt64()) {
 
  175      conversion_stack.
template CastCurrentPrimitive<std::uint64_t>();
 
  176    } 
else if (from.IsDouble()) {
 
  177      conversion_stack.
template CastCurrentPrimitive<
double>();
 
  178    } 
else if (from.IsString()) {
 
  179      conversion_stack.
template CastCurrentPrimitive<std::string>();
 
  180    } 
else if (from.IsNull()) {
 
  181      conversion_stack.SetCurrent(common::Type::kNull);
 
  182    } 
else if (from.IsArray() || from.IsObject()) {
 
  183      conversion_stack.EnterItems();
 
  185      throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
 
  186          "Failed to convert value at '{}' from {} to {}: unknown node type",
 
  187          from.GetPath(), compiler::GetTypeName<std::decay_t<ValueFrom>>(),
 
  188          compiler::GetTypeName<ValueTo>()));
 
  191  return std::move(conversion_stack).GetParsed().ExtractValue();