/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::sync::Arc;

use common::WithLocation;

use crate::ir::*;
use crate::program::Program;

pub trait Transformer {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;
    const RETAIN_EMPTY_SELECTION_SETS: bool = false;

    fn transform_program(&mut self, program: &Program) -> TransformedValue<Program> {
        self.default_transform_program(program)
    }

    fn default_transform_program(&mut self, program: &Program) -> TransformedValue<Program> {
        let mut next_program = Program::new(Arc::clone(&program.schema));
        let mut has_changes = false;
        for operation in program.operations() {
            match self.transform_operation(operation) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_operation(Arc::clone(operation)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_operation(Arc::new(replacement))
                }
            }
        }
        for fragment in program.fragments() {
            match self.transform_fragment(fragment) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_fragment(Arc::clone(fragment)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_fragment(Arc::new(replacement))
                }
            }
        }
        if has_changes {
            TransformedValue::Replace(next_program)
        } else {
            TransformedValue::Keep
        }
    }

    // Fragment Definition
    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.default_transform_fragment(fragment)
    }

    fn default_transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        let selections = self.transform_selections(&fragment.selections);
        let directives = self.transform_directives(&fragment.directives);
        let variable_definitions =
            self.transform_variable_definitions(&fragment.variable_definitions);

        // Special-case for empty selections
        if let TransformedValue::Replace(selections) = &selections {
            if !Self::RETAIN_EMPTY_SELECTION_SETS && selections.is_empty() {
                return Transformed::Delete;
            }
        }

        if selections.should_keep()
            && directives.should_keep()
            && variable_definitions.should_keep()
        {
            return Transformed::Keep;
        }

        Transformed::Replace(FragmentDefinition {
            variable_definitions: variable_definitions
                .replace_or_else(|| fragment.variable_definitions.clone()),
            directives: directives.replace_or_else(|| fragment.directives.clone()),
            selections: selections.replace_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })
    }

    // Operation Definition
    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.default_transform_operation(operation)
    }

    fn default_transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let selections = self.transform_selections(&operation.selections);
        let directives = self.transform_directives(&operation.directives);
        let variable_definitions =
            self.transform_variable_definitions(&operation.variable_definitions);

        // Special-case for empty selections
        if let TransformedValue::Replace(selections) = &selections {
            if !Self::RETAIN_EMPTY_SELECTION_SETS && selections.is_empty() {
                return Transformed::Delete;
            }
        }

        if variable_definitions.should_keep()
            && directives.should_keep()
            && selections.should_keep()
        {
            return Transformed::Keep;
        }

        Transformed::Replace(OperationDefinition {
            variable_definitions: variable_definitions
                .replace_or_else(|| operation.variable_definitions.clone()),
            directives: directives.replace_or_else(|| operation.directives.clone()),
            selections: selections.replace_or_else(|| operation.selections.clone()),
            ..operation.clone()
        })
    }

    fn transform_variable_definitions(
        &mut self,
        variable_definitions: &[VariableDefinition],
    ) -> TransformedValue<Vec<VariableDefinition>> {
        self.default_transform_variable_definitions(variable_definitions)
    }

    fn default_transform_variable_definitions(
        &mut self,
        variable_definitions: &[VariableDefinition],
    ) -> TransformedValue<Vec<VariableDefinition>> {
        transform_list(variable_definitions, |variable_definition| {
            self.default_transform_variable_definition(variable_definition)
        })
    }

    fn transform_variable_definition(
        &mut self,
        variable_definition: &VariableDefinition,
    ) -> TransformedValue<VariableDefinition> {
        self.default_transform_variable_definition(variable_definition)
    }

    fn default_transform_variable_definition(
        &mut self,
        variable_definition: &VariableDefinition,
    ) -> TransformedValue<VariableDefinition> {
        let directives = self.transform_directives(&variable_definition.directives);
        if directives.should_keep() {
            return TransformedValue::Keep;
        }
        TransformedValue::Replace(VariableDefinition {
            directives: directives.replace_or_else(|| variable_definition.directives.clone()),
            ..variable_definition.clone()
        })
    }

    // Selection
    fn transform_selections(
        &mut self,
        selections: &[Selection],
    ) -> TransformedValue<Vec<Selection>> {
        transform_list(selections, |selection| self.transform_selection(selection))
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        self.default_transform_selection(selection)
    }

    fn default_transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::FragmentSpread(selection) => self.transform_fragment_spread(selection),
            Selection::InlineFragment(selection) => self.transform_inline_fragment(selection),
            Selection::LinkedField(selection) => self.transform_linked_field(selection),
            Selection::ScalarField(selection) => self.transform_scalar_field(selection),
            Selection::Condition(selection) => self.transform_condition(selection),
        }
    }

    // Selection Kinds
    fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Selection> {
        self.default_transform_scalar_field(field)
    }

    fn default_transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Selection> {
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if arguments.should_keep() && directives.should_keep() {
            return Transformed::Keep;
        }
        Transformed::Replace(Selection::ScalarField(Arc::new(ScalarField {
            arguments: arguments.replace_or_else(|| field.arguments.clone()),
            directives: directives.replace_or_else(|| field.directives.clone()),
            ..field.clone()
        })))
    }

    fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Selection> {
        self.default_transform_linked_field(field)
    }

    fn default_transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Selection> {
        // Special-case for empty selections
        let selections = self.transform_selections(&field.selections);
        if let TransformedValue::Replace(selections) = &selections {
            if !Self::RETAIN_EMPTY_SELECTION_SETS && selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if selections.should_keep() && arguments.should_keep() && directives.should_keep() {
            return Transformed::Keep;
        }
        Transformed::Replace(Selection::LinkedField(Arc::new(LinkedField {
            arguments: arguments.replace_or_else(|| field.arguments.clone()),
            directives: directives.replace_or_else(|| field.directives.clone()),
            selections: selections.replace_or_else(|| field.selections.clone()),
            ..field.clone()
        })))
    }

    fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed<Selection> {
        self.default_transform_inline_fragment(fragment)
    }

    fn default_transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Selection> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let TransformedValue::Replace(selections) = &selections {
            if !Self::RETAIN_EMPTY_SELECTION_SETS && selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.should_keep() && directives.should_keep() {
            return Transformed::Keep;
        }
        Transformed::Replace(Selection::InlineFragment(Arc::new(InlineFragment {
            directives: directives.replace_or_else(|| fragment.directives.clone()),
            selections: selections.replace_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })))
    }

    fn transform_fragment_spread(&mut self, spread: &FragmentSpread) -> Transformed<Selection> {
        self.default_transform_fragment_spread(spread)
    }
    fn default_transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Selection> {
        let arguments = self.transform_arguments(&spread.arguments);
        let directives = self.transform_directives(&spread.directives);
        if arguments.should_keep() && directives.should_keep() {
            return Transformed::Keep;
        }
        Transformed::Replace(Selection::FragmentSpread(Arc::new(FragmentSpread {
            arguments: arguments.replace_or_else(|| spread.arguments.clone()),
            directives: directives.replace_or_else(|| spread.directives.clone()),
            ..spread.clone()
        })))
    }

    // Conditions
    fn transform_condition(&mut self, condition: &Condition) -> Transformed<Selection> {
        self.default_transform_condition(condition)
    }

    fn default_transform_condition(&mut self, condition: &Condition) -> Transformed<Selection> {
        // Special-case for empty selections
        let selections = self.transform_selections(&condition.selections);
        if let TransformedValue::Replace(selections) = &selections {
            if !Self::RETAIN_EMPTY_SELECTION_SETS && selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let condition_value = self.transform_condition_value(&condition.value);
        if selections.should_keep() && condition_value.should_keep() {
            Transformed::Keep
        } else {
            Transformed::Replace(Selection::Condition(Arc::new(Condition {
                value: condition_value.replace_or_else(|| condition.value.clone()),
                selections: selections.replace_or_else(|| condition.selections.clone()),
                ..condition.clone()
            })))
        }
    }

    fn transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> TransformedValue<ConditionValue> {
        if Self::VISIT_ARGUMENTS {
            self.default_transform_condition_value(condition_value)
        } else {
            TransformedValue::Keep
        }
    }

    fn default_transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> TransformedValue<ConditionValue> {
        match condition_value {
            ConditionValue::Variable(variable) => self
                .transform_variable(variable)
                .map(ConditionValue::Variable),
            ConditionValue::Constant(_) => TransformedValue::Keep,
        }
    }

    // Directives
    fn transform_directives(
        &mut self,
        directives: &[Directive],
    ) -> TransformedValue<Vec<Directive>> {
        if Self::VISIT_DIRECTIVES {
            transform_list(directives, |directive| self.transform_directive(directive))
        } else {
            TransformedValue::Keep
        }
    }

    fn transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        self.default_transform_directive(directive)
    }

    fn default_transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        let arguments = self.transform_arguments(&directive.arguments);
        match arguments {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(replacement) => Transformed::Replace(Directive {
                arguments: replacement,
                ..directive.clone()
            }),
        }
    }

    // Arguments
    fn transform_arguments(&mut self, arguments: &[Argument]) -> TransformedValue<Vec<Argument>> {
        if Self::VISIT_ARGUMENTS {
            transform_list(arguments, |argument| self.transform_argument(argument))
        } else {
            TransformedValue::Keep
        }
    }

    fn transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        self.default_transform_argument(argument)
    }

    fn default_transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        match self.transform_value(&argument.value.item) {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(replacement) => Transformed::Replace(Argument {
                value: WithLocation::new(argument.value.location, replacement),
                ..argument.clone()
            }),
        }
    }

    // Values
    fn transform_value(&mut self, value: &Value) -> TransformedValue<Value> {
        self.default_transform_value(value)
    }

    fn default_transform_value(&mut self, value: &Value) -> TransformedValue<Value> {
        match value {
            Value::Variable(variable) => self.transform_variable(variable).map(Value::Variable),
            Value::Constant(_) => TransformedValue::Keep,
            Value::List(items) => {
                transform_list(items, |item| self.transform_value(item)).map(Value::List)
            }
            Value::Object(arguments) => self.transform_arguments(arguments).map(Value::Object),
        }
    }

    fn transform_variable(&mut self, _variable: &Variable) -> TransformedValue<Variable> {
        TransformedValue::Keep
    }
}

