in mcrouter/lib/config/ConfigPreprocessor.cpp [1938:2041]
dynamic ConfigPreprocessor::expandMacros(dynamic json, const Context& context)
const {
NestedLimitGuard nestedGuard(nestedLimit_);
if (json.isString()) {
// look for macros in string
return expandStringMacro(json.stringPiece(), context);
} else if (json.isObject()) {
folly::Optional<Context> extContext;
if (auto jVars = json.get_ptr("vars")) {
checkLogic(
jVars->isObject(), "vars is {}, expected object", jVars->typeName());
extContext.emplace(Context::Extended, context);
// since vars may use other vars from local context, we
// do the initialization in two steps: first add them to context,
// then lazily expand
for (auto& it : jVars->items()) {
auto var = const_cast<dynamic&>(it.second);
extContext->addLocal(it.first.stringPiece(), std::move(var));
}
for (const auto& varName : jVars->keys()) {
extContext->doLazyExpand(varName.stringPiece());
}
json.erase("vars");
}
const auto& localContext = extContext ? *extContext : context;
// check for built-in calls and long-form macros
auto typeIt = json.find("type");
if (typeIt != json.items().end()) {
auto type = expandMacros(typeIt->second, localContext);
if (type.isString()) {
auto typeStr = type.stringPiece();
// built-in call
auto builtInIt = builtInCalls_.find(typeStr);
if (builtInIt != builtInCalls_.end()) {
try {
return builtInIt->second(std::move(json), localContext);
} catch (const std::logic_error& e) {
throwLogic("Built-in '{}':\n{}", typeStr, e.what());
}
}
// long form macro substitution
auto macroIt = macros_.find(typeStr);
if (macroIt != macros_.end()) {
const auto& inner = macroIt->second;
try {
return inner->getResult(std::move(json), localContext);
} catch (const std::logic_error& e) {
throwLogic("Macro '{}':\n{}", typeStr, e.what());
}
}
}
}
// raw object
dynamic result = dynamic::object();
for (const auto& it : json.items()) {
auto& value = const_cast<dynamic&>(it.second);
try {
auto nKey = expandMacros(it.first, localContext);
checkLogic(nKey.isString(), "Expanded key is not a string");
result.insert(
std::move(nKey), expandMacros(std::move(value), localContext));
// Since new json is being created with expanded macros we need
// to re-populate the config metadata map with new dynamic objects
// created in the process.
const auto nKeyPtr = result.get_ptr(it.first);
const auto nKeyJsonPtr = json.get_ptr(it.first);
if (nKeyPtr && nKeyJsonPtr) {
const auto resMetadataPtr = configMetadataMap_.find(nKeyJsonPtr);
const auto jsonMetadataPtr = configMetadataMap_.find(nKeyPtr);
if (resMetadataPtr != configMetadataMap_.end()) {
// If it already exists in the map, replace it
// Otherwise, create an entry in the map
if (jsonMetadataPtr == configMetadataMap_.end()) {
configMetadataMap_.emplace(nKeyPtr, resMetadataPtr->second);
} else {
jsonMetadataPtr->second = resMetadataPtr->second;
}
}
}
} catch (const std::logic_error& e) {
throwLogic(
"Raw object property '{}':\n{}", it.first.stringPiece(), e.what());
}
}
return result;
} else if (json.isArray()) {
for (size_t i = 0; i < json.size(); ++i) {
auto& value = json[i];
try {
value = expandMacros(std::move(value), context);
} catch (const std::logic_error& e) {
throwLogic("Array element #{}:\n{}", i, e.what());
}
}
return json;
} else {
// some number or other type of json. Return 'as is'.
return json;
}
}