1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use std::collections::HashMap;

use crate::{
    static_graphql::schema::{self},
    validation::utils::find_object_type_by_name,
};

use super::{find_schema_definition, CompositeType, TypeDefinitionExtension};

#[derive(Debug)]
pub struct TypeInfoRegistry<'a> {
    pub query_type: &'a schema::ObjectType,
    pub mutation_type: Option<&'a schema::ObjectType>,
    pub subscription_type: Option<&'a schema::ObjectType>,
    pub type_by_name: HashMap<String, &'a schema::TypeDefinition>,
    pub directives: HashMap<String, &'a schema::DirectiveDefinition>,
}

impl<'a> TypeInfoRegistry<'a> {
    pub fn new(schema: &'a schema::Document) -> Self {
        let schema_definition = find_schema_definition(&schema);
        let query_type = find_object_type_by_name(
            &schema,
            match schema_definition {
                Some(schema_definition) => schema_definition
                    .query
                    .clone()
                    .unwrap_or("Query".to_string()),
                None => "Query".to_string(),
            },
        )
        .expect("Schema does not contain a Query root type");
        let mutation_type = find_object_type_by_name(
            &schema,
            match schema_definition {
                Some(schema_definition) => schema_definition
                    .mutation
                    .clone()
                    .unwrap_or("Mutation".to_string()),
                None => "Mutation".to_string(),
            },
        );
        let subscription_type = find_object_type_by_name(
            &schema,
            match schema_definition {
                Some(schema_definition) => schema_definition
                    .subscription
                    .clone()
                    .unwrap_or("Subscription".to_string()),
                None => "Subscription".to_string(),
            },
        );

        let type_by_name =
            HashMap::from_iter(schema.definitions.iter().filter_map(
                |definition| match definition {
                    schema::Definition::TypeDefinition(type_definition) => {
                        Some((type_definition.name(), type_definition))
                    }
                    _ => None,
                },
            ));

        let directives =
            HashMap::from_iter(schema.definitions.iter().filter_map(
                |definition| match definition {
                    schema::Definition::DirectiveDefinition(directive_definition) => {
                        Some((directive_definition.name.clone(), directive_definition))
                    }
                    _ => None,
                },
            ));

        return TypeInfoRegistry {
            mutation_type,
            query_type,
            subscription_type,
            type_by_name,
            directives,
        };
    }
}

/// This struct is used to mark a "node" or nothing (null, undefined). While tracking TypeInfo, we need to check if there was a node before or not.
#[derive(Debug, Clone, Copy)]
pub enum TypeInfoElementRef<T> {
    Empty,
    Ref(T),
}

pub struct TypeInfo {
    pub type_stack: Vec<TypeInfoElementRef<schema::Type>>,
    pub parent_type_stack: Vec<TypeInfoElementRef<CompositeType>>,
    pub field_def_stack: Vec<TypeInfoElementRef<schema::Field>>,
    pub input_type_stack: Vec<TypeInfoElementRef<schema::InputObjectType>>,
    pub argument: Option<TypeInfoElementRef<schema::InputValue>>,
}

impl TypeInfo {
    pub fn new() -> Self {
        return TypeInfo {
            type_stack: Vec::new(),
            parent_type_stack: Vec::new(),
            input_type_stack: Vec::new(),
            field_def_stack: Vec::new(),
            argument: None,
        };
    }

    pub fn get_argument(&self) -> Option<TypeInfoElementRef<schema::InputValue>> {
        self.argument.clone()
    }

    pub fn enter_argument(&mut self, input_value: TypeInfoElementRef<schema::InputValue>) {
        self.argument = Some(input_value);
    }

    pub fn leave_argument(&mut self) {
        self.argument = None;
    }

    pub fn get_type(&self) -> Option<TypeInfoElementRef<schema::Type>> {
        self.type_stack.last().cloned()
    }

    pub fn enter_type(&mut self, object: TypeInfoElementRef<schema::Type>) {
        self.type_stack.push(object);
    }

    pub fn leave_type(&mut self) {
        self.type_stack.pop();
    }

    pub fn get_input_type(&self) -> Option<TypeInfoElementRef<schema::InputObjectType>> {
        self.input_type_stack.last().cloned()
    }

    pub fn enter_input_type(&mut self, object: TypeInfoElementRef<schema::InputObjectType>) {
        self.input_type_stack.push(object);
    }

    pub fn leave_input_type(&mut self) {
        self.input_type_stack.pop();
    }

    pub fn get_parent_type(&self) -> Option<TypeInfoElementRef<CompositeType>> {
        self.parent_type_stack.last().cloned()
    }

    pub fn enter_parent_type(&mut self, object: TypeInfoElementRef<CompositeType>) {
        self.parent_type_stack.push(object);
    }

    pub fn leave_parent_type(&mut self) {
        self.parent_type_stack.pop();
    }

    pub fn get_field_def(&self) -> Option<TypeInfoElementRef<schema::Field>> {
        self.field_def_stack.last().cloned()
    }

    pub fn enter_field_def(&mut self, field: TypeInfoElementRef<schema::Field>) {
        self.field_def_stack.push(field);
    }

    pub fn leave_field_def(&mut self) {
        self.field_def_stack.pop();
    }
}