// Helpers
pub fn transform_list<T, F, R>(list: &[T], mut transform: F) -> TransformedValue<Vec<T>>
where
    T: Clone,
    F: FnMut(&T) -> R,
    R: Into<Transformed<T>>,
{
    let mut result = Vec::new();
    let mut has_changes = false;
    for (index, prev_item) in list.iter().enumerate() {
        let next_item: Transformed<_> = transform(prev_item).into();
        match next_item {
            Transformed::Keep => {
                if has_changes {
                    result.push(prev_item.clone());
                }
            }
            Transformed::Delete => {
                if !has_changes {
                    debug_assert!(result.capacity() == 0);
                    // assume most items won't be skipped and allocate space for all items
                    result.reserve(list.len());
                    result.extend(list.iter().take(index).cloned());
                }
                has_changes = true;
            }
            Transformed::Replace(next_item) => {
                if !has_changes {
                    debug_assert!(result.capacity() == 0);
                    // assume most items won't be skipped and allocate space for all items
                    result.reserve(list.len());
                    result.extend(list.iter().take(index).cloned());
                }
                result.push(next_item);
                has_changes = true;
            }
        }
    }
    if has_changes {
        // Note that result can be empty if the input was empty and all items were skipped
        TransformedValue::Replace(result)
    } else {
        TransformedValue::Keep
    }
}

/// Similar to `transform_list`, but replaces the return value of the item
/// callback with `TransformedMulti<T>` which allows more than one item to
/// be returned and grow the list. This helper is unused by the default
/// implementations and one has to override `transform_selections` or a
/// similar list transform function.
pub fn transform_list_multi<T, F>(list: &[T], mut transform: F) -> TransformedValue<Vec<T>>
where
    T: Clone,
    F: FnMut(&T) -> TransformedMulti<T>,
{
    let mut result = Vec::new();
    let mut has_changes = false;
    for (index, prev_item) in list.iter().enumerate() {
        let next_item = transform(prev_item);
        match next_item {
            TransformedMulti::Keep => {
                if has_changes {
                    result.push(prev_item.clone());
                }
            }
            TransformedMulti::Delete => {
                if !has_changes {
                    debug_assert!(result.capacity() == 0);
                    // assume most items won't be skipped and allocate space for all items
                    result.reserve(list.len());
                    result.extend(list.iter().take(index).cloned());
                }
                has_changes = true;
            }
            TransformedMulti::Replace(next_item) => {
                if !has_changes {
                    debug_assert!(result.capacity() == 0);
                    // assume most items won't be skipped and allocate space for all items
                    result.reserve(list.len());
                    result.extend(list.iter().take(index).cloned());
                }
                result.push(next_item);
                has_changes = true;
            }
            TransformedMulti::ReplaceMultiple(next_items) => {
                if !has_changes {
                    debug_assert!(result.capacity() == 0);
                    // assume most items won't be skipped and allocate space for all items
                    result.reserve(list.len() + next_items.len() - 1);
                    result.extend(list.iter().take(index).cloned());
                }
                result.extend(next_items);
                has_changes = true;
            }
        }
    }
    if has_changes {
        // Note that result can be empty if the input was empty and all items were skipped
        TransformedValue::Replace(result)
    } else {
        TransformedValue::Keep
    }
}

#[derive(Clone, Debug)]
pub enum Transformed<T> {
    Delete,
    Keep,
    Replace(T),
}

impl<T> Transformed<T> {
    pub fn map<F, U>(self, f: F) -> Transformed<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(f(replacement)),
        }
    }

    pub fn unwrap_or_else<F>(self, f: F) -> T
    where
        F: FnOnce() -> T,
    {
        match self {
            Transformed::Replace(replacement) => replacement,
            _ => f(),
        }
    }
}

/// An extension of Transformed that has the additional value `ReplaceMultiple`
/// which allows more then one item to be returned.
#[derive(Clone, Debug)]
pub enum TransformedMulti<T> {
    Delete,
    Keep,
    Replace(T),
    ReplaceMultiple(Vec<T>),
}

impl<T> From<Transformed<T>> for TransformedMulti<T> {
    fn from(other: Transformed<T>) -> TransformedMulti<T> {
        match other {
            Transformed::Delete => TransformedMulti::Delete,
            Transformed::Keep => TransformedMulti::Keep,
            Transformed::Replace(replacement) => TransformedMulti::Replace(replacement),
        }
    }
}

#[derive(Clone, Debug)]
pub enum TransformedValue<T> {
    Keep,
    Replace(T),
}

impl<T> TransformedValue<T> {
    pub fn should_keep(&self) -> bool {
        match self {
            TransformedValue::Keep => true,
            TransformedValue::Replace(_) => false,
        }
    }

    pub fn replace_or_else<F>(self, f: F) -> T
    where
        F: FnOnce() -> T,
    {
        match self {
            TransformedValue::Keep => f(),
            TransformedValue::Replace(next_value) => next_value,
        }
    }

    pub fn map<F, U>(self, f: F) -> TransformedValue<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            TransformedValue::Keep => TransformedValue::Keep,
            TransformedValue::Replace(replacement) => TransformedValue::Replace(f(replacement)),
        }
    }
}

