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();