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 {
52 ConversionStack(ConversionStack&&) =
delete;
53 ConversionStack& operator=(ConversionStack&&) =
delete;
56 bool IsParsed()
const {
return stack_.front().is_parsed; }
61 return std::move(stack_.front().to).value();
66 if (stack_.front().is_parsed) {
67 return stack_.front().from;
68 }
else if (!stack_.back().is_parsed) {
69 return stack_.back().from;
71 return stack_[stack_.size() - 2].from;
80 UASSERT(!stack_.front().is_parsed && !stack_.back().is_parsed);
81 SetCurrent(stack_.back().from.
template As<T>());
91 stack_.back().to.emplace(std::forward<T>(value));
92 stack_.back().is_parsed =
true;
97 if (!stack_.back().is_parsed) {
98 if (!stack_.back().to) {
99 if (stack_.back().from.IsObject()) {
100 stack_.back().to.emplace(common::Type::kObject);
101 }
else if (stack_.back().from.IsArray()) {
102 stack_.back().to.emplace(common::Type::kArray);
107 if (stack_.back().current_parsing_elem) {
108 ++*stack_.back().current_parsing_elem;
110 stack_.back().current_parsing_elem = stack_.back().from.begin();
112 if (stack_.back().current_parsing_elem.value() == stack_.back().from.end()) {
113 stack_.back().is_parsed =
true;
116 auto new_from = *stack_.back().current_parsing_elem.value();
117 stack_.emplace_back(std::move(new_from));
121 auto& current_docs = stack_[stack_.size() - 2];
122 if (current_docs.from.IsObject()) {
123 current_docs.to.value()[current_docs.current_parsing_elem->GetName()] = std::move(stack_.back().to.value());
124 }
else if (current_docs.from.IsArray()) {
125 current_docs.to->PushBack(std::move(stack_.back().to.value()));
133 struct StackFrame
final {
134 explicit StackFrame(ValueFrom&& from) : from(std::move(from)) {}
135 explicit StackFrame(
const ValueFrom& from) : from(from) {}
137 const ValueFrom from;
138 std::optional<ValueToBuilder> to{};
139 std::optional<
typename ValueFrom::const_iterator> current_parsing_elem{};
140 bool is_parsed{
false};
143 boost::container::small_vector<StackFrame, 10> stack_;
151template <
typename ValueTo,
typename ValueFrom>
153 if (value.IsMissing()) {
154 throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
155 "Failed to convert value at '{}' from {} to {}: missing value",
157 compiler::GetTypeName<std::decay_t<ValueFrom>>(),
158 compiler::GetTypeName<ValueTo>()
161 formats::common::ConversionStack<std::decay_t<ValueFrom>,
typename ValueTo::Builder> conversion_stack(
162 std::forward<ValueFrom>(value)
164 while (!conversion_stack.IsParsed()) {
165 const auto& from = conversion_stack.GetNextFrom();
167 conversion_stack.
template CastCurrentPrimitive<
bool>();
168 }
else if (from.IsInt()) {
169 conversion_stack.
template CastCurrentPrimitive<
int>();
170 }
else if (from.IsInt64()) {
171 conversion_stack.
template CastCurrentPrimitive<std::int64_t>();
172 }
else if (from.IsUInt64()) {
173 conversion_stack.
template CastCurrentPrimitive<std::uint64_t>();
174 }
else if (from.IsDouble()) {
175 conversion_stack.
template CastCurrentPrimitive<
double>();
176 }
else if (from.IsString()) {
177 conversion_stack.
template CastCurrentPrimitive<std::string>();
178 }
else if (from.IsNull()) {
179 conversion_stack.SetCurrent(common::Type::kNull);
180 }
else if (from.IsArray() || from.IsObject()) {
181 conversion_stack.EnterItems();
183 throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
184 "Failed to convert value at '{}' from {} to {}: unknown node type",
186 compiler::GetTypeName<std::decay_t<ValueFrom>>(),
187 compiler::GetTypeName<ValueTo>()
191 return std::move(conversion_stack).GetParsed().ExtractValue();