impl<T> From<TransformedValue<T>> for Transformed<T> {
    fn from(other: TransformedValue<T>) -> Transformed<T> {
        match other {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(replacement) => Transformed::Replace(replacement),
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::DiagnosticsResult;
use errors::validate;
use errors::validate_map;

use crate::ir::*;
use crate::program::Program;

pub trait Validator {
    const NAME: &'static str;
    const VALIDATE_ARGUMENTS: bool;
    const VALIDATE_DIRECTIVES: bool;

    fn validate_program(&mut self, program: &Program) -> DiagnosticsResult<()> {
        self.default_validate_program(program)
    }

    fn default_validate_program(&mut self, program: &Program) -> DiagnosticsResult<()> {
        validate!(
            validate_map(program.operations(), |operation| {
                self.validate_operation(operation)
            }),
            validate_map(program.fragments(), |fragment| {
                self.validate_fragment(fragment)
            })
        )
    }

    // Fragment Definition
    fn validate_fragment(&mut self, fragment: &FragmentDefinition) -> DiagnosticsResult<()> {
        self.default_validate_fragment(fragment)
    }

    fn default_validate_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> DiagnosticsResult<()> {
        validate!(
            self.validate_selections(&fragment.selections),
            self.validate_directives(&fragment.directives)
        )
    }

    // Operation Definition
    fn validate_operation(&mut self, operation: &OperationDefinition) -> DiagnosticsResult<()> {
        self.default_validate_operation(operation)
    }

    fn default_validate_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> DiagnosticsResult<()> {
        validate!(
            self.validate_directives(&operation.directives),
            self.validate_selections(&operation.selections)
        )
    }

    // Selection
    fn validate_selections(&mut self, selections: &[Selection]) -> DiagnosticsResult<()> {
        self.validate_list(selections, Self::validate_selection)
    }

    fn validate_selection(&mut self, selection: &Selection) -> DiagnosticsResult<()> {
        self.default_validate_selection(selection)
    }

    fn default_validate_selection(&mut self, selection: &Selection) -> DiagnosticsResult<()> {
        match selection {
            Selection::FragmentSpread(selection) => self.validate_fragment_spread(selection),
            Selection::InlineFragment(selection) => self.validate_inline_fragment(selection),
            Selection::LinkedField(selection) => self.validate_linked_field(selection),
            Selection::ScalarField(selection) => self.validate_scalar_field(selection),
            Selection::Condition(selection) => self.validate_condition(selection),
        }
    }

    // Selection Kinds
    fn validate_scalar_field(&mut self, field: &ScalarField) -> DiagnosticsResult<()> {
        self.default_validate_scalar_field(field)
    }

    fn default_validate_scalar_field(&mut self, field: &ScalarField) -> DiagnosticsResult<()> {
        validate!(
            self.validate_arguments(&field.arguments),
            self.validate_directives(&field.directives)
        )
    }

    fn validate_linked_field(&mut self, field: &LinkedField) -> DiagnosticsResult<()> {
        self.default_validate_linked_field(field)
    }

    fn default_validate_linked_field(&mut self, field: &LinkedField) -> DiagnosticsResult<()> {
        validate!(
            self.validate_selections(&field.selections),
            self.validate_arguments(&field.arguments),
            self.validate_directives(&field.directives)
        )
    }

    fn validate_inline_fragment(&mut self, fragment: &InlineFragment) -> DiagnosticsResult<()> {
        self.default_validate_inline_fragment(fragment)
    }

    fn default_validate_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> DiagnosticsResult<()> {
        validate!(
            self.validate_selections(&fragment.selections),
            self.validate_directives(&fragment.directives)
        )
    }

    fn validate_fragment_spread(&mut self, spread: &FragmentSpread) -> DiagnosticsResult<()> {
        self.default_validate_fragment_spread(spread)
    }

    fn default_validate_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> DiagnosticsResult<()> {
        validate!(
            self.validate_arguments(&spread.arguments),
            self.validate_directives(&spread.directives)
        )
    }

    fn validate_condition(&mut self, condition: &Condition) -> DiagnosticsResult<()> {
        self.default_validate_condition(condition)
    }

    fn default_validate_condition(&mut self, condition: &Condition) -> DiagnosticsResult<()> {
        validate!(
            self.validate_condition_value(&condition.value),
            self.validate_selections(&condition.selections)
        )
    }

    fn validate_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> DiagnosticsResult<()> {
        self.default_validate_condition_value(condition_value)
    }

    fn default_validate_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> DiagnosticsResult<()> {
        if Self::VALIDATE_ARGUMENTS {
            match condition_value {
                ConditionValue::Variable(variable) => self.validate_variable(variable),
                ConditionValue::Constant(_) => Ok(()),
            }
        } else {
            Ok(())
        }
    }

    // Directives
    fn validate_directives(&mut self, directives: &[Directive]) -> DiagnosticsResult<()> {
        if Self::VALIDATE_DIRECTIVES {
            self.validate_list(directives, Self::validate_directive)
        } else {
            Ok(())
        }
    }

    fn validate_directive(&mut self, directive: &Directive) -> DiagnosticsResult<()> {
        self.default_validate_directive(directive)
    }

    fn default_validate_directive(&mut self, directive: &Directive) -> DiagnosticsResult<()> {
        self.validate_arguments(&directive.arguments)
    }

    // Arguments
    fn validate_arguments(&mut self, arguments: &[Argument]) -> DiagnosticsResult<()> {
        if Self::VALIDATE_ARGUMENTS {
            self.validate_list(arguments, Self::validate_argument)?;
        }
        Ok(())
    }

    fn validate_argument(&mut self, argument: &Argument) -> DiagnosticsResult<()> {
        self.default_validate_argument(argument)
    }

    fn default_validate_argument(&mut self, argument: &Argument) -> DiagnosticsResult<()> {
        self.validate_value(&argument.value.item)
    }

    // Values
    fn validate_value(&mut self, value: &Value) -> DiagnosticsResult<()> {
        self.default_validate_value(value)
    }

    fn default_validate_value(&mut self, value: &Value) -> DiagnosticsResult<()> {
        match value {
            Value::Variable(variable) => self.validate_variable(variable),
            Value::Constant(_) => Ok(()),
            Value::List(items) => self.validate_list(items, Self::validate_value),
            Value::Object(arguments) => self.validate_arguments(arguments),
        }
    }

    fn validate_variable(&mut self, value: &Variable) -> DiagnosticsResult<()> {
        let _ = value;
        Ok(())
    }

    // Helpers
    fn validate_list<F, T>(&mut self, list: &[T], f: F) -> DiagnosticsResult<()>
    where
        F: Fn(&mut Self, &T) -> DiagnosticsResult<()>,
    {
        validate_map(list, |item| f(self, item))
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::ir::*;
use crate::program::Program;

pub trait Visitor {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;

    fn visit_program(&mut self, program: &Program) {
        self.default_visit_program(program)
    }

    fn default_visit_program(&mut self, program: &Program) {
        for operation in program.operations() {
            self.visit_operation(operation);
        }
        for fragment in program.fragments() {
            self.visit_fragment(fragment);
        }
    }

    // Fragment Definition
    fn visit_fragment(&mut self, fragment: &FragmentDefinition) {
        self.default_visit_fragment(fragment)
    }

    fn default_visit_fragment(&mut self, fragment: &FragmentDefinition) {
        self.visit_selections(&fragment.selections);
        self.visit_directives(&fragment.directives);
        self.visit_variable_definitions(&fragment.variable_definitions);
        self.visit_variable_definitions(&fragment.used_global_variables);
    }

    // Operation Definition
    fn visit_operation(&mut self, operation: &OperationDefinition) {
        self.default_visit_operation(operation)
    }

    fn default_visit_operation(&mut self, operation: &OperationDefinition) {
        self.visit_directives(&operation.directives);
        self.visit_selections(&operation.selections);
        self.visit_variable_definitions(&operation.variable_definitions);
    }

    // Selection
    fn visit_selections(&mut self, selections: &[Selection]) {
        self.visit_list(selections, Self::visit_selection)
    }

    fn visit_selection(&mut self, selection: &Selection) {
        self.default_visit_selection(selection)
    }

    fn default_visit_selection(&mut self, selection: &Selection) {
        match selection {
            Selection::FragmentSpread(selection) => self.visit_fragment_spread(selection),
            Selection::InlineFragment(selection) => self.visit_inline_fragment(selection),
            Selection::LinkedField(selection) => self.visit_linked_field(selection),
            Selection::ScalarField(selection) => self.visit_scalar_field(selection),
            Selection::Condition(selection) => self.visit_condition(selection),
        }
    }

    // Selection Kinds
    fn visit_scalar_field(&mut self, field: &ScalarField) {
        self.default_visit_scalar_field(field)
    }

    fn default_visit_scalar_field(&mut self, field: &ScalarField) {
        self.visit_arguments(&field.arguments);
        self.visit_directives(&field.directives);
    }

    fn visit_linked_field(&mut self, field: &LinkedField) {
        self.default_visit_linked_field(field)
    }

    fn default_visit_linked_field(&mut self, field: &LinkedField) {
        self.visit_arguments(&field.arguments);
        self.visit_directives(&field.directives);
        self.visit_selections(&field.selections);
    }

    fn visit_inline_fragment(&mut self, fragment: &InlineFragment) {
        self.default_visit_inline_fragment(fragment)
    }

    fn default_visit_inline_fragment(&mut self, fragment: &InlineFragment) {
        self.visit_directives(&fragment.directives);
        self.visit_selections(&fragment.selections);
    }

    fn visit_fragment_spread(&mut self, spread: &FragmentSpread) {
        self.default_visit_fragment_spread(spread)
    }
    fn default_visit_fragment_spread(&mut self, spread: &FragmentSpread) {
        self.visit_arguments(&spread.arguments);
        self.visit_directives(&spread.directives);
    }

    fn visit_condition(&mut self, condition: &Condition) {
        self.default_visit_condition(condition)
    }
    fn default_visit_condition(&mut self, condition: &Condition) {
        self.visit_condition_value(&condition.value);
        self.visit_selections(&condition.selections);
    }

    fn visit_condition_value(&mut self, condition_value: &ConditionValue) {
        self.default_visit_condition_value(condition_value);
    }
    fn default_visit_condition_value(&mut self, condition_value: &ConditionValue) {
        if Self::VISIT_ARGUMENTS {
            match condition_value {
                ConditionValue::Variable(variable) => self.visit_variable(variable),
                ConditionValue::Constant(_) => {}
            }
        }
    }

    // Directives
    fn visit_directives(&mut self, directives: &[Directive]) {
        if Self::VISIT_DIRECTIVES {
            self.visit_list(directives, Self::visit_directive)
        }
    }

    fn visit_directive(&mut self, directive: &Directive) {
        self.default_visit_directive(directive)
    }

    fn default_visit_directive(&mut self, directive: &Directive) {
        self.visit_arguments(&directive.arguments)
    }

    // Arguments
    fn visit_arguments(&mut self, arguments: &[Argument]) {
        if Self::VISIT_ARGUMENTS {
            self.visit_list(arguments, Self::visit_argument)
        }
    }

    fn visit_argument(&mut self, argument: &Argument) {
        self.default_visit_argument(argument)
    }

    fn default_visit_argument(&mut self, argument: &Argument) {
        self.visit_value(&argument.value.item)
    }

    // Values
    fn visit_value(&mut self, value: &Value) {
        self.default_visit_value(value)
    }

    fn default_visit_value(&mut self, value: &Value) {
        match value {
            Value::Variable(variable) => self.visit_variable(variable),
            Value::Constant(_) => {}
            Value::List(items) => self.visit_list(items, Self::visit_value),
            Value::Object(arguments) => self.visit_arguments(arguments),
        }
    }

    fn visit_variable(&mut self, value: &Variable) {
        let _ = value;
    }

    // Variable Definitions
    fn visit_variable_definitions(&mut self, variable_definitions: &[VariableDefinition]) {
        self.visit_list(variable_definitions, Self::visit_variable_definition)
    }

    fn visit_variable_definition(&mut self, variable_definition: &VariableDefinition) {
        self.default_visit_variable_definition(variable_definition)
    }

    fn default_visit_variable_definition(&mut self, variable_definition: &VariableDefinition) {
        self.visit_directives(&variable_definition.directives)
    }

    // Helpers
    fn visit_list<F, T>(&mut self, list: &[T], f: F)
    where
        F: Fn(&mut Self, &T),
        T: Clone,
    {
        for prev_item in list {
            f(self, prev_item)
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @generated SignedSource<<9dade3d0ff881e265617477148740319>>
 */

mod parse;

use parse::transform_fixture;
use fixture_tests::test_fixture;

#[test]
fn argument_complex_object_invalid() {
    let input = include_str!("parse/fixtures/argument-complex-object.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument-complex-object.invalid.expected");
    test_fixture(transform_fixture, "argument-complex-object.invalid.graphql", "parse/fixtures/argument-complex-object.invalid.expected", input, expected);
}

#[test]
fn argument_definitions() {
    let input = include_str!("parse/fixtures/argument_definitions.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions.expected");
    test_fixture(transform_fixture, "argument_definitions.graphql", "parse/fixtures/argument_definitions.expected", input, expected);
}

#[test]
fn argument_definitions_directives_invalid_directive_arg_invalid() {
    let input = include_str!("parse/fixtures/argument_definitions_directives_invalid_directive_arg.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions_directives_invalid_directive_arg.invalid.expected");
    test_fixture(transform_fixture, "argument_definitions_directives_invalid_directive_arg.invalid.graphql", "parse/fixtures/argument_definitions_directives_invalid_directive_arg.invalid.expected", input, expected);
}

#[test]
fn argument_definitions_directives_invalid_locations_invalid() {
    let input = include_str!("parse/fixtures/argument_definitions_directives_invalid_locations.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions_directives_invalid_locations.invalid.expected");
    test_fixture(transform_fixture, "argument_definitions_directives_invalid_locations.invalid.graphql", "parse/fixtures/argument_definitions_directives_invalid_locations.invalid.expected", input, expected);
}

#[test]
fn argument_definitions_directives_invalid_syntax_invalid() {
    let input = include_str!("parse/fixtures/argument_definitions_directives_invalid_syntax.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions_directives_invalid_syntax.invalid.expected");
    test_fixture(transform_fixture, "argument_definitions_directives_invalid_syntax.invalid.graphql", "parse/fixtures/argument_definitions_directives_invalid_syntax.invalid.expected", input, expected);
}

#[test]
fn argument_definitions_directives_invalid_type_invalid() {
    let input = include_str!("parse/fixtures/argument_definitions_directives_invalid_type.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions_directives_invalid_type.invalid.expected");
    test_fixture(transform_fixture, "argument_definitions_directives_invalid_type.invalid.graphql", "parse/fixtures/argument_definitions_directives_invalid_type.invalid.expected", input, expected);
}

#[test]
fn argument_definitions_typo_invalid() {
    let input = include_str!("parse/fixtures/argument_definitions_typo.invalid.graphql");
    let expected = include_str!("parse/fixtures/argument_definitions_typo.invalid.expected");
    test_fixture(transform_fixture, "argument_definitions_typo.invalid.graphql", "parse/fixtures/argument_definitions_typo.invalid.expected", input, expected);
}

#[test]
fn complex_object_with_invalid_constant_fields_invalid() {
    let input = include_str!("parse/fixtures/complex-object-with-invalid-constant-fields.invalid.graphql");
    let expected = include_str!("parse/fixtures/complex-object-with-invalid-constant-fields.invalid.expected");
    test_fixture(transform_fixture, "complex-object-with-invalid-constant-fields.invalid.graphql", "parse/fixtures/complex-object-with-invalid-constant-fields.invalid.expected", input, expected);
}

#[test]
fn complex_object_with_invalid_fields_invalid() {
    let input = include_str!("parse/fixtures/complex-object-with-invalid-fields.invalid.graphql");
    let expected = include_str!("parse/fixtures/complex-object-with-invalid-fields.invalid.expected");
    test_fixture(transform_fixture, "complex-object-with-invalid-fields.invalid.graphql", "parse/fixtures/complex-object-with-invalid-fields.invalid.expected", input, expected);
}

#[test]
fn complex_object_with_missing_fields_invalid() {
    let input = include_str!("parse/fixtures/complex-object-with-missing-fields.invalid.graphql");
    let expected = include_str!("parse/fixtures/complex-object-with-missing-fields.invalid.expected");
    test_fixture(transform_fixture, "complex-object-with-missing-fields.invalid.graphql", "parse/fixtures/complex-object-with-missing-fields.invalid.expected", input, expected);
}

#[test]
fn directive_generic() {
    let input = include_str!("parse/fixtures/directive-generic.graphql");
    let expected = include_str!("parse/fixtures/directive-generic.expected");
    test_fixture(transform_fixture, "directive-generic.graphql", "parse/fixtures/directive-generic.expected", input, expected);
}

#[test]
fn directive_include() {
    let input = include_str!("parse/fixtures/directive-include.graphql");
    let expected = include_str!("parse/fixtures/directive-include.expected");
    test_fixture(transform_fixture, "directive-include.graphql", "parse/fixtures/directive-include.expected", input, expected);
}

#[test]
fn directive_match_on_fragment_invalid() {
    let input = include_str!("parse/fixtures/directive-match-on-fragment.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive-match-on-fragment.invalid.expected");
    test_fixture(transform_fixture, "directive-match-on-fragment.invalid.graphql", "parse/fixtures/directive-match-on-fragment.invalid.expected", input, expected);
}

#[test]
fn directive_missing_required_argument_invalid() {
    let input = include_str!("parse/fixtures/directive_missing_required_argument.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive_missing_required_argument.invalid.expected");
    test_fixture(transform_fixture, "directive_missing_required_argument.invalid.graphql", "parse/fixtures/directive_missing_required_argument.invalid.expected", input, expected);
}

#[test]
fn directive_module_match_on_query_invalid() {
    let input = include_str!("parse/fixtures/directive-module-match-on-query.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive-module-match-on-query.invalid.expected");
    test_fixture(transform_fixture, "directive-module-match-on-query.invalid.graphql", "parse/fixtures/directive-module-match-on-query.invalid.expected", input, expected);
}

#[test]
fn directive_module_on_field_invalid() {
    let input = include_str!("parse/fixtures/directive-module-on-field.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive-module-on-field.invalid.expected");
    test_fixture(transform_fixture, "directive-module-on-field.invalid.graphql", "parse/fixtures/directive-module-on-field.invalid.expected", input, expected);
}

#[test]
fn directive_unknown_argument_invalid() {
    let input = include_str!("parse/fixtures/directive-unknown-argument.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive-unknown-argument.invalid.expected");
    test_fixture(transform_fixture, "directive-unknown-argument.invalid.graphql", "parse/fixtures/directive-unknown-argument.invalid.expected", input, expected);
}

#[test]
fn directive_wrong_argument_type_invalid() {
    let input = include_str!("parse/fixtures/directive_wrong_argument_type.invalid.graphql");
    let expected = include_str!("parse/fixtures/directive_wrong_argument_type.invalid.expected");
    test_fixture(transform_fixture, "directive_wrong_argument_type.invalid.graphql", "parse/fixtures/directive_wrong_argument_type.invalid.expected", input, expected);
}

#[test]
fn duplicate_arguments_invalid() {
    let input = include_str!("parse/fixtures/duplicate_arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/duplicate_arguments.invalid.expected");
    test_fixture(transform_fixture, "duplicate_arguments.invalid.graphql", "parse/fixtures/duplicate_arguments.invalid.expected", input, expected);
}

#[test]
fn duplicate_variables_invalid() {
    let input = include_str!("parse/fixtures/duplicate_variables_invalid.graphql");
    let expected = include_str!("parse/fixtures/duplicate_variables_invalid.expected");
    test_fixture(transform_fixture, "duplicate_variables_invalid.graphql", "parse/fixtures/duplicate_variables_invalid.expected", input, expected);
}

#[test]
fn enum_values() {
    let input = include_str!("parse/fixtures/enum-values.graphql");
    let expected = include_str!("parse/fixtures/enum-values.expected");
    test_fixture(transform_fixture, "enum-values.graphql", "parse/fixtures/enum-values.expected", input, expected);
}

#[test]
fn enum_values_invalid() {
    let input = include_str!("parse/fixtures/enum-values.invalid.graphql");
    let expected = include_str!("parse/fixtures/enum-values.invalid.expected");
    test_fixture(transform_fixture, "enum-values.invalid.graphql", "parse/fixtures/enum-values.invalid.expected", input, expected);
}

#[test]
fn fetch_token_with_arguments_invalid() {
    let input = include_str!("parse/fixtures/fetch_token_with_arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/fetch_token_with_arguments.invalid.expected");
    test_fixture(transform_fixture, "fetch_token_with_arguments.invalid.graphql", "parse/fixtures/fetch_token_with_arguments.invalid.expected", input, expected);
}

#[test]
fn field_argument_missing_required_invalid() {
    let input = include_str!("parse/fixtures/field_argument_missing_required.invalid.graphql");
    let expected = include_str!("parse/fixtures/field_argument_missing_required.invalid.expected");
    test_fixture(transform_fixture, "field_argument_missing_required.invalid.graphql", "parse/fixtures/field_argument_missing_required.invalid.expected", input, expected);
}

#[test]
fn field_argument_unknown_invalid() {
    let input = include_str!("parse/fixtures/field_argument_unknown.invalid.graphql");
    let expected = include_str!("parse/fixtures/field_argument_unknown.invalid.expected");
    test_fixture(transform_fixture, "field_argument_unknown.invalid.graphql", "parse/fixtures/field_argument_unknown.invalid.expected", input, expected);
}

#[test]
fn field_argument_wrong_type_invalid() {
    let input = include_str!("parse/fixtures/field_argument_wrong_type.invalid.graphql");
    let expected = include_str!("parse/fixtures/field_argument_wrong_type.invalid.expected");
    test_fixture(transform_fixture, "field_argument_wrong_type.invalid.graphql", "parse/fixtures/field_argument_wrong_type.invalid.expected", input, expected);
}

#[test]
fn field_arguments() {
    let input = include_str!("parse/fixtures/field-arguments.graphql");
    let expected = include_str!("parse/fixtures/field-arguments.expected");
    test_fixture(transform_fixture, "field-arguments.graphql", "parse/fixtures/field-arguments.expected", input, expected);
}

#[test]
fn fixme_fat_interface_on_union() {
    let input = include_str!("parse/fixtures/fixme_fat_interface_on_union.graphql");
    let expected = include_str!("parse/fixtures/fixme_fat_interface_on_union.expected");
    test_fixture(transform_fixture, "fixme_fat_interface_on_union.graphql", "parse/fixtures/fixme_fat_interface_on_union.expected", input, expected);
}

#[test]
fn fixme_fat_interface_on_union_invalid() {
    let input = include_str!("parse/fixtures/fixme_fat_interface_on_union.invalid.graphql");
    let expected = include_str!("parse/fixtures/fixme_fat_interface_on_union.invalid.expected");
    test_fixture(transform_fixture, "fixme_fat_interface_on_union.invalid.graphql", "parse/fixtures/fixme_fat_interface_on_union.invalid.expected", input, expected);
}

#[test]
fn fragment_spread_on_wrong_type_invalid() {
    let input = include_str!("parse/fixtures/fragment-spread-on-wrong-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-spread-on-wrong-type.invalid.expected");
    test_fixture(transform_fixture, "fragment-spread-on-wrong-type.invalid.graphql", "parse/fixtures/fragment-spread-on-wrong-type.invalid.expected", input, expected);
}

#[test]
fn fragment_spread_within_wrong_parent_type_invalid() {
    let input = include_str!("parse/fixtures/fragment-spread-within-wrong-parent-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-spread-within-wrong-parent-type.invalid.expected");
    test_fixture(transform_fixture, "fragment-spread-within-wrong-parent-type.invalid.graphql", "parse/fixtures/fragment-spread-within-wrong-parent-type.invalid.expected", input, expected);
}

#[test]
fn fragment_with_argument_type_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-argument-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-argument-type.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-argument-type.invalid.graphql", "parse/fixtures/fragment-with-argument-type.invalid.expected", input, expected);
}

#[test]
fn fragment_with_argument_type_syntax_error_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-argument-type-syntax-error.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-argument-type-syntax-error.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-argument-type-syntax-error.invalid.graphql", "parse/fixtures/fragment-with-argument-type-syntax-error.invalid.expected", input, expected);
}

#[test]
fn fragment_with_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-arguments.graphql", "parse/fixtures/fragment-with-arguments.expected", input, expected);
}

#[test]
fn fragment_with_arguments_defaulting() {
    let input = include_str!("parse/fixtures/fragment_with_arguments_defaulting.graphql");
    let expected = include_str!("parse/fixtures/fragment_with_arguments_defaulting.expected");
    test_fixture(transform_fixture, "fragment_with_arguments_defaulting.graphql", "parse/fixtures/fragment_with_arguments_defaulting.expected", input, expected);
}

#[test]
fn fragment_with_arguments_duplicate_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-arguments-duplicate.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-arguments-duplicate.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-arguments-duplicate.invalid.graphql", "parse/fixtures/fragment-with-arguments-duplicate.invalid.expected", input, expected);
}

#[test]
fn fragment_with_arguments_incorrect_nullability_invalid() {
    let input = include_str!("parse/fixtures/fragment_with_arguments_incorrect_nullability.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment_with_arguments_incorrect_nullability.invalid.expected");
    test_fixture(transform_fixture, "fragment_with_arguments_incorrect_nullability.invalid.graphql", "parse/fixtures/fragment_with_arguments_incorrect_nullability.invalid.expected", input, expected);
}

#[test]
fn fragment_with_arguments_invalid_type_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-arguments-invalid-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-arguments-invalid-type.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-arguments-invalid-type.invalid.graphql", "parse/fixtures/fragment-with-arguments-invalid-type.invalid.expected", input, expected);
}

#[test]
fn fragment_with_arguments_syntax() {
    let input = include_str!("parse/fixtures/fragment-with-arguments-syntax.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-arguments-syntax.expected");
    test_fixture(transform_fixture, "fragment-with-arguments-syntax.graphql", "parse/fixtures/fragment-with-arguments-syntax.expected", input, expected);
}

#[test]
fn fragment_with_both_arguments_and_directive_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-both-arguments-and-directive.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-both-arguments-and-directive.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-both-arguments-and-directive.invalid.graphql", "parse/fixtures/fragment-with-both-arguments-and-directive.invalid.expected", input, expected);
}

#[test]
fn fragment_with_both_variable_definition_and_directive_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-both-variable-definition-and-directive.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-both-variable-definition-and-directive.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-both-variable-definition-and-directive.invalid.graphql", "parse/fixtures/fragment-with-both-variable-definition-and-directive.invalid.expected", input, expected);
}

#[test]
fn fragment_with_literal_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-literal-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-literal-arguments.graphql", "parse/fixtures/fragment-with-literal-arguments.expected", input, expected);
}

