fn validate_transform_linked_field_with_match_directive()

in compiler/crates/relay-transforms/src/match_/match_transform.rs [522:702]


    fn validate_transform_linked_field_with_match_directive(
        &mut self,
        field: &LinkedField,
        match_directive: &Directive,
    ) -> Result<Transformed<Selection>, Diagnostic> {
        // Validate and keep track of the module key
        let field_definition = self.program.schema.field(field.definition.item);
        let key_arg = match_directive.arguments.named(MATCH_CONSTANTS.key_arg);
        if let Some(arg) = key_arg {
            if let Value::Constant(ConstantValue::String(str)) = arg.value.item {
                if str.lookup().starts_with(self.document_name.lookup()) {
                    self.match_directive_key_argument = Some(str);
                }
            }
            if self.match_directive_key_argument.is_none() {
                return Err(Diagnostic::error(
                    ValidationMessage::InvalidMatchKeyArgument {
                        document_name: self.document_name,
                    },
                    arg.value.location,
                ));
            }
        }

        let previous_parent_type = self.parent_type;
        self.parent_type = field_definition.type_.inner();
        self.path.push(Path {
            location: field.definition.location,
            item: field.alias_or_name(&self.program.schema),
        });
        let next_selections = self.transform_selections(&field.selections);
        self.path.pop();
        self.parent_type = previous_parent_type;

        // The linked field definition should have: 'supported: [String]'
        let supported_arg_definition = field_definition
            .arguments
            .named(MATCH_CONSTANTS.supported_arg);
        match supported_arg_definition {
            None => {
                if key_arg.is_none() {
                    return Err(Diagnostic::error(
                        ValidationMessage::InvalidMatchWithNoSupportedArgument,
                        match_directive.name.location,
                    ));
                }
                return Ok(if let TransformedValue::Keep = next_selections {
                    Transformed::Keep
                } else {
                    Transformed::Replace(Selection::LinkedField(Arc::new(LinkedField {
                        alias: field.alias,
                        definition: field.definition,
                        arguments: field.arguments.clone(),
                        directives: field.directives.clone(),
                        selections: next_selections.replace_or_else(|| field.selections.clone()),
                    })))
                });
            }
            Some(supported_arg_definition) => {
                let is_supported_string = {
                    if let TypeReference::List(of) = supported_arg_definition.type_.nullable_type()
                    {
                        if let TypeReference::Named(of) = of.nullable_type() {
                            self.program.schema.is_string(*of)
                        } else {
                            false
                        }
                    } else {
                        false
                    }
                };
                if !is_supported_string {
                    return Err(Diagnostic::error(
                        ValidationMessage::InvalidMatchNotOnNonNullListString {
                            field_name: field_definition.name.item,
                        },
                        field.definition.location,
                    ));
                }
            }
        }

        // The linked field should be an abstract type
        if !field_definition.type_.inner().is_abstract_type() {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidMatchNotOnUnionOrInterface {
                    field_name: field_definition.name.item,
                },
                field.definition.location,
            ));
        }

        // The supported arg shouldn't be defined by the user
        let supported_arg = field.arguments.named(MATCH_CONSTANTS.supported_arg);
        if let Some(supported_arg) = supported_arg {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidMatchNoUserSuppliedSupportedArg {
                    supported_arg: MATCH_CONSTANTS.supported_arg,
                },
                supported_arg.name.location,
            ));
        }

        // Track fragment spread types that has @module
        // Validate that there are only `__typename`, and `...spread @module` selections
        let mut seen_types = IndexSet::with_hasher(FnvBuildHasher::default());
        for selection in &field.selections {
            match selection {
                Selection::FragmentSpread(field) => {
                    let has_directive_with_module = field.directives.iter().any(|directive| {
                        directive.name.item == MATCH_CONSTANTS.module_directive_name
                    });
                    if has_directive_with_module {
                        let fragment = self.program.fragment(field.fragment.item).unwrap();
                        seen_types.insert(fragment.type_condition);
                    } else {
                        self.push_fragment_spread_with_module_selection_err(
                            field.fragment.location,
                            match_directive.name.location,
                        );
                    }
                }
                Selection::ScalarField(field) => {
                    if field.definition.item != self.program.schema.typename_field() {
                        self.push_fragment_spread_with_module_selection_err(
                            field.definition.location,
                            match_directive.name.location,
                        );
                    }
                }
                Selection::LinkedField(field) => self
                    .push_fragment_spread_with_module_selection_err(
                        field.definition.location,
                        match_directive.name.location,
                    ),
                // TODO: no location on InlineFragment and Condition yet
                _ => self.push_fragment_spread_with_module_selection_err(
                    field.definition.location,
                    match_directive.name.location,
                ),
            }
        }
        if seen_types.is_empty() {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidMatchNoModuleSelection,
                match_directive.name.location,
            ));
        }

        let mut next_arguments = field.arguments.clone();
        next_arguments.push(Argument {
            name: WithLocation::new(field.definition.location, MATCH_CONSTANTS.supported_arg),
            value: WithLocation::new(
                field.definition.location,
                Value::Constant(ConstantValue::List(
                    seen_types
                        .into_iter()
                        .map(|type_| {
                            ConstantValue::String(self.program.schema.get_type_name(type_))
                        })
                        .collect(),
                )),
            ),
        });
        let mut next_directives = Vec::with_capacity(field.directives.len() - 1);
        for directive in &field.directives {
            if directive.name.item != MATCH_CONSTANTS.match_directive_name {
                next_directives.push(directive.clone());
            }
        }

        Ok(Transformed::Replace(Selection::LinkedField(Arc::new(
            LinkedField {
                alias: field.alias,
                definition: field.definition,
                arguments: next_arguments,
                directives: next_directives,
                selections: next_selections.replace_or_else(|| field.selections.clone()),
            },
        ))))
    }