From 3c31a60971a971af1ec1c22617bfde59af1af080 Mon Sep 17 00:00:00 2001
From: PatrykWalach <35966385+PatrykWalach@users.noreply.github.com>
Date: Mon, 24 Feb 2025 22:33:02 +0100
Subject: [PATCH] generate client pointer param types

---
 .../src/generate_artifacts.rs                 | 171 ++++++++----------
 crates/generate_artifacts/src/reader_ast.rs   |   8 +-
 crates/isograph_schema/src/isograph_schema.rs |  89 ++++++++-
 3 files changed, 169 insertions(+), 99 deletions(-)

diff --git a/crates/generate_artifacts/src/generate_artifacts.rs b/crates/generate_artifacts/src/generate_artifacts.rs
index 62a3a109..29b2a094 100644
--- a/crates/generate_artifacts/src/generate_artifacts.rs
+++ b/crates/generate_artifacts/src/generate_artifacts.rs
@@ -1,6 +1,6 @@
 use common_lang_types::{
     derive_display, ArtifactFileName, ArtifactFilePrefix, ArtifactPathAndContent, DescriptionValue,
-    Location, ObjectTypeAndFieldName, Span, WithLocation, WithSpan,
+    FieldNameOrAlias, Location, ObjectTypeAndFieldName, Span, WithLocation, WithSpan,
 };
 use graphql_lang_types::{
     GraphQLNamedTypeAnnotation, GraphQLNonNullTypeAnnotation, GraphQLTypeAnnotation,
@@ -11,8 +11,8 @@ use core::panic;
 use isograph_config::CompilerConfig;
 use isograph_lang_types::{
     ArgumentKeyAndValue, ClientFieldId, NonConstantValue, ScalarFieldSelection,
-    SelectableServerFieldId, SelectionType, ServerFieldSelection, TypeAnnotation, UnionVariant,
-    VariableDefinition,
+    SelectableServerFieldId, SelectionType, ServerFieldSelection, ServerObjectId, TypeAnnotation,
+    UnionVariant, VariableDefinition,
 };
 use isograph_schema::{
     get_provided_arguments, selection_map_wrapped, ClientFieldVariant, ClientType,
@@ -627,15 +627,17 @@ fn write_param_type_from_selection<TOutputFormat: OutputFormat>(
             }
         }
         ServerFieldSelection::LinkedField(linked_field) => {
-            let parent_field = parent_type
-                .encountered_fields
-                .get(&linked_field.name.item.into())
-                .expect("parent_field should exist 2")
-                .as_server_field()
-                .expect("Parent field should exist and be server field");
-            let field = schema.server_field(*parent_field);
+            let field = match linked_field.associated_data.field_id {
+                FieldType::ServerField(server_field_id) => {
+                    FieldType::ServerField(schema.server_field(server_field_id))
+                }
+                FieldType::ClientField(client_pointer_id) => {
+                    FieldType::ClientField(schema.client_pointer(client_pointer_id))
+                }
+            };
+
             write_optional_description(
-                field.description,
+                field.description(),
                 query_type_declaration,
                 indentation_level,
             );
@@ -643,28 +645,22 @@ fn write_param_type_from_selection<TOutputFormat: OutputFormat>(
             let name_or_alias = linked_field.name_or_alias().item;
 
             let type_annotation =
-                match &field.associated_data {
-                    SelectionType::Scalar(_) => panic!(
-                        "output_type_id should be an object. \
-                        This is indicative of a bug in Isograph.",
-                    ),
-                    SelectionType::Object(associated_data) => associated_data
-                        .type_name
-                        .clone()
-                        .map(&mut |output_type_id| {
-                            let object_id = output_type_id;
-                            let object = schema.server_field_data.object(object_id);
-                            generate_client_field_parameter_type(
-                                schema,
-                                &linked_field.selection_set,
-                                object,
-                                nested_client_field_imports,
-                                loadable_fields,
-                                indentation_level,
-                                link_fields,
-                            )
-                        }),
-                };
+                field
+                    .output_type_annotation()
+                    .clone()
+                    .map(&mut |output_type_id| {
+                        let object_id = output_type_id;
+                        let object = schema.server_field_data.object(object_id);
+                        generate_client_field_parameter_type(
+                            schema,
+                            &linked_field.selection_set,
+                            object,
+                            nested_client_field_imports,
+                            loadable_fields,
+                            indentation_level,
+                            link_fields,
+                        )
+                    });
 
             query_type_declaration.push_str(&format!(
                 "readonly {}: {},\n",
@@ -838,71 +834,57 @@ fn write_updatable_data_type_from_selection<TOutputFormat: OutputFormat>(
             }
         }
         ServerFieldSelection::LinkedField(linked_field) => {
-            let parent_field = parent_type
-                .encountered_fields
-                .get(&linked_field.name.item.into())
-                .expect("parent_field should exist 2")
-                .as_server_field()
-                .expect("Parent field should exist and be server field");
-            let field = schema.server_field(*parent_field);
+            let field = schema.linked_type(linked_field.associated_data.field_id);
 
             write_optional_description(
-                field.description,
+                field.description(),
                 query_type_declaration,
                 indentation_level,
             );
             query_type_declaration.push_str(&"  ".repeat(indentation_level as usize).to_string());
             let name_or_alias = linked_field.name_or_alias().item;
 
-            match &field.associated_data {
-                SelectionType::Scalar(_) => panic!(
-                    "output_type_id should be an object. \
-                        This is indicative of a bug in Isograph.",
-                ),
-                SelectionType::Object(associated_data) => {
-                    let type_annotation =
-                        associated_data
-                            .type_name
-                            .clone()
-                            .map(&mut |output_type_id| {
-                                let object_id = output_type_id;
-                                let object = schema.server_field_data.object(object_id);
-                                generate_client_field_updatable_data_type(
-                                    schema,
-                                    &linked_field.selection_set,
-                                    object,
-                                    nested_client_field_imports,
-                                    loadable_fields,
-                                    indentation_level,
-                                    link_fields,
-                                    updatable_fields,
-                                )
-                            });
-
-                    match linked_field.associated_data.selection_variant {
-                        ValidatedIsographSelectionVariant::Loadable(_) => {
-                            panic!("@loadable server fields are not supported")
-                        }
-                        ValidatedIsographSelectionVariant::Updatable => {
-                            *updatable_fields = true;
-                            write_getter_and_setter(
-                                query_type_declaration,
-                                indentation_level,
-                                name_or_alias,
-                                associated_data,
-                                &type_annotation,
-                            );
-                        }
-                        ValidatedIsographSelectionVariant::Regular => {
-                            query_type_declaration.push_str(&format!(
-                                "readonly {}: {},\n",
-                                name_or_alias,
-                                print_javascript_type_declaration(&type_annotation),
-                            ));
-                        }
-                    }
+            let type_annotation =
+                field
+                    .output_type_annotation()
+                    .clone()
+                    .map(&mut |output_type_id| {
+                        let object_id = output_type_id;
+                        let object = schema.server_field_data.object(object_id);
+                        generate_client_field_updatable_data_type(
+                            schema,
+                            &linked_field.selection_set,
+                            object,
+                            nested_client_field_imports,
+                            loadable_fields,
+                            indentation_level,
+                            link_fields,
+                            updatable_fields,
+                        )
+                    });
+
+            match linked_field.associated_data.selection_variant {
+                ValidatedIsographSelectionVariant::Loadable(_) => {
+                    panic!("@loadable server fields are not supported")
                 }
-            };
+                ValidatedIsographSelectionVariant::Updatable => {
+                    *updatable_fields = true;
+                    write_getter_and_setter(
+                        query_type_declaration,
+                        indentation_level,
+                        name_or_alias,
+                        field.output_type_annotation(),
+                        &type_annotation,
+                    );
+                }
+                ValidatedIsographSelectionVariant::Regular => {
+                    query_type_declaration.push_str(&format!(
+                        "readonly {}: {},\n",
+                        name_or_alias,
+                        print_javascript_type_declaration(&type_annotation),
+                    ));
+                }
+            }
         }
     }
 }
@@ -910,10 +892,8 @@ fn write_updatable_data_type_from_selection<TOutputFormat: OutputFormat>(
 fn write_getter_and_setter(
     query_type_declaration: &mut String,
     indentation_level: u8,
-    name_or_alias: common_lang_types::FieldNameOrAlias,
-    associated_data: &isograph_schema::ServerFieldTypeAssociatedData<
-        TypeAnnotation<isograph_lang_types::ServerObjectId>,
-    >,
+    name_or_alias: FieldNameOrAlias,
+    output_type_annotation: TypeAnnotation<ServerObjectId>,
     type_annotation: &TypeAnnotation<ClientFieldUpdatableDataType>,
 ) {
     query_type_declaration.push_str(&format!(
@@ -921,10 +901,7 @@ fn write_getter_and_setter(
         name_or_alias,
         print_javascript_type_declaration(type_annotation),
     ));
-    let setter_type_annotation = associated_data
-        .type_name
-        .clone()
-        .map(&mut |_| "{ link: Link }");
+    let setter_type_annotation = output_type_annotation.map(&mut |_| "{ link: Link }");
     query_type_declaration.push_str(&"  ".repeat(indentation_level as usize).to_string());
     query_type_declaration.push_str(&format!(
         "set {}(value: {}),\n",
diff --git a/crates/generate_artifacts/src/reader_ast.rs b/crates/generate_artifacts/src/reader_ast.rs
index b66a93cf..02cc7f6c 100644
--- a/crates/generate_artifacts/src/reader_ast.rs
+++ b/crates/generate_artifacts/src/reader_ast.rs
@@ -122,7 +122,13 @@ fn linked_field_ast_node<TOutputFormat: OutputFormat>(
     let indent_2 = "  ".repeat((indentation_level + 1) as usize);
 
     let condition = match linked_field.associated_data.field_id {
-        FieldType::ClientField(_) => todo!(),
+        FieldType::ClientField(client_pointer_id) => {
+            let client_pointer = schema.client_pointer(client_pointer_id);
+            format!(
+                "{}__resolver_reader",
+                client_pointer.type_and_field.underscore_separated()
+            )
+        }
         FieldType::ServerField(server_field_id) => {
             match &schema.server_field(server_field_id).associated_data {
                 SelectionType::Scalar(_) => panic!("Expected object"),
diff --git a/crates/isograph_schema/src/isograph_schema.rs b/crates/isograph_schema/src/isograph_schema.rs
index 6814435e..d7dfe578 100644
--- a/crates/isograph_schema/src/isograph_schema.rs
+++ b/crates/isograph_schema/src/isograph_schema.rs
@@ -24,7 +24,7 @@ use lazy_static::lazy_static;
 use crate::{
     refetch_strategy::RefetchStrategy, schema_validation_state::SchemaValidationState,
     ClientFieldVariant, NormalizationKey, OutputFormat, ServerFieldTypeAssociatedData,
-    ValidatedClientField, ValidatedClientPointer,
+    ValidatedClientField, ValidatedClientPointer, ValidatedSchemaServerField,
 };
 
 lazy_static! {
@@ -134,12 +134,79 @@ pub enum FieldType<TServer, TClient> {
     ClientField(TClient),
 }
 
+pub type LinkedType<
+    'a,
+    ServerFieldTypeAssociatedData,
+    ClientTypeSelectionScalarFieldAssociatedData,
+    ClientTypeSelectionLinkedFieldAssociatedData,
+    VariableDefinitionInnerType,
+    TOutputFormat,
+> = FieldType<
+    &'a SchemaServerField<
+        ServerFieldTypeAssociatedData,
+        VariableDefinitionInnerType,
+        TOutputFormat,
+    >,
+    &'a ClientPointer<
+        ClientTypeSelectionScalarFieldAssociatedData,
+        ClientTypeSelectionLinkedFieldAssociatedData,
+        VariableDefinitionInnerType,
+        TOutputFormat,
+    >,
+>;
+
 #[derive(Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq, Hash)]
 pub enum ClientType<TField, TPointer> {
     ClientField(TField),
     ClientPointer(TPointer),
 }
 
+impl<
+        ServerFieldTypeAssociatedData,
+        TClientTypeSelectionScalarFieldAssociatedData,
+        TClientTypeSelectionLinkedFieldAssociatedData,
+        TClientTypeVariableDefinitionAssociatedData: Ord + Debug,
+        TOutputFormat: OutputFormat,
+    >
+    FieldType<
+        &SchemaServerField<
+            ServerFieldTypeAssociatedData,
+            TClientTypeVariableDefinitionAssociatedData,
+            TOutputFormat,
+        >,
+        &ClientPointer<
+            TClientTypeSelectionScalarFieldAssociatedData,
+            TClientTypeSelectionLinkedFieldAssociatedData,
+            TClientTypeVariableDefinitionAssociatedData,
+            TOutputFormat,
+        >,
+    >
+{
+    pub fn description(&self) -> Option<DescriptionValue> {
+        match self {
+            FieldType::ServerField(server_field) => server_field.description,
+            FieldType::ClientField(client_field) => client_field.description,
+        }
+    }
+}
+
+impl<TOutputFormat: OutputFormat>
+    FieldType<&ValidatedSchemaServerField<TOutputFormat>, &ValidatedClientPointer<TOutputFormat>>
+{
+    pub fn output_type_annotation(&self) -> TypeAnnotation<ServerObjectId> {
+        match self {
+            FieldType::ClientField(client_pointer) => client_pointer.to.clone(),
+            FieldType::ServerField(server_field) => match &server_field.associated_data {
+                SelectionType::Scalar(_) => panic!(
+                    "output_type_id should be an object. \
+                                       This is indicative of a bug in Isograph.",
+                ),
+                SelectionType::Object(associated_data) => associated_data.type_name.clone(),
+            },
+        }
+    }
+}
+
 pub type ClientTypeId = ClientType<ClientFieldId, ClientPointerId>;
 
 pub type ValidatedClientType<'a, TOutputFormat> =
@@ -299,6 +366,26 @@ impl<TSchemaValidationState: SchemaValidationState, TOutputFormat: OutputFormat>
         }
     }
 
+    pub fn linked_type(
+        &self,
+        field_id: FieldType<ServerFieldId, ClientPointerId>,
+    ) -> LinkedType<
+        TSchemaValidationState::ServerFieldTypeAssociatedData,
+        TSchemaValidationState::ClientTypeSelectionScalarFieldAssociatedData,
+        TSchemaValidationState::ClientTypeSelectionLinkedFieldAssociatedData,
+        TSchemaValidationState::VariableDefinitionInnerType,
+        TOutputFormat,
+    > {
+        match field_id {
+            FieldType::ServerField(server_field_id) => {
+                FieldType::ServerField(self.server_field(server_field_id))
+            }
+            FieldType::ClientField(client_pointer_id) => {
+                FieldType::ClientField(self.client_pointer(client_pointer_id))
+            }
+        }
+    }
+
     /// Get a reference to a given client pointer by its id.
     pub fn client_pointer(
         &self,