#[test]
fn fragment_with_literal_enum_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-literal-enum-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-enum-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-literal-enum-arguments.graphql", "parse/fixtures/fragment-with-literal-enum-arguments.expected", input, expected);
}

#[test]
fn fragment_with_literal_enum_arguments_into_enum_list() {
    let input = include_str!("parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list.expected");
    test_fixture(transform_fixture, "fragment-with-literal-enum-arguments-into-enum-list.graphql", "parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list.expected", input, expected);
}

#[test]
fn fragment_with_literal_enum_arguments_into_enum_list_indirect_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list-indirect.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list-indirect.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-literal-enum-arguments-into-enum-list-indirect.invalid.graphql", "parse/fixtures/fragment-with-literal-enum-arguments-into-enum-list-indirect.invalid.expected", input, expected);
}

#[test]
fn fragment_with_literal_enum_list_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-literal-enum-list-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-enum-list-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-literal-enum-list-arguments.graphql", "parse/fixtures/fragment-with-literal-enum-list-arguments.expected", input, expected);
}

#[test]
fn fragment_with_literal_object_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-literal-object-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-object-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-literal-object-arguments.graphql", "parse/fixtures/fragment-with-literal-object-arguments.expected", input, expected);
}

#[test]
fn fragment_with_literal_object_list_arguments() {
    let input = include_str!("parse/fixtures/fragment-with-literal-object-list-arguments.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-literal-object-list-arguments.expected");
    test_fixture(transform_fixture, "fragment-with-literal-object-list-arguments.graphql", "parse/fixtures/fragment-with-literal-object-list-arguments.expected", input, expected);
}

