12#include <fmt/format.h>
13#include <boost/container/deque.hpp>
14#include <boost/container/options.hpp>
16#include <userver/compiler/demangle.hpp>
17#include <userver/formats/common/type.hpp>
18#include <userver/utils/assert.hpp>
20USERVER_NAMESPACE_BEGIN
22namespace formats::common {
47template <
typename ValueFrom,
typename ValueToBuilder>
48class ConversionStack
final {
54 stack_.emplace_back(std::move(value));
57 ConversionStack(ConversionStack&&) =
delete;
58 ConversionStack& operator=(ConversionStack&&) =
delete;
61 bool IsParsed()
const {
return stack_.front().is_parsed; }
66 return std::move(stack_.front().to).value();
71 if (stack_.front().is_parsed) {
72 return stack_.front().from;
73 }
else if (!stack_.back().is_parsed) {
74 return stack_.back().from;
76 return stack_[stack_.size() - 2].from;
85 UASSERT(!stack_.front().is_parsed && !stack_.back().is_parsed);
86 SetCurrent(stack_.back().from.
template As<T>());
96 stack_.back().to.emplace(std::forward<T>(value));
97 stack_.back().is_parsed =
true;
102 if (!stack_.back().is_parsed) {
103 if (!stack_.back().to) {
104 if (stack_.back().from.IsObject()) {
105 stack_.back().to.emplace(common::Type::kObject);
106 }
else if (stack_.back().from.IsArray()) {
107 stack_.back().to.emplace(common::Type::kArray);
109 UINVARIANT(
false,
"Type mismatch");
112 if (stack_.back().current_parsing_elem) {
113 ++*stack_.back().current_parsing_elem;
115 stack_.back().current_parsing_elem = stack_.back().from.begin();
117 if (stack_.back().current_parsing_elem.value() == stack_.back().from.end()) {
118 stack_.back().is_parsed =
true;
121 auto new_from = *stack_.back().current_parsing_elem.value();
122 stack_.emplace_back(std::move(new_from));
125 UASSERT(stack_.size() > 1);
126 auto& current_docs = stack_[stack_.size() - 2];
127 if (current_docs.from.IsObject()) {
128 current_docs.to.value()[current_docs.current_parsing_elem->GetName()] = std::move(stack_.back().to.value());
129 }
else if (current_docs.from.IsArray()) {
130 current_docs.to->PushBack(std::move(stack_.back().to.value()));
132 UASSERT_MSG(
false,
"Type mismatch");
138 struct StackFrame
final {
139 explicit StackFrame(ValueFrom&& from)
140 : from(std::move(from))
142 explicit StackFrame(
const ValueFrom& from)
146 const ValueFrom from;
147 std::optional<ValueToBuilder> to{};
148 std::optional<
typename ValueFrom::const_iterator> current_parsing_elem{};
149 bool is_parsed{
false};
155 boost::container::deque<StackFrame,
void, boost::container::deque_options<boost::container::block_size<16>>::type>
164template <
typename ValueTo,
typename ValueFrom>
166 if (value.IsMissing()) {
167 throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
168 "Failed to convert value at '{}' from {} to {}: missing value",
170 compiler::GetTypeName<std::decay_t<ValueFrom>>(),
171 compiler::GetTypeName<ValueTo>()
174 formats::common::ConversionStack<std::decay_t<ValueFrom>,
typename ValueTo::Builder>
175 conversion_stack(std::forward<ValueFrom>(value));
176 while (!conversion_stack.IsParsed()) {
177 const auto& from = conversion_stack.GetNextFrom();
179 conversion_stack.
template CastCurrentPrimitive<
bool>();
180 }
else if (from.IsInt()) {
181 conversion_stack.
template CastCurrentPrimitive<
int>();
182 }
else if (from.IsInt64()) {
183 conversion_stack.
template CastCurrentPrimitive<std::int64_t>();
184 }
else if (from.IsUInt64()) {
185 conversion_stack.
template CastCurrentPrimitive<std::uint64_t>();
186 }
else if (from.IsDouble()) {
187 conversion_stack.
template CastCurrentPrimitive<
double>();
188 }
else if (from.IsString()) {
189 conversion_stack.
template CastCurrentPrimitive<std::string>();
190 }
else if (from.IsNull()) {
191 conversion_stack.SetCurrent(common::Type::kNull);
192 }
else if (from.IsArray() || from.IsObject()) {
193 conversion_stack.EnterItems();
195 throw typename std::decay_t<ValueFrom>::Exception(fmt::format(
196 "Failed to convert value at '{}' from {} to {}: unknown node type",
198 compiler::GetTypeName<std::decay_t<ValueFrom>>(),
199 compiler::GetTypeName<ValueTo>()
203 return std::move(conversion_stack).GetParsed().ExtractValue();