blob: b53cbf4e9989736bcb9a3de3cdc2f6877ad2f61b [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/tools/json_merge/json_merge.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/istreamwrapper.h"
#include "rapidjson/ostreamwrapper.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/writer.h"
namespace {
bool MergeObject(rapidjson::Value& merge_to, const rapidjson::Value& merge_from,
rapidjson::MemoryPoolAllocator<>& allocator, const std::string& input_name,
std::ostream& errors, bool deep_merge) {
for (auto value_it = merge_from.MemberBegin(); value_it != merge_from.MemberEnd(); ++value_it) {
auto old_member = merge_to.FindMember(value_it->name);
if (old_member != merge_to.MemberEnd()) {
if (deep_merge && old_member->value.IsObject() && value_it->value.IsObject()) {
MergeObject(old_member->value, value_it->value, allocator, input_name, errors, true);
continue;
}
errors << input_name << " has a conflicting value for key \"" << value_it->name.GetString()
<< "\"!\n";
return false;
}
merge_to.AddMember(rapidjson::Value(value_it->name, allocator).Move(),
rapidjson::Value(value_it->value, allocator).Move(), allocator);
}
return true;
}
const int kRelaxedFlags = rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag;
} // namespace
int JSONMerge(const std::vector<struct input_file>& inputs, std::ostream& output,
std::ostream& errors, const MergeConfig& config) {
rapidjson::Document merged;
merged.SetObject();
auto& allocator = merged.GetAllocator();
for (auto input_it = inputs.begin(); input_it != inputs.end(); ++input_it) {
rapidjson::IStreamWrapper isw(*input_it->contents.get());
rapidjson::Document input_doc;
rapidjson::ParseResult parse_result = config.relaxed_input
? input_doc.ParseStream<kRelaxedFlags>(isw)
: input_doc.ParseStream(isw);
if (!parse_result) {
errors << "Failed to parse " << input_it->name << "!\n";
errors << rapidjson::GetParseError_En(parse_result.Code()) << " (offset "
<< parse_result.Offset() << ")\n";
return 1;
}
if (!input_doc.IsObject()) {
errors << input_it->name << " is not a JSON object!\n";
return 1;
}
if (!MergeObject(merged, input_doc, allocator, input_it->name, errors, config.deep_merge))
return 1;
}
rapidjson::OStreamWrapper osw(output);
if (config.minify) {
rapidjson::Writer<rapidjson::OStreamWrapper> writer(osw);
merged.Accept(writer);
} else {
rapidjson::PrettyWriter<rapidjson::OStreamWrapper> writer(osw);
merged.Accept(writer);
}
return 0;
}