bool Generator::outputSource()

in src/ClientGenerator.cpp [424:674]


bool Generator::outputSource() const noexcept
{
	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" << _schemaLoader.getFilenamePrefix()
			   << R"cpp(Client.h"

#include <algorithm>
#include <array>
#include <sstream>
#include <stdexcept>
#include <string_view>

using namespace std::literals;

)cpp";

	NamespaceScope clientNamespaceScope { sourceFile, getClientNamespace() };
	PendingBlankLine pendingSeparator { sourceFile };

	sourceFile << R"cpp(
using namespace )cpp"
			   << getRequestNamespace() << R"cpp(;
)cpp";

	const auto& variables = _requestLoader.getVariables();

	for (const auto& enumType : _requestLoader.getReferencedEnums())
	{
		pendingSeparator.reset();

		const auto& enumValues = enumType->enumValues();
		const auto cppType = _schemaLoader.getCppType(enumType->name());

		sourceFile << R"cpp(static const std::array<std::string_view, )cpp" << enumValues.size()
				   << R"cpp(> s_names)cpp" << cppType << R"cpp( = {
)cpp";

		for (const auto& enumValue : enumValues)
		{
			sourceFile << R"cpp(	")cpp" << SchemaLoader::getSafeCppName(enumValue->name())
					   << R"cpp("sv,
)cpp";
		}

		sourceFile << R"cpp(};
)cpp";

		pendingSeparator.add();
	}

	if (!variables.empty())
	{
		for (const auto& enumType : _requestLoader.getReferencedEnums())
		{
			pendingSeparator.reset();

			const auto cppType = _schemaLoader.getCppType(enumType->name());

			if (!variables.empty())
			{
				sourceFile << R"cpp(template <>
response::Value ModifiedVariable<)cpp"
						   << cppType << R"cpp(>::serialize()cpp" << cppType << R"cpp(&& value)
{
	response::Value result { response::Type::EnumValue };

	result.set<std::string>(std::string { s_names)cpp"
						   << cppType << R"cpp([static_cast<size_t>(value)] });

	return result;
}
)cpp";
			}

			pendingSeparator.add();
		}

		for (const auto& inputType : _requestLoader.getReferencedInputTypes())
		{
			pendingSeparator.reset();

			const auto cppType = _schemaLoader.getCppType(inputType->name());

			sourceFile << R"cpp(template <>
response::Value ModifiedVariable<Variables::)cpp"
					   << cppType << R"cpp(>::serialize(Variables::)cpp" << cppType
					   << R"cpp(&& inputValue)
{
	response::Value result { response::Type::Map };

)cpp";
			for (const auto& inputField : inputType->inputFields())
			{
				const auto [type, modifiers] =
					RequestLoader::unwrapSchemaType(inputField->type().lock());

				sourceFile << R"cpp(	result.emplace_back(R"js()cpp" << inputField->name()
						   << R"cpp()js"s, ModifiedVariable<)cpp"
						   << _schemaLoader.getCppType(type->name()) << R"cpp(>::serialize)cpp"
						   << getTypeModifierList(modifiers) << R"cpp((std::move(inputValue.)cpp"
						   << SchemaLoader::getSafeCppName(inputField->name()) << R"cpp()));
)cpp";
			}

			sourceFile << R"cpp(
	return result;
}
)cpp";

			pendingSeparator.add();
		}
	}

	for (const auto& enumType : _requestLoader.getReferencedEnums())
	{
		pendingSeparator.reset();

		const auto cppType = _schemaLoader.getCppType(enumType->name());

		sourceFile << R"cpp(template <>
)cpp" << cppType << R"cpp( ModifiedResponse<)cpp"
				   << cppType << R"cpp(>::parse(response::Value value)
{
	if (!value.maybe_enum())
	{
		throw std::logic_error { "not a valid )cpp"
				   << cppType << R"cpp( value" };
	}

	const auto itr = std::find(s_names)cpp"
				   << cppType << R"cpp(.cbegin(), s_names)cpp" << cppType
				   << R"cpp(.cend(), value.release<std::string>());

	if (itr == s_names)cpp"
				   << cppType << R"cpp(.cend())
	{
		throw std::logic_error { "not a valid )cpp"
				   << cppType << R"cpp( value" };
	}

	return static_cast<)cpp"
				   << cppType << R"cpp(>(itr - s_names)cpp" << cppType << R"cpp(.cbegin());
}
)cpp";

		pendingSeparator.add();
	}

	const auto& responseType = _requestLoader.getResponseType();
	const auto currentScope = R"cpp(Response)cpp"s;

	for (const auto& responseField : responseType.fields)
	{
		if (outputModifiedResponseImplementation(sourceFile, currentScope, responseField))
		{
			pendingSeparator.add();
		}
	}

	pendingSeparator.reset();

	NamespaceScope requestNamespaceScope { sourceFile, getRequestNamespace() };

	pendingSeparator.add();

	outputGetRequestImplementation(sourceFile);

	if (!variables.empty())
	{
		sourceFile << R"cpp(
response::Value serializeVariables(Variables&& variables)
{
	response::Value result { response::Type::Map };

)cpp";

		for (const auto& variable : variables)
		{
			sourceFile << R"cpp(	result.emplace_back(R"js()cpp" << variable.name
					   << R"cpp()js"s, ModifiedVariable<)cpp";

			const auto& builtinTypes = _schemaLoader.getBuiltinTypes();

			if (builtinTypes.find(variable.type->name()) == builtinTypes.cend()
				&& _schemaLoader.getSchemaType(variable.type->name()) != SchemaType::Scalar)
			{
				sourceFile << R"cpp(Variables::)cpp";
			}

			sourceFile << _schemaLoader.getCppType(variable.type->name()) << R"cpp(>::serialize)cpp"
					   << getTypeModifierList(variable.modifiers)
					   << R"cpp((std::move(variables.)cpp" << variable.cppName << R"cpp()));
)cpp";
		}

		sourceFile << R"cpp(
	return result;
}
)cpp";
	}

	sourceFile << R"cpp(
Response parseResponse(response::Value response)
{
	Response result;

	if (response.type() == response::Type::Map)
	{
		auto members = response.release<response::MapType>();

		for (auto& member : members)
		{
)cpp";

	std::unordered_set<std::string_view> fieldNames;

	for (const auto& responseField : responseType.fields)
	{
		if (fieldNames.emplace(responseField.name).second)
		{
			sourceFile << R"cpp(			if (member.first == R"js()cpp" << responseField.name
					   << R"cpp()js"sv)
			{
				result.)cpp"
					   << responseField.cppName << R"cpp( = ModifiedResponse<)cpp"
					   << getResponseFieldCppType(responseField, currentScope)
					   << R"cpp(>::parse)cpp" << getTypeModifierList(responseField.modifiers)
					   << R"cpp((std::move(member.second));
				continue;
			}
)cpp";
		}
	}

	sourceFile << R"cpp(		}
	}

	return result;
}
)cpp";

	pendingSeparator.reset();

	return true;
}