in src/SchemaGenerator.cpp [1850:2946]
void Generator::outputObjectImplementation(
std::ostream& sourceFile, const ObjectType& objectType, bool isQueryType) const
{
using namespace std::literals;
if (_loader.isIntrospection())
{
// Output the public constructor which calls through to the service::Object constructor
// with arguments that declare the set of types it implements and bind the fields to the
// resolver methods.
sourceFile << objectType.cppType << R"cpp(::)cpp" << objectType.cppType
<< R"cpp((std::shared_ptr<)cpp" << SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::)cpp" << objectType.cppType << R"cpp(> pimpl))cpp";
}
else
{
// Output the private constructor which calls through to the service::Object constructor
// with arguments that declare the set of types it implements and bind the fields to the
// resolver methods.
sourceFile << objectType.cppType << R"cpp(::)cpp" << objectType.cppType
<< R"cpp((std::unique_ptr<const Concept>&& pimpl))cpp";
}
sourceFile << R"cpp( noexcept
: service::Object{ getTypeNames(), getResolvers() })cpp";
if (!_options.noIntrospection && isQueryType)
{
sourceFile << R"cpp(
, _schema { GetSchema() })cpp";
}
if (_loader.isIntrospection())
{
sourceFile << R"cpp(
, _pimpl { std::make_unique<Model<)cpp"
<< SchemaLoader::getIntrospectionNamespace() << R"cpp(::)cpp"
<< objectType.cppType << R"cpp(>>(std::move(pimpl)) })cpp";
}
else
{
sourceFile << R"cpp(
, _pimpl { std::move(pimpl) })cpp";
}
sourceFile << R"cpp(
{
}
)cpp";
if (_loader.isIntrospection())
{
sourceFile << R"cpp(
)cpp" << objectType.cppType
<< R"cpp(::~)cpp" << objectType.cppType << R"cpp(()
{
// This is empty, but explicitly defined here so that it can access the un-exported destructor
// of the implementation type.
}
)cpp";
}
sourceFile << R"cpp(
service::TypeNames )cpp"
<< objectType.cppType << R"cpp(::getTypeNames() const noexcept
{
return {
)cpp";
for (const auto& interfaceName : objectType.interfaces)
{
sourceFile << R"cpp( R"gql()cpp" << interfaceName << R"cpp()gql"sv,
)cpp";
}
for (const auto& unionName : objectType.unions)
{
sourceFile << R"cpp( R"gql()cpp" << unionName << R"cpp()gql"sv,
)cpp";
}
sourceFile << R"cpp( R"gql()cpp" << objectType.type << R"cpp()gql"sv
};
}
service::ResolverMap )cpp"
<< objectType.cppType << R"cpp(::getResolvers() const noexcept
{
return {
)cpp";
std::map<std::string_view, std::string, internal::shorter_or_less> resolvers;
std::transform(objectType.fields.cbegin(),
objectType.fields.cend(),
std::inserter(resolvers, resolvers.begin()),
[](const OutputField& outputField) noexcept {
std::string fieldName(outputField.cppName);
fieldName[0] =
static_cast<char>(std::toupper(static_cast<unsigned char>(fieldName[0])));
std::ostringstream output;
output << R"cpp( { R"gql()cpp" << outputField.name
<< R"cpp()gql"sv, [this](service::ResolverParams&& params) { return resolve)cpp"
<< fieldName << R"cpp((std::move(params)); } })cpp";
return std::make_pair(std::string_view { outputField.name }, output.str());
});
resolvers["__typename"sv] =
R"cpp( { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } })cpp"s;
if (!_options.noIntrospection && isQueryType)
{
resolvers["__schema"sv] =
R"cpp( { R"gql(__schema)gql"sv, [this](service::ResolverParams&& params) { return resolve_schema(std::move(params)); } })cpp"s;
resolvers["__type"sv] =
R"cpp( { R"gql(__type)gql"sv, [this](service::ResolverParams&& params) { return resolve_type(std::move(params)); } })cpp"s;
}
bool firstField = true;
for (const auto& resolver : resolvers)
{
if (!firstField)
{
sourceFile << R"cpp(,
)cpp";
}
firstField = false;
sourceFile << resolver.second;
}
sourceFile << R"cpp(
};
}
)cpp";
if (!_loader.isIntrospection())
{
sourceFile << R"cpp(
void )cpp" << objectType.cppType
<< R"cpp(::beginSelectionSet(const service::SelectionSetParams& params) const
{
_pimpl->beginSelectionSet(params);
}
void )cpp" << objectType.cppType
<< R"cpp(::endSelectionSet(const service::SelectionSetParams& params) const
{
_pimpl->endSelectionSet(params);
}
)cpp";
}
// Output each of the resolver implementations, which call the virtual property
// getters that the implementer must define.
for (const auto& outputField : objectType.fields)
{
std::string fieldName(outputField.cppName);
fieldName[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(fieldName[0])));
sourceFile << R"cpp(
service::AwaitableResolver )cpp"
<< objectType.cppType << R"cpp(::resolve)cpp" << fieldName
<< R"cpp((service::ResolverParams&& params) const
{
)cpp";
// Output a preamble to retrieve all of the arguments from the resolver parameters.
if (!outputField.arguments.empty())
{
bool firstArgument = true;
for (const auto& argument : outputField.arguments)
{
if (argument.defaultValue.type() != response::Type::Null)
{
if (firstArgument)
{
firstArgument = false;
sourceFile << R"cpp( static const auto defaultArguments = []()
{
response::Value values(response::Type::Map);
response::Value entry;
)cpp";
}
sourceFile << getArgumentDefaultValue(0, argument.defaultValue)
<< R"cpp( values.emplace_back(")cpp" << argument.name
<< R"cpp(", std::move(entry));
)cpp";
}
}
if (!firstArgument)
{
sourceFile << R"cpp(
return values;
}();
)cpp";
}
for (const auto& argument : outputField.arguments)
{
sourceFile << getArgumentDeclaration(argument,
"arg",
"params.arguments",
"defaultArguments");
}
}
sourceFile << R"cpp( std::unique_lock resolverLock(_resolverMutex);
)cpp";
if (!_loader.isIntrospection())
{
sourceFile << R"cpp( auto directives = std::move(params.fieldDirectives);
)cpp";
}
sourceFile << R"cpp( auto result = _pimpl->)cpp" << outputField.accessor << fieldName
<< R"cpp(()cpp";
bool firstArgument = _loader.isIntrospection();
if (!firstArgument)
{
sourceFile
<< R"cpp(service::FieldParams(service::SelectionSetParams{ params }, std::move(directives)))cpp";
}
if (!outputField.arguments.empty())
{
for (const auto& argument : outputField.arguments)
{
std::string argumentName(argument.cppName);
argumentName[0] =
static_cast<char>(std::toupper(static_cast<unsigned char>(argumentName[0])));
if (!firstArgument)
{
sourceFile << R"cpp(, )cpp";
}
sourceFile << R"cpp(std::move(arg)cpp" << argumentName << R"cpp())cpp";
firstArgument = false;
}
}
sourceFile << R"cpp();
resolverLock.unlock();
return )cpp" << getResultAccessType(outputField)
<< R"cpp(::convert)cpp" << getTypeModifiers(outputField.modifiers)
<< R"cpp((std::move(result), std::move(params));
}
)cpp";
}
sourceFile << R"cpp(
service::AwaitableResolver )cpp"
<< objectType.cppType
<< R"cpp(::resolve_typename(service::ResolverParams&& params) const
{
return service::ModifiedResult<std::string>::convert(std::string{ R"gql()cpp"
<< objectType.type << R"cpp()gql" }, std::move(params));
}
)cpp";
if (!_options.noIntrospection && isQueryType)
{
sourceFile
<< R"cpp(
service::AwaitableResolver )cpp"
<< objectType.cppType << R"cpp(::resolve_schema(service::ResolverParams&& params) const
{
return service::ModifiedResult<service::Object>::convert(std::static_pointer_cast<service::Object>(std::make_shared<)cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::object::Schema>(std::make_shared<)cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::Schema>(_schema))), std::move(params));
}
service::AwaitableResolver )cpp"
<< objectType.cppType << R"cpp(::resolve_type(service::ResolverParams&& params) const
{
auto argName = service::ModifiedArgument<std::string>::require("name", params.arguments);
const auto& baseType = _schema->LookupType(argName);
std::shared_ptr<)cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::object::Type> result { baseType ? std::make_shared<)cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::object::Type>(std::make_shared<)cpp"
<< SchemaLoader::getIntrospectionNamespace() << R"cpp(::Type>(baseType)) : nullptr };
return service::ModifiedResult<)cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::object::Type>::convert<service::TypeModifier::Nullable>(result, std::move(params));
}
)cpp";
}
}
void Generator::outputObjectIntrospection(
std::ostream& sourceFile, const ObjectType& objectType) const
{
outputIntrospectionInterfaces(sourceFile, objectType.cppType, objectType.interfaces);
outputIntrospectionFields(sourceFile, objectType.cppType, objectType.fields);
}
void Generator::outputIntrospectionInterfaces(std::ostream& sourceFile, std::string_view cppType,
const std::vector<std::string_view>& interfaces) const
{
if (!interfaces.empty())
{
bool firstInterface = true;
sourceFile << R"cpp( type)cpp" << cppType << R"cpp(->AddInterfaces({
)cpp";
for (const auto& interfaceName : interfaces)
{
if (!firstInterface)
{
sourceFile << R"cpp(,
)cpp";
}
firstInterface = false;
sourceFile
<< R"cpp( std::static_pointer_cast<const schema::InterfaceType>(schema->LookupType(R"gql()cpp"
<< interfaceName << R"cpp()gql"sv)))cpp";
}
sourceFile << R"cpp(
});
)cpp";
}
}
void Generator::outputIntrospectionFields(
std::ostream& sourceFile, std::string_view cppType, const OutputFieldList& fields) const
{
if (fields.empty())
{
return;
}
bool firstValue = true;
sourceFile << R"cpp( type)cpp" << cppType << R"cpp(->AddFields({
)cpp";
for (const auto& objectField : fields)
{
if (!firstValue)
{
sourceFile << R"cpp(,
)cpp";
}
firstValue = false;
sourceFile << R"cpp( schema::Field::Make(R"gql()cpp" << objectField.name
<< R"cpp()gql"sv, R"md()cpp";
if (!_options.noIntrospection)
{
sourceFile << objectField.description;
}
sourceFile << R"cpp()md"sv, )cpp";
if (objectField.deprecationReason)
{
sourceFile << R"cpp(std::make_optional(R"md()cpp" << *objectField.deprecationReason
<< R"cpp()md"sv))cpp";
}
else
{
sourceFile << R"cpp(std::nullopt)cpp";
}
sourceFile << R"cpp(, )cpp"
<< getIntrospectionType(objectField.type, objectField.modifiers);
if (!objectField.arguments.empty())
{
bool firstArgument = true;
sourceFile << R"cpp(, {
)cpp";
for (const auto& argument : objectField.arguments)
{
if (!firstArgument)
{
sourceFile << R"cpp(,
)cpp";
}
firstArgument = false;
sourceFile << R"cpp( schema::InputValue::Make(R"gql()cpp"
<< argument.name << R"cpp()gql"sv, R"md()cpp";
if (!_options.noIntrospection)
{
sourceFile << argument.description;
}
sourceFile << R"cpp()md"sv, )cpp"
<< getIntrospectionType(argument.type, argument.modifiers)
<< R"cpp(, R"gql()cpp" << argument.defaultValueString
<< R"cpp()gql"sv))cpp";
}
sourceFile << R"cpp(
})cpp";
}
sourceFile << R"cpp())cpp";
}
sourceFile << R"cpp(
});
)cpp";
}
std::string Generator::getArgumentDefaultValue(
size_t level, const response::Value& defaultValue) const noexcept
{
const std::string padding(level, '\t');
std::ostringstream argumentDefaultValue;
switch (defaultValue.type())
{
case response::Type::Map:
{
const auto& members = defaultValue.get<response::MapType>();
argumentDefaultValue << padding << R"cpp( entry = []()
)cpp" << padding << R"cpp( {
)cpp" << padding << R"cpp( response::Value members(response::Type::Map);
)cpp" << padding << R"cpp( response::Value entry;
)cpp";
for (const auto& entry : members)
{
argumentDefaultValue << getArgumentDefaultValue(level + 1, entry.second) << padding
<< R"cpp( members.emplace_back(")cpp" << entry.first
<< R"cpp(", std::move(entry));
)cpp";
}
argumentDefaultValue << padding << R"cpp( return members;
)cpp" << padding << R"cpp( }();
)cpp";
break;
}
case response::Type::List:
{
const auto& elements = defaultValue.get<response::ListType>();
argumentDefaultValue << padding << R"cpp( entry = []()
)cpp" << padding << R"cpp( {
)cpp" << padding << R"cpp( response::Value elements(response::Type::List);
)cpp" << padding << R"cpp( response::Value entry;
)cpp";
for (const auto& entry : elements)
{
argumentDefaultValue << getArgumentDefaultValue(level + 1, entry) << padding
<< R"cpp( elements.emplace_back(std::move(entry));
)cpp";
}
argumentDefaultValue << padding << R"cpp( return elements;
)cpp" << padding << R"cpp( }();
)cpp";
break;
}
case response::Type::String:
{
argumentDefaultValue << padding
<< R"cpp( entry = response::Value(std::string(R"gql()cpp"
<< defaultValue.get<std::string>() << R"cpp()gql"));
)cpp";
break;
}
case response::Type::Null:
{
argumentDefaultValue << padding << R"cpp( entry = {};
)cpp";
break;
}
case response::Type::Boolean:
{
argumentDefaultValue << padding << R"cpp( entry = response::Value()cpp"
<< (defaultValue.get<bool>() ? R"cpp(true)cpp" : R"cpp(false)cpp")
<< R"cpp();
)cpp";
break;
}
case response::Type::Int:
{
argumentDefaultValue << padding
<< R"cpp( entry = response::Value(static_cast<int>()cpp"
<< defaultValue.get<int>() << R"cpp());
)cpp";
break;
}
case response::Type::Float:
{
argumentDefaultValue << padding
<< R"cpp( entry = response::Value(static_cast<double>()cpp"
<< defaultValue.get<double>() << R"cpp());
)cpp";
break;
}
case response::Type::EnumValue:
{
argumentDefaultValue
<< padding << R"cpp( entry = response::Value(response::Type::EnumValue);
entry.set<std::string>(R"gql()cpp"
<< defaultValue.get<std::string>() << R"cpp()gql");
)cpp";
break;
}
case response::Type::Scalar:
{
argumentDefaultValue << padding << R"cpp( entry = []()
)cpp" << padding << R"cpp( {
)cpp" << padding << R"cpp( response::Value scalar(response::Type::Scalar);
)cpp" << padding << R"cpp( response::Value entry;
)cpp";
argumentDefaultValue
<< padding << R"cpp( )cpp"
<< getArgumentDefaultValue(level + 1, defaultValue.get<response::ScalarType>())
<< padding << R"cpp( scalar.set<response::ScalarType>(std::move(entry));
)cpp" << padding << R"cpp( return scalar;
)cpp" << padding << R"cpp( }();
)cpp";
break;
}
}
return argumentDefaultValue.str();
}
std::string Generator::getArgumentDeclaration(const InputField& argument, const char* prefixToken,
const char* argumentsToken, const char* defaultToken) const noexcept
{
std::ostringstream argumentDeclaration;
std::string argumentName(argument.cppName);
argumentName[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(argumentName[0])));
if (argument.defaultValue.type() == response::Type::Null)
{
argumentDeclaration << R"cpp( auto )cpp" << prefixToken << argumentName << R"cpp( = )cpp"
<< getArgumentAccessType(argument) << R"cpp(::require)cpp"
<< getTypeModifiers(argument.modifiers) << R"cpp((")cpp"
<< argument.name << R"cpp(", )cpp" << argumentsToken << R"cpp();
)cpp";
}
else
{
argumentDeclaration << R"cpp( auto pair)cpp" << argumentName << R"cpp( = )cpp"
<< getArgumentAccessType(argument) << R"cpp(::find)cpp"
<< getTypeModifiers(argument.modifiers) << R"cpp((")cpp"
<< argument.name << R"cpp(", )cpp" << argumentsToken << R"cpp();
auto )cpp" << prefixToken
<< argumentName << R"cpp( = (pair)cpp" << argumentName << R"cpp(.second
? std::move(pair)cpp"
<< argumentName << R"cpp(.first)
: )cpp" << getArgumentAccessType(argument)
<< R"cpp(::require)cpp" << getTypeModifiers(argument.modifiers)
<< R"cpp((")cpp" << argument.name << R"cpp(", )cpp" << defaultToken
<< R"cpp());
)cpp";
}
return argumentDeclaration.str();
}
std::string Generator::getArgumentAccessType(const InputField& argument) const noexcept
{
std::ostringstream argumentType;
argumentType << R"cpp(service::ModifiedArgument<)cpp";
switch (argument.fieldType)
{
case InputFieldType::Builtin:
argumentType << _loader.getCppType(argument.type);
break;
case InputFieldType::Enum:
case InputFieldType::Input:
argumentType << _loader.getSchemaNamespace() << R"cpp(::)cpp"
<< _loader.getCppType(argument.type);
break;
case InputFieldType::Scalar:
argumentType << R"cpp(response::Value)cpp";
break;
}
argumentType << R"cpp(>)cpp";
return argumentType.str();
}
std::string Generator::getResultAccessType(const OutputField& result) const noexcept
{
std::ostringstream resultType;
resultType << R"cpp(service::ModifiedResult<)cpp";
switch (result.fieldType)
{
case OutputFieldType::Builtin:
case OutputFieldType::Enum:
case OutputFieldType::Interface:
case OutputFieldType::Union:
case OutputFieldType::Object:
resultType << _loader.getCppType(result.type);
break;
case OutputFieldType::Scalar:
resultType << R"cpp(response::Value)cpp";
break;
}
resultType << R"cpp(>)cpp";
return resultType.str();
}
std::string Generator::getTypeModifiers(const TypeModifierStack& modifiers) const noexcept
{
bool firstValue = true;
std::ostringstream typeModifiers;
for (auto modifier : modifiers)
{
if (firstValue)
{
typeModifiers << R"cpp(<)cpp";
firstValue = false;
}
else
{
typeModifiers << R"cpp(, )cpp";
}
switch (modifier)
{
case service::TypeModifier::Nullable:
typeModifiers << R"cpp(service::TypeModifier::Nullable)cpp";
break;
case service::TypeModifier::List:
typeModifiers << R"cpp(service::TypeModifier::List)cpp";
break;
case service::TypeModifier::None:
break;
}
}
if (!firstValue)
{
typeModifiers << R"cpp(>)cpp";
}
return typeModifiers.str();
}
std::string Generator::getIntrospectionType(
std::string_view type, const TypeModifierStack& modifiers) const noexcept
{
size_t wrapperCount = 0;
bool nonNull = true;
std::ostringstream introspectionType;
for (auto modifier : modifiers)
{
if (nonNull)
{
switch (modifier)
{
case service::TypeModifier::None:
case service::TypeModifier::List:
{
introspectionType << R"cpp(schema->WrapType()cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::TypeKind::NON_NULL, )cpp";
++wrapperCount;
break;
}
case service::TypeModifier::Nullable:
{
// If the next modifier is Nullable that cancels the non-nullable state.
nonNull = false;
break;
}
}
}
switch (modifier)
{
case service::TypeModifier::None:
{
nonNull = true;
break;
}
case service::TypeModifier::List:
{
nonNull = true;
introspectionType << R"cpp(schema->WrapType()cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::TypeKind::LIST, )cpp";
++wrapperCount;
break;
}
case service::TypeModifier::Nullable:
break;
}
}
if (nonNull)
{
introspectionType << R"cpp(schema->WrapType()cpp"
<< SchemaLoader::getIntrospectionNamespace()
<< R"cpp(::TypeKind::NON_NULL, )cpp";
++wrapperCount;
}
introspectionType << R"cpp(schema->LookupType(R"gql()cpp" << type << R"cpp()gql"sv))cpp";
for (size_t i = 0; i < wrapperCount; ++i)
{
introspectionType << R"cpp())cpp";
}
return introspectionType.str();
}
std::vector<std::string> Generator::outputSeparateFiles() const noexcept
{
const std::filesystem::path headerDir(_headerDir);
const std::filesystem::path sourceDir(_sourceDir);
std::vector<std::string> files;
std::string_view queryType;
for (const auto& operation : _loader.getOperationTypes())
{
if (operation.operation == service::strQuery)
{
queryType = operation.type;
break;
}
}
std::ostringstream ossNamespace;
ossNamespace << R"cpp(graphql::)cpp" << _loader.getSchemaNamespace();
const auto schemaNamespace = ossNamespace.str();
std::ostringstream ossInterfaceNamespace;
ossInterfaceNamespace << schemaNamespace << R"cpp(::object)cpp";
const auto objectNamespace = ossInterfaceNamespace.str();
for (const auto& interfaceType : _loader.getInterfaceTypes())
{
const auto headerFilename = std::string(interfaceType.cppType) + "Object.h";
auto headerPath = (headerDir / headerFilename).string();
{
std::ofstream headerFile(headerPath, std::ios_base::trunc);
IncludeGuardScope includeGuard { headerFile, headerFilename };
headerFile << R"cpp(#include ")cpp"
<< std::filesystem::path(_headerPath).filename().string() << R"cpp("
)cpp";
NamespaceScope headerNamespace { headerFile, objectNamespace };
// Output the full declaration
headerFile << std::endl;
outputInterfaceDeclaration(headerFile, interfaceType.cppType);
headerFile << std::endl;
if (_options.verbose)
{
files.push_back(std::move(headerPath));
}
}
const auto sourceFilename = std::string(interfaceType.cppType) + "Object.cpp";
auto sourcePath = (sourceDir / sourceFilename).string();
{
std::ofstream sourceFile(sourcePath, std::ios_base::trunc);
sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// WARNING! Do not edit this file manually, your changes will be overwritten.
#include ")cpp" << headerFilename
<< R"cpp("
#include "graphqlservice/internal/Schema.h"
#include "graphqlservice/introspection/IntrospectionSchema.h"
using namespace std::literals;
)cpp";
NamespaceScope sourceSchemaNamespace { sourceFile, schemaNamespace };
NamespaceScope sourceInterfaceNamespace { sourceFile, "object" };
sourceFile << std::endl;
outputInterfaceImplementation(sourceFile, interfaceType.cppType);
sourceFile << std::endl;
sourceInterfaceNamespace.exit();
sourceFile << std::endl;
sourceFile << R"cpp(void Add)cpp" << interfaceType.cppType
<< R"cpp(Details(const std::shared_ptr<schema::InterfaceType>& type)cpp"
<< interfaceType.cppType
<< R"cpp(, const std::shared_ptr<schema::Schema>& schema)
{
)cpp";
outputInterfaceIntrospection(sourceFile, interfaceType);
sourceFile << R"cpp(}
)cpp";
}
files.push_back(std::move(sourcePath));
}
for (const auto& unionType : _loader.getUnionTypes())
{
const auto headerFilename = std::string(unionType.cppType) + "Object.h";
auto headerPath = (headerDir / headerFilename).string();
{
std::ofstream headerFile(headerPath, std::ios_base::trunc);
IncludeGuardScope includeGuard { headerFile, headerFilename };
headerFile << R"cpp(#include ")cpp"
<< std::filesystem::path(_headerPath).filename().string() << R"cpp("
)cpp";
NamespaceScope headerNamespace { headerFile, objectNamespace };
// Output the full declaration
headerFile << std::endl;
outputInterfaceDeclaration(headerFile, unionType.cppType);
headerFile << std::endl;
}
if (_options.verbose)
{
files.push_back(std::move(headerPath));
}
const auto sourceFilename = std::string(unionType.cppType) + "Object.cpp";
auto sourcePath = (sourceDir / sourceFilename).string();
{
std::ofstream sourceFile(sourcePath, std::ios_base::trunc);
sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// WARNING! Do not edit this file manually, your changes will be overwritten.
#include ")cpp" << headerFilename
<< R"cpp("
#include "graphqlservice/internal/Schema.h"
#include "graphqlservice/introspection/IntrospectionSchema.h"
using namespace std::literals;
)cpp";
NamespaceScope sourceSchemaNamespace { sourceFile, schemaNamespace };
NamespaceScope sourceUnionNamespace { sourceFile, "object" };
sourceFile << std::endl;
outputInterfaceImplementation(sourceFile, unionType.cppType);
sourceFile << std::endl;
sourceUnionNamespace.exit();
sourceFile << std::endl;
sourceFile << R"cpp(void Add)cpp" << unionType.cppType
<< R"cpp(Details(const std::shared_ptr<schema::UnionType>& type)cpp"
<< unionType.cppType
<< R"cpp(, const std::shared_ptr<schema::Schema>& schema)
{
)cpp";
outputUnionIntrospection(sourceFile, unionType);
sourceFile << R"cpp(}
)cpp";
}
files.push_back(std::move(sourcePath));
}
for (const auto& objectType : _loader.getObjectTypes())
{
const bool isQueryType = objectType.type == queryType;
const auto headerFilename = std::string(objectType.cppType) + "Object.h";
auto headerPath = (headerDir / headerFilename).string();
{
std::ofstream headerFile(headerPath, std::ios_base::trunc);
IncludeGuardScope includeGuard { headerFile, headerFilename };
headerFile << R"cpp(#include ")cpp"
<< std::filesystem::path(_headerPath).filename().string() << R"cpp("
)cpp";
NamespaceScope headerNamespace { headerFile, objectNamespace };
if (!_loader.isIntrospection())
{
if (!objectType.interfaces.empty() || !objectType.unions.empty())
{
NamespaceScope implementsNamespace { headerFile, R"cpp(implements)cpp" };
headerFile << std::endl;
outputObjectImplements(headerFile, objectType);
implementsNamespace.exit();
headerFile << std::endl;
}
// Output the stub concepts
std::ostringstream ossConceptNamespace;
ossConceptNamespace << R"cpp(methods::)cpp" << objectType.cppType << R"cpp(Has)cpp";
const auto conceptNamespace = ossConceptNamespace.str();
NamespaceScope stubNamespace { headerFile, conceptNamespace };
outputObjectStubs(headerFile, objectType);
}
// Output the full declaration
headerFile << std::endl;
outputObjectDeclaration(headerFile, objectType, isQueryType);
headerFile << std::endl;
}
if (_options.verbose)
{
files.push_back(std::move(headerPath));
}
const auto sourceFilename = std::string(objectType.cppType) + "Object.cpp";
auto sourcePath = (sourceDir / sourceFilename).string();
{
std::ofstream sourceFile(sourcePath, std::ios_base::trunc);
sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// WARNING! Do not edit this file manually, your changes will be overwritten.
#include ")cpp" << headerFilename
<< R"cpp("
)cpp";
std::unordered_set<std::string_view> includedObjects;
for (const auto& field : objectType.fields)
{
switch (field.fieldType)
{
case OutputFieldType::Interface:
case OutputFieldType::Union:
case OutputFieldType::Object:
if (includedObjects.insert(field.type).second)
{
sourceFile << R"cpp(#include ")cpp"
<< _loader.getSafeCppName(field.type) << R"cpp(Object.h"
)cpp";
}
break;
default:
break;
}
}
if (_loader.isIntrospection() || (isQueryType && !_options.noIntrospection))
{
sourceFile << R"cpp(
#include "graphqlservice/internal/Introspection.h"
)cpp";
}
else
{
sourceFile << R"cpp(
#include "graphqlservice/internal/Schema.h"
#include "graphqlservice/introspection/IntrospectionSchema.h"
)cpp";
}
if (isQueryType && !_options.noIntrospection)
{
sourceFile << R"cpp(
#include "graphqlservice/introspection/SchemaObject.h"
#include "graphqlservice/introspection/TypeObject.h"
)cpp";
}
sourceFile << R"cpp(
#include <algorithm>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
using namespace std::literals;
)cpp";
NamespaceScope sourceSchemaNamespace { sourceFile, schemaNamespace };
NamespaceScope sourceObjectNamespace { sourceFile, "object" };
sourceFile << std::endl;
outputObjectImplementation(sourceFile, objectType, isQueryType);
sourceFile << std::endl;
sourceObjectNamespace.exit();
sourceFile << std::endl;
sourceFile << R"cpp(void Add)cpp" << objectType.cppType
<< R"cpp(Details(const std::shared_ptr<schema::ObjectType>& type)cpp"
<< objectType.cppType
<< R"cpp(, const std::shared_ptr<schema::Schema>& schema)
{
)cpp";
outputObjectIntrospection(sourceFile, objectType);
sourceFile << R"cpp(}
)cpp";
}
files.push_back(std::move(sourcePath));
}
return files;
}
} // namespace graphql::generator::schema