| 95 | } |
| 96 | |
| 97 | void MakeDiff( |
| 98 | NJson::TJsonValue& container, |
| 99 | const TYandexConfig::Section* source, |
| 100 | const TYandexConfig::Section* target, |
| 101 | const TString& parentPrefix = TString()) { |
| 102 | Y_ABORT_UNLESS(target); |
| 103 | const TString& prefix = parentPrefix ? (parentPrefix + ".") : parentPrefix; |
| 104 | |
| 105 | for (const auto& [name, value]: target->GetDirectives()) { |
| 106 | const auto p = source ? source->GetDirectives().FindPtr(name) : nullptr; |
| 107 | if (!p || TString(*p) != value) { |
| 108 | container[prefix + name] = value; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | if (source) { |
| 113 | for (const auto& [name, value] : source->GetDirectives()) { |
| 114 | if (!target->GetDirectives().contains(name)) { |
| 115 | container[prefix + name] = "__remove__"; |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | TMap<TCiString, std::array<TVector<const TYandexConfig::Section *>, 2>> alignedSections; |
| 121 | |
| 122 | auto fillSections = [&](const TYandexConfig::TSectionsMap& sections, size_t targetIndex) { |
| 123 | for (const auto& [sectionName, section] : sections) { |
| 124 | alignedSections[sectionName][targetIndex].push_back(section); |
| 125 | } |
| 126 | }; |
| 127 | |
| 128 | if (source) { |
| 129 | fillSections(source->GetAllChildren(), 0); |
| 130 | } |
| 131 | fillSections(target->GetAllChildren(), 1); |
| 132 | |
| 133 | for (const auto& [sectionName, pair]: alignedSections) { |
| 134 | // Cannot use structured binding on std::array here because of a bug in MSVC. |
| 135 | const auto& sourceSections = pair[0]; |
| 136 | const auto& targetSections = pair[1]; |
| 137 | const bool needsIndex = targetSections.size() > 1; |
| 138 | |
| 139 | if (targetSections.empty()) { |
| 140 | Y_ABORT_UNLESS(source); |
| 141 | container[prefix + sectionName] = "__remove_all__"; |
| 142 | } else { |
| 143 | for (const size_t i: xrange(targetSections.size())) { |
| 144 | MakeDiff( |
| 145 | container, |
| 146 | i < sourceSections.size() ? sourceSections[i] : nullptr, |
| 147 | targetSections[i], |
| 148 | !needsIndex ? prefix + sectionName : prefix + sectionName + '[' + ToString(i) + ']'); |
| 149 | } |
| 150 | if (sourceSections.size() > targetSections.size()) { |
| 151 | container[ |
| 152 | prefix + |
| 153 | sectionName + |
| 154 | '[' + |