#[test]
fn fragment_with_undefined_literal_arguments_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-undefined-literal-arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-undefined-literal-arguments.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-undefined-literal-arguments.invalid.graphql", "parse/fixtures/fragment-with-undefined-literal-arguments.invalid.expected", input, expected);
}

#[test]
fn fragment_with_undefined_variable_arguments_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-undefined-variable-arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-undefined-variable-arguments.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-undefined-variable-arguments.invalid.graphql", "parse/fixtures/fragment-with-undefined-variable-arguments.invalid.expected", input, expected);
}

#[test]
fn fragment_with_unnecessary_unchecked_arguments_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-unnecessary-unchecked-arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-unnecessary-unchecked-arguments.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-unnecessary-unchecked-arguments.invalid.graphql", "parse/fixtures/fragment-with-unnecessary-unchecked-arguments.invalid.expected", input, expected);
}

#[test]
fn fragment_with_variable_definitions_syntax() {
    let input = include_str!("parse/fixtures/fragment-with-variable-definitions-syntax.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-variable-definitions-syntax.expected");
    test_fixture(transform_fixture, "fragment-with-variable-definitions-syntax.graphql", "parse/fixtures/fragment-with-variable-definitions-syntax.expected", input, expected);
}

#[test]
fn fragment_with_variable_definitions_syntax_and_argdefs_invalid() {
    let input = include_str!("parse/fixtures/fragment-with-variable-definitions-syntax-and-argdefs.invalid.graphql");
    let expected = include_str!("parse/fixtures/fragment-with-variable-definitions-syntax-and-argdefs.invalid.expected");
    test_fixture(transform_fixture, "fragment-with-variable-definitions-syntax-and-argdefs.invalid.graphql", "parse/fixtures/fragment-with-variable-definitions-syntax-and-argdefs.invalid.expected", input, expected);
}

