in extensions/standard-processors/utils/JoltUtils.cpp [819:948]
void putValue(const Spec::Context& ctx, const Spec::Destination& dest, const rapidjson::Value& val, rapidjson::Document& output) {
std::vector<std::pair<std::string, Spec::MemberType>> evaled_dest;
for (auto& [templ, type] : dest) {
if (auto* val_ref = std::get_if<Spec::ValueRef>(&templ)) {
auto* root = ctx.find(val_ref->first);
if (!root) {
throw Exception(GENERAL_EXCEPTION, fmt::format("Could not find ancestor at index {} from {}", val_ref->first, ctx.path()));
}
if (!root->node) {
return;
}
std::reference_wrapper<const rapidjson::Value> member_value = std::ref(*root->node);
if (auto inner_member_value = resolvePath(ctx, ctx.path(), *root->node, val_ref->second)) {
member_value = inner_member_value.value();
} else {
auto sub_path = toString(ctx, val_ref->second);
ctx.log([&] (auto logger) {
logger->log_trace("Could not find member at @({},{} as {}) from {}", val_ref->first, sub_path.first, sub_path.second, ctx.path());
}, [] (auto) {});
// do not write anything and do not throw
return;
}
if (type == Spec::MemberType::INDEX) {
size_t idx{};
if (member_value.get().IsUint64()) {
idx = gsl::narrow<size_t>(member_value.get().GetUint64());
} else if (member_value.get().IsInt64()) {
int64_t idx_val = member_value.get().GetInt64();
if (idx_val < 0) {
return;
}
idx = gsl::narrow<size_t>(idx_val);
} else if (member_value.get().IsDouble()) {
// no words
double idx_val = member_value.get().GetDouble();
if (idx_val < 0) {
return;
}
idx = static_cast<size_t>(idx_val);
} else if (member_value.get().IsString()) {
// amazing... not
if (isAllDigits(member_value.get().GetString(), member_value.get().GetString() + member_value.get().GetStringLength())) {
idx = std::stoull(std::string{member_value.get().GetString(), member_value.get().GetStringLength()});
} else {
return;
}
} else {
return;
}
evaled_dest.push_back({std::to_string(idx), type});
} else {
auto member = jsonValueToString(member_value);
if (!member) {
return;
}
evaled_dest.push_back({std::move(member.value()), type});
}
continue;
}
if (auto* match_idx = std::get_if<Spec::MatchingIndex>(&templ)) {
gsl_Expects(type == Spec::MemberType::INDEX);
auto* target = ctx.find(*match_idx);
if (!target) {
throw Exception(GENERAL_EXCEPTION, fmt::format("Could not find ancestor at index {} from {}", *match_idx, ctx.path()));
}
evaled_dest.push_back({std::to_string(target->match_count), type});
continue;
}
// empty segment is self-reference, e.g. a..b == a.b
if (type == Spec::MemberType::FIELD && get<Spec::Template>(templ).empty()) {
continue;
}
evaled_dest.push_back({get<Spec::Template>(templ).eval(ctx), type});
}
std::reference_wrapper<rapidjson::Value> target = output;
for (auto& [member, type] : evaled_dest) {
if (type == Spec::MemberType::INDEX) {
if (!target.get().IsArray()) {
if (!target.get().IsNull()) {
throw Exception(GENERAL_EXCEPTION, "Cannot write based on index into non-array");
}
target.get().SetArray();
}
size_t idx = [&, member_ptr = &member] () -> size_t {
if (member_ptr->empty()) {
// an empty index like "field.inner[]" means to append to the end
return gsl::narrow<size_t>(target.get().Size());
}
try {
return std::stoull(*member_ptr);
} catch (const std::exception&) {
throw Exception(GENERAL_EXCEPTION, fmt::format("Could not convert '{}' to index", *member_ptr));
}
}();
target.get().Reserve(gsl::narrow<rapidjson::SizeType>(idx + 1), output.GetAllocator());
for (size_t arr_idx = target.get().Size(); arr_idx <= idx; ++arr_idx) {
target.get().PushBack(rapidjson::Value{}, output.GetAllocator());
}
target = target.get()[gsl::narrow<rapidjson::SizeType>(idx)];
} else {
if (!target.get().IsObject()) {
if (!target.get().IsNull()) {
throw Exception(GENERAL_EXCEPTION, "Cannot write member into non-object");
}
target.get().SetObject();
}
if (!target.get().HasMember(member)) {
target.get().AddMember(rapidjson::Value{member.c_str(), gsl::narrow<rapidjson::SizeType>(member.size()), output.GetAllocator()}, rapidjson::Value{}, output.GetAllocator());
}
target = target.get()[member];
}
}
if (!target.get().IsNull()) {
if (!target.get().IsArray()) {
// put it in an array
rapidjson::Value tmp{target.get().Move(), output.GetAllocator()};
target.get().SetArray();
target.get().GetArray().PushBack(tmp.Move(), output.GetAllocator());
}
target.get().PushBack(rapidjson::Value{}, output.GetAllocator());
target = target.get()[target.get().GetArray().Size() - 1];
}
target.get().CopyFrom(val, output.GetAllocator());
}