void RequestLoader::SelectionVisitor::visitField()

in src/RequestLoader.cpp [1009:1121]


void RequestLoader::SelectionVisitor::visitField(const peg::ast_node& field)
{
	std::string_view name;

	peg::on_first_child<peg::field_name>(field, [&name](const peg::ast_node& child) {
		name = child.string_view();
	});

	std::string_view alias;

	peg::on_first_child<peg::alias_name>(field, [&alias](const peg::ast_node& child) {
		alias = child.string_view();
	});

	if (alias.empty())
	{
		alias = name;
	}

	if (!_names.emplace(alias).second)
	{
		// Skip fields which map to the same response name as a field we've already visited.
		// Validation should handle merging multiple references to the same field or to
		// compatible fields.
		return;
	}

	ResponseField responseField;

	responseField.name = alias;
	responseField.cppName = SchemaLoader::getSafeCppName(alias);
	responseField.position = field.begin();

	// Special case to handle __typename on any ResponseType
	if (name == R"gql(__typename)gql"sv)
	{
		responseField.type = _schema->LookupType("String"sv);
		_fields.push_back(std::move(responseField));
		return;
	}

	if (_schema->supportsIntrospection() && _type == _schema->queryType())
	{
		if (name == R"gql(__schema)gql"sv)
		{
			responseField.type = _schema->LookupType("__Schema"sv);
		}
		else if (name == R"gql(__type)gql"sv)
		{
			responseField.type = _schema->LookupType("__Type"sv);
			responseField.modifiers = { service::TypeModifier::Nullable };
		}
	}

	if (!responseField.type)
	{
		const auto& typeFields = _type->fields();
		const auto itr = std::find_if(typeFields.begin(),
			typeFields.end(),
			[name](const std::shared_ptr<const schema::Field>& typeField) noexcept {
				return typeField->name() == name;
			});

		if (itr == typeFields.end())
		{
			// Skip fields that are not found on the current type.
			return;
		}

		responseField.type = (*itr)->type().lock();
	}

	std::tie(responseField.type, responseField.modifiers) =
		unwrapSchemaType(std::move(responseField.type));

	const peg::ast_node* selection = nullptr;

	peg::on_first_child<peg::selection_set>(field, [&selection](const peg::ast_node& child) {
		selection = &child;
	});

	if (selection)
	{
		switch (responseField.type->kind())
		{
			case introspection::TypeKind::OBJECT:
			case introspection::TypeKind::INTERFACE:
			case introspection::TypeKind::UNION:
			{
				SelectionVisitor selectionVisitor { _schemaLoader,
					_fragments,
					_schema,
					responseField.type };

				selectionVisitor.visit(*selection);

				auto selectionFields = selectionVisitor.getFields();

				if (!selectionFields.empty())
				{
					responseField.children = std::move(selectionFields);
				}

				break;
			}

			default:
				break;
		}
	}

	_fields.push_back(std::move(responseField));
}