#[test]
fn inline_fragment_on_wrong_type_invalid() {
    let input = include_str!("parse/fixtures/inline-fragment-on-wrong-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/inline-fragment-on-wrong-type.invalid.expected");
    test_fixture(transform_fixture, "inline-fragment-on-wrong-type.invalid.graphql", "parse/fixtures/inline-fragment-on-wrong-type.invalid.expected", input, expected);
}

#[test]
fn inline_fragment_with_invalid_type() {
    let input = include_str!("parse/fixtures/inline-fragment-with-invalid-type.graphql");
    let expected = include_str!("parse/fixtures/inline-fragment-with-invalid-type.expected");
    test_fixture(transform_fixture, "inline-fragment-with-invalid-type.graphql", "parse/fixtures/inline-fragment-with-invalid-type.expected", input, expected);
}

#[test]
fn inline_fragment_within_invalid_inline_fragment_grandparent_invalid() {
    let input = include_str!("parse/fixtures/inline-fragment-within-invalid-inline-fragment-grandparent.invalid.graphql");
    let expected = include_str!("parse/fixtures/inline-fragment-within-invalid-inline-fragment-grandparent.invalid.expected");
    test_fixture(transform_fixture, "inline-fragment-within-invalid-inline-fragment-grandparent.invalid.graphql", "parse/fixtures/inline-fragment-within-invalid-inline-fragment-grandparent.invalid.expected", input, expected);
}

#[test]
fn inline_fragment_within_linked_field_and_wrong_inline_fragment_invalid() {
    let input = include_str!("parse/fixtures/inline-fragment-within-linked-field-and-wrong-inline-fragment.invalid.graphql");
    let expected = include_str!("parse/fixtures/inline-fragment-within-linked-field-and-wrong-inline-fragment.invalid.expected");
    test_fixture(transform_fixture, "inline-fragment-within-linked-field-and-wrong-inline-fragment.invalid.graphql", "parse/fixtures/inline-fragment-within-linked-field-and-wrong-inline-fragment.invalid.expected", input, expected);
}

#[test]
fn inline_fragment_within_wrong_parent_type_invalid() {
    let input = include_str!("parse/fixtures/inline-fragment-within-wrong-parent-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/inline-fragment-within-wrong-parent-type.invalid.expected");
    test_fixture(transform_fixture, "inline-fragment-within-wrong-parent-type.invalid.graphql", "parse/fixtures/inline-fragment-within-wrong-parent-type.invalid.expected", input, expected);
}

#[test]
fn inline_untyped_fragment() {
    let input = include_str!("parse/fixtures/inline-untyped-fragment.graphql");
    let expected = include_str!("parse/fixtures/inline-untyped-fragment.expected");
    test_fixture(transform_fixture, "inline-untyped-fragment.graphql", "parse/fixtures/inline-untyped-fragment.expected", input, expected);
}

#[test]
fn invalid_argument_type_invalid() {
    let input = include_str!("parse/fixtures/invalid-argument-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/invalid-argument-type.invalid.expected");
    test_fixture(transform_fixture, "invalid-argument-type.invalid.graphql", "parse/fixtures/invalid-argument-type.invalid.expected", input, expected);
}

#[test]
fn linked_field_with_no_selection_invalid() {
    let input = include_str!("parse/fixtures/linked-field-with-no-selection.invalid.graphql");
    let expected = include_str!("parse/fixtures/linked-field-with-no-selection.invalid.expected");
    test_fixture(transform_fixture, "linked-field-with-no-selection.invalid.graphql", "parse/fixtures/linked-field-with-no-selection.invalid.expected", input, expected);
}

