source/CanonicalName.cpp (124 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <boost/algorithm/string/replace.hpp>
#include <json/value.h>
#include <re2/re2.h>
#include <mariana-trench/Assert.h>
#include <mariana-trench/CanonicalName.h>
#include <mariana-trench/JsonValidation.h>
#include <mariana-trench/Log.h>
namespace marianatrench {
namespace {
static const re2::RE2 add_underscore_regex("([a-z])([A-Z])");
void convert_to_lower_underscore(std::string& input) {
RE2::GlobalReplace(&input, add_underscore_regex, "\\1_\\2");
std::transform(
input.begin(), input.end(), input.begin(), [](unsigned char c) {
return std::tolower(c);
});
}
} // namespace
bool CanonicalName::is_via_type_of_template() const {
auto value = template_value();
if (!value) {
return false;
}
return value->find(k_via_type_of_marker) != std::string::npos;
}
std::optional<CanonicalName> CanonicalName::instantiate(
const Method* method,
const std::vector<const Feature*>& via_type_ofs) const {
auto value = template_value();
mt_assert(value);
auto canonical_name = *value;
if (canonical_name.find(k_leaf_name_marker) != std::string::npos) {
auto callee_name = method->signature();
boost::algorithm::replace_all(
canonical_name, k_leaf_name_marker, callee_name);
}
// Converts Lcom/SomeMutationData;.setPhoneField:.* to
// some_mutation:phone_field which follows the graphql notation
if (canonical_name.find(k_graphql_root_marker) != std::string::npos) {
auto class_signature = method->get_class()->get_name()->str();
auto pos = class_signature.find_last_of("/");
if (pos != std::string::npos && class_signature.size() > (pos + 2)) {
std::string class_name =
class_signature.substr(pos + 1, class_signature.size() - pos - 2);
boost::replace_last(class_name, "Data", "");
convert_to_lower_underscore(class_name);
std::string method_name = method->get_name();
boost::replace_first(method_name, "set", "");
convert_to_lower_underscore(method_name);
boost::algorithm::replace_all(
canonical_name,
k_graphql_root_marker,
class_name + ":" + method_name);
}
}
if (canonical_name.find(k_via_type_of_marker) != std::string::npos) {
if (via_type_ofs.size() == 0) {
WARNING(
2,
"Could not instantiate canonical name template '{}'. Via-type-of feature not available.",
*value);
return std::nullopt;
} else if (via_type_ofs.size() > 1) {
ERROR(
1,
"Could not instantiate canonical name template '{}'. Unable to disambiguate between {} via-type-of features.",
*value,
via_type_ofs.size());
// Should have been verified when parsing models during model-generation.
mt_assert(false);
return std::nullopt;
}
auto via_type_of = *via_type_ofs.begin();
boost::algorithm::replace_all(
canonical_name, k_via_type_of_marker, via_type_of->name());
}
return CanonicalName(CanonicalName::InstantiatedValue{canonical_name});
}
CanonicalName CanonicalName::from_json(const Json::Value& value) {
JsonValidation::validate_object(value);
if (value.isMember("template")) {
auto template_value = JsonValidation::string(value["template"]);
if (value.isMember("instantiated")) {
throw JsonValidationError(
value,
/* field */ std::nullopt,
"either 'template' or 'instantiated' value but not both.");
}
return CanonicalName(CanonicalName::TemplateValue{template_value});
}
if (value.isMember("instantiated")) {
auto instantiated_value = JsonValidation::string(value["instantiated"]);
return CanonicalName(CanonicalName::InstantiatedValue{instantiated_value});
}
throw JsonValidationError(
value,
/* field */ std::nullopt,
"either 'template' or 'instantiated' value.");
}
Json::Value CanonicalName::to_json() const {
auto result = Json::Value(Json::objectValue);
auto value = template_value();
if (value) {
result["template"] = *value;
return result;
}
value = instantiated_value();
if (value) {
result["instantiated"] = *value;
return result;
}
mt_unreachable();
return result;
}
std::ostream& operator<<(std::ostream& out, const CanonicalName& root) {
auto value = root.template_value();
if (value) {
return out << "template=" << *value;
}
value = root.instantiated_value();
if (value) {
return out << "instantiated=" << *value;
}
mt_unreachable();
return out;
}
} // namespace marianatrench