#[test]
fn linked_handle_field() {
    let input = include_str!("parse/fixtures/linked-handle-field.graphql");
    let expected = include_str!("parse/fixtures/linked-handle-field.expected");
    test_fixture(transform_fixture, "linked-handle-field.graphql", "parse/fixtures/linked-handle-field.expected", input, expected);
}

#[test]
fn linked_handle_field_with_filters() {
    let input = include_str!("parse/fixtures/linked-handle-field-with-filters.graphql");
    let expected = include_str!("parse/fixtures/linked-handle-field-with-filters.expected");
    test_fixture(transform_fixture, "linked-handle-field-with-filters.graphql", "parse/fixtures/linked-handle-field-with-filters.expected", input, expected);
}

#[test]
fn linked_handle_field_with_key() {
    let input = include_str!("parse/fixtures/linked-handle-field-with-key.graphql");
    let expected = include_str!("parse/fixtures/linked-handle-field-with-key.expected");
    test_fixture(transform_fixture, "linked-handle-field-with-key.graphql", "parse/fixtures/linked-handle-field-with-key.expected", input, expected);
}

#[test]
fn linked_handle_filter() {
    let input = include_str!("parse/fixtures/linked-handle-filter.graphql");
    let expected = include_str!("parse/fixtures/linked-handle-filter.expected");
    test_fixture(transform_fixture, "linked-handle-filter.graphql", "parse/fixtures/linked-handle-filter.expected", input, expected);
}

#[test]
fn list_argument() {
    let input = include_str!("parse/fixtures/list-argument.graphql");
    let expected = include_str!("parse/fixtures/list-argument.expected");
    test_fixture(transform_fixture, "list-argument.graphql", "parse/fixtures/list-argument.expected", input, expected);
}

#[test]
fn list_argument_complex_object() {
    let input = include_str!("parse/fixtures/list-argument-complex-object.graphql");
    let expected = include_str!("parse/fixtures/list-argument-complex-object.expected");
    test_fixture(transform_fixture, "list-argument-complex-object.graphql", "parse/fixtures/list-argument-complex-object.expected", input, expected);
}

#[test]
fn list_of_enums() {
    let input = include_str!("parse/fixtures/list-of-enums.graphql");
    let expected = include_str!("parse/fixtures/list-of-enums.expected");
    test_fixture(transform_fixture, "list-of-enums.graphql", "parse/fixtures/list-of-enums.expected", input, expected);
}

#[test]
fn literal_list_argument() {
    let input = include_str!("parse/fixtures/literal-list-argument.graphql");
    let expected = include_str!("parse/fixtures/literal-list-argument.expected");
    test_fixture(transform_fixture, "literal-list-argument.graphql", "parse/fixtures/literal-list-argument.expected", input, expected);
}

#[test]
fn literal_list_argument_invalid() {
    let input = include_str!("parse/fixtures/literal-list-argument.invalid.graphql");
    let expected = include_str!("parse/fixtures/literal-list-argument.invalid.expected");
    test_fixture(transform_fixture, "literal-list-argument.invalid.graphql", "parse/fixtures/literal-list-argument.invalid.expected", input, expected);
}

#[test]
fn literal_object_argument() {
    let input = include_str!("parse/fixtures/literal-object-argument.graphql");
    let expected = include_str!("parse/fixtures/literal-object-argument.expected");
    test_fixture(transform_fixture, "literal-object-argument.graphql", "parse/fixtures/literal-object-argument.expected", input, expected);
}

#[test]
fn literal_object_argument_invalid() {
    let input = include_str!("parse/fixtures/literal-object-argument.invalid.graphql");
    let expected = include_str!("parse/fixtures/literal-object-argument.invalid.expected");
    test_fixture(transform_fixture, "literal-object-argument.invalid.graphql", "parse/fixtures/literal-object-argument.invalid.expected", input, expected);
}

#[test]
fn null_values() {
    let input = include_str!("parse/fixtures/null-values.graphql");
    let expected = include_str!("parse/fixtures/null-values.expected");
    test_fixture(transform_fixture, "null-values.graphql", "parse/fixtures/null-values.expected", input, expected);
}

#[test]
fn null_values_invalid() {
    let input = include_str!("parse/fixtures/null-values.invalid.graphql");
    let expected = include_str!("parse/fixtures/null-values.invalid.expected");
    test_fixture(transform_fixture, "null-values.invalid.graphql", "parse/fixtures/null-values.invalid.expected", input, expected);
}

#[test]
fn object_argument() {
    let input = include_str!("parse/fixtures/object-argument.graphql");
    let expected = include_str!("parse/fixtures/object-argument.expected");
    test_fixture(transform_fixture, "object-argument.graphql", "parse/fixtures/object-argument.expected", input, expected);
}

#[test]
fn query_with_argument_invalid() {
    let input = include_str!("parse/fixtures/query-with-argument.invalid.graphql");
    let expected = include_str!("parse/fixtures/query-with-argument.invalid.expected");
    test_fixture(transform_fixture, "query-with-argument.invalid.graphql", "parse/fixtures/query-with-argument.invalid.expected", input, expected);
}

#[test]
fn scalar_field_with_selection() {
    let input = include_str!("parse/fixtures/scalar-field-with-selection.graphql");
    let expected = include_str!("parse/fixtures/scalar-field-with-selection.expected");
    test_fixture(transform_fixture, "scalar-field-with-selection.graphql", "parse/fixtures/scalar-field-with-selection.expected", input, expected);
}

#[test]
fn scalar_handle_field() {
    let input = include_str!("parse/fixtures/scalar-handle-field.graphql");
    let expected = include_str!("parse/fixtures/scalar-handle-field.expected");
    test_fixture(transform_fixture, "scalar-handle-field.graphql", "parse/fixtures/scalar-handle-field.expected", input, expected);
}

#[test]
fn simple_fragment() {
    let input = include_str!("parse/fixtures/simple-fragment.graphql");
    let expected = include_str!("parse/fixtures/simple-fragment.expected");
    test_fixture(transform_fixture, "simple-fragment.graphql", "parse/fixtures/simple-fragment.expected", input, expected);
}

#[test]
fn simple_query() {
    let input = include_str!("parse/fixtures/simple-query.graphql");
    let expected = include_str!("parse/fixtures/simple-query.expected");
    test_fixture(transform_fixture, "simple-query.graphql", "parse/fixtures/simple-query.expected", input, expected);
}

#[test]
fn subscription_with_multiple_selections_invalid() {
    let input = include_str!("parse/fixtures/subscription-with-multiple-selections.invalid.graphql");
    let expected = include_str!("parse/fixtures/subscription-with-multiple-selections.invalid.expected");
    test_fixture(transform_fixture, "subscription-with-multiple-selections.invalid.graphql", "parse/fixtures/subscription-with-multiple-selections.invalid.expected", input, expected);
}

#[test]
fn typename_with_arguments_invalid() {
    let input = include_str!("parse/fixtures/typename_with_arguments.invalid.graphql");
    let expected = include_str!("parse/fixtures/typename_with_arguments.invalid.expected");
    test_fixture(transform_fixture, "typename_with_arguments.invalid.graphql", "parse/fixtures/typename_with_arguments.invalid.expected", input, expected);
}

#[test]
fn undefined_fragment_but_close_invalid() {
    let input = include_str!("parse/fixtures/undefined-fragment-but-close.invalid.graphql");
    let expected = include_str!("parse/fixtures/undefined-fragment-but-close.invalid.expected");
    test_fixture(transform_fixture, "undefined-fragment-but-close.invalid.graphql", "parse/fixtures/undefined-fragment-but-close.invalid.expected", input, expected);
}

#[test]
fn undefined_fragment_invalid() {
    let input = include_str!("parse/fixtures/undefined-fragment.invalid.graphql");
    let expected = include_str!("parse/fixtures/undefined-fragment.invalid.expected");
    test_fixture(transform_fixture, "undefined-fragment.invalid.graphql", "parse/fixtures/undefined-fragment.invalid.expected", input, expected);
}

#[test]
fn undefined_type_invalid() {
    let input = include_str!("parse/fixtures/undefined-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/undefined-type.invalid.expected");
    test_fixture(transform_fixture, "undefined-type.invalid.graphql", "parse/fixtures/undefined-type.invalid.expected", input, expected);
}

#[test]
fn unknown_field_invalid() {
    let input = include_str!("parse/fixtures/unknown_field.invalid.graphql");
    let expected = include_str!("parse/fixtures/unknown_field.invalid.expected");
    test_fixture(transform_fixture, "unknown_field.invalid.graphql", "parse/fixtures/unknown_field.invalid.expected", input, expected);
}

#[test]
fn unknown_fragment_type_invalid() {
    let input = include_str!("parse/fixtures/unknown-fragment-type.invalid.graphql");
    let expected = include_str!("parse/fixtures/unknown-fragment-type.invalid.expected");
    test_fixture(transform_fixture, "unknown-fragment-type.invalid.graphql", "parse/fixtures/unknown-fragment-type.invalid.expected", input, expected);
}

#[test]
fn unknown_fragment_type_suggestions_invalid() {
    let input = include_str!("parse/fixtures/unknown-fragment-type-suggestions.invalid.graphql");
    let expected = include_str!("parse/fixtures/unknown-fragment-type-suggestions.invalid.expected");
    test_fixture(transform_fixture, "unknown-fragment-type-suggestions.invalid.graphql", "parse/fixtures/unknown-fragment-type-suggestions.invalid.expected", input, expected);
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @generated SignedSource<<e573b168de2e50d455bd24bed56b02c6>>
 */

mod parse_with_extensions;

use parse_with_extensions::transform_fixture;
use fixture_tests::test_fixture;

#[test]
fn client_fields() {
    let input = include_str!("parse_with_extensions/fixtures/client-fields.graphql");
    let expected = include_str!("parse_with_extensions/fixtures/client-fields.expected");
    test_fixture(transform_fixture, "client-fields.graphql", "parse_with_extensions/fixtures/client-fields.expected", input, expected);
}

#[test]
fn client_fields_invalid() {
    let input = include_str!("parse_with_extensions/fixtures/client-fields.invalid.graphql");
    let expected = include_str!("parse_with_extensions/fixtures/client-fields.invalid.expected");
    test_fixture(transform_fixture, "client-fields.invalid.graphql", "parse_with_extensions/fixtures/client-fields.invalid.expected", input, expected);
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @generated SignedSource<<1670bffb19ef1ac0b170241dddc11e88>>
 */

mod parse_with_provider;

use parse_with_provider::transform_fixture;
use fixture_tests::test_fixture;

#[test]
fn fragment_with_invalid_defaultvalue_provider() {
    let input = include_str!("parse_with_provider/fixtures/fragment_with_invalid_defaultvalue_provider.graphql");
    let expected = include_str!("parse_with_provider/fixtures/fragment_with_invalid_defaultvalue_provider.expected");
    test_fixture(transform_fixture, "fragment_with_invalid_defaultvalue_provider.graphql", "parse_with_provider/fixtures/fragment_with_invalid_defaultvalue_provider.expected", input, expected);
}

#[test]
fn fragment_with_invalid_type_provider() {
    let input = include_str!("parse_with_provider/fixtures/fragment_with_invalid_type_provider.graphql");
    let expected = include_str!("parse_with_provider/fixtures/fragment_with_invalid_type_provider.expected");
    test_fixture(transform_fixture, "fragment_with_invalid_type_provider.graphql", "parse_with_provider/fixtures/fragment_with_invalid_type_provider.expected", input, expected);
}

#[test]
fn fragment_with_valid_provider() {
    let input = include_str!("parse_with_provider/fixtures/fragment_with_valid_provider.graphql");
    let expected = include_str!("parse_with_provider/fixtures/fragment_with_valid_provider.expected");
    test_fixture(transform_fixture, "fragment_with_valid_provider.graphql", "parse_with_provider/fixtures/fragment_with_valid_provider.expected", input, expected);
}

#[test]
fn use_fragment_spread_with_provider() {
    let input = include_str!("parse_with_provider/fixtures/use_fragment_spread_with_provider.graphql");
    let expected = include_str!("parse_with_provider/fixtures/use_fragment_spread_with_provider.expected");
    test_fixture(transform_fixture, "use_fragment_spread_with_provider.graphql", "parse_with_provider/fixtures/use_fragment_spread_with_provider.expected", input, expected);
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::SourceLocationKey;
use common::TextSource;
use fixture_tests::Fixture;
use fnv::FnvHashMap;
use graphql_cli::DiagnosticPrinter;
use graphql_ir::build;
use graphql_syntax::parse_executable_with_features;
use graphql_syntax::FragmentArgumentSyntaxKind;
use graphql_syntax::ParserFeatures;
use relay_test_schema::TEST_SCHEMA;

pub fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String> {
    let source_location = SourceLocationKey::standalone(fixture.file_name);
    let features = ParserFeatures {
        fragment_argument_capability:
            FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions,
    };
    let ast = parse_executable_with_features(fixture.content, source_location, features).unwrap();
    let mut sources = FnvHashMap::default();
    sources.insert(source_location, fixture.content);

    build(&TEST_SCHEMA, &ast.definitions)
        .map(|x| format!("{:#?}", x))
        .map_err(|errors| {
            errors
                .into_iter()
                .map(|error| {
                    let printer = DiagnosticPrinter::new(|_| {
                        Some(TextSource::from_whole_document(fixture.content.to_string()))
                    });
                    printer.diagnostic_to_string(&error)
                })
                .collect::<Vec<_>>()
                .join("\n\n")
        })
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::SourceLocationKey;
use common::TextSource;
use fixture_tests::Fixture;
use fnv::FnvHashMap;
use graphql_cli::DiagnosticPrinter;
use graphql_ir::build;
use graphql_syntax::parse_executable;
use relay_test_schema::get_test_schema_with_extensions;

pub fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String> {
    let mut sources = FnvHashMap::default();
    sources.insert(
        SourceLocationKey::standalone(fixture.file_name),
        fixture.content,
    );

    let parts: Vec<_> = fixture.content.split("%extensions%").collect();
    if let [base, extensions] = parts.as_slice() {
        let source_location = SourceLocationKey::standalone(fixture.file_name);
        let ast = parse_executable(base, source_location).unwrap();
        let schema = get_test_schema_with_extensions(extensions);
        build(&schema, &ast.definitions)
            .map(|x| format!("{:#?}", x))
            .map_err(|errors| {
                errors
                    .into_iter()
                    .map(|error| {
                        let printer = DiagnosticPrinter::new(|_| {
                            Some(TextSource::from_whole_document(fixture.content.to_string()))
                        });
                        printer.diagnostic_to_string(&error)
                    })
                    .collect::<Vec<_>>()
                    .join("\n\n")
            })
    } else {
        panic!("Expected exactly one %extensions% section marker.")
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::SourceLocationKey;
use common::TextSource;
use fixture_tests::Fixture;
use fnv::FnvHashMap;
use graphql_cli::DiagnosticPrinter;
use graphql_ir::build_ir_with_extra_features;
use graphql_ir::BuilderOptions;
use graphql_ir::FragmentVariablesSemantic;
use graphql_ir::RelayMode;
use graphql_syntax::parse_executable_with_features;
use graphql_syntax::FragmentArgumentSyntaxKind;
use graphql_syntax::ParserFeatures;
use relay_test_schema::TEST_SCHEMA;

pub fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String> {
    let source_location = SourceLocationKey::standalone(fixture.file_name);
    let features = ParserFeatures {
        fragment_argument_capability:
            FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions,
    };
    let ast = parse_executable_with_features(fixture.content, source_location, features).unwrap();
    let mut sources = FnvHashMap::default();
    sources.insert(source_location, fixture.content);

    let builder_options = BuilderOptions {
        allow_undefined_fragment_spreads: false,
        fragment_variables_semantic: FragmentVariablesSemantic::PassedValue,
        relay_mode: Some(RelayMode),
        default_anonymous_operation_name: None,
    };

    build_ir_with_extra_features(&TEST_SCHEMA, &ast.definitions, &builder_options)
        .map(|x| format!("{:#?}", x))
        .map_err(|errors| {
            errors
                .into_iter()
                .map(|error| {
                    let printer = DiagnosticPrinter::new(|_| {
                        Some(TextSource::from_whole_document(fixture.content.to_string()))
                    });
                    printer.diagnostic_to_string(&error)
                })
                .collect::<Vec<_>>()
                .join("\n\n")
        })
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]
#![deny(clippy::clone_on_ref_ptr)]
#![allow(clippy::comparison_chain)]

mod validate_selection_conflict;

pub use validate_selection_conflict::validate_selection_conflict;