use super::{Schema, StoredInputFieldType, TypeId};
use crate::schema::resolve_field_type;
use gurkle_parser::schema::{self as parser, Definition, Document, TypeDefinition, UnionType};
pub(super) fn build_schema(mut src: gurkle_parser::schema::Document) -> super::Schema {
let mut schema = Schema::new();
convert(&mut src, &mut schema);
schema
}
fn convert(src: &mut gurkle_parser::schema::Document, schema: &mut Schema) {
populate_names_map(schema, &src.definitions);
src.definitions
.iter_mut()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Scalar(scalar)) => Some(scalar),
_ => None,
})
.for_each(|scalar| ingest_scalar(schema, scalar));
enums_mut(src).for_each(|enm| ingest_enum(schema, enm));
unions_mut(src).for_each(|union| ingest_union(schema, union));
interfaces_mut(src).for_each(|iface| ingest_interface(schema, iface));
objects_mut(src).for_each(|obj| ingest_object(schema, obj));
inputs_mut(src).for_each(|input| ingest_input(schema, input));
let schema_definition = src.definitions.iter_mut().find_map(|def| match def {
Definition::SchemaDefinition(definition) => Some(definition),
_ => None,
});
if let Some(schema_definition) = schema_definition {
schema.query_type = schema_definition
.query
.as_mut()
.and_then(|n| schema.names.get(n))
.and_then(|id| id.as_object_id());
schema.mutation_type = schema_definition
.mutation
.as_mut()
.and_then(|n| schema.names.get(n))
.and_then(|id| id.as_object_id());
schema.subscription_type = schema_definition
.subscription
.as_mut()
.and_then(|n| schema.names.get(n))
.and_then(|id| id.as_object_id());
} else {
schema.query_type = schema.names.get("Query").and_then(|id| id.as_object_id());
schema.mutation_type = schema
.names
.get("Mutation")
.and_then(|id| id.as_object_id());
schema.subscription_type = schema
.names
.get("Subscription")
.and_then(|id| id.as_object_id());
};
}
fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) {
definitions
.iter()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Enum(enm)) => Some(enm.name.as_str()),
_ => None,
})
.enumerate()
.for_each(|(idx, enum_name)| {
schema.names.insert(enum_name.into(), TypeId::r#enum(idx));
});
definitions
.iter()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Object(object)) => {
Some(object.name.as_str())
}
_ => None,
})
.enumerate()
.for_each(|(idx, object_name)| {
schema
.names
.insert(object_name.into(), TypeId::r#object(idx as u32));
});
definitions
.iter()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Interface(interface)) => {
Some(interface.name.as_str())
}
_ => None,
})
.enumerate()
.for_each(|(idx, interface_name)| {
schema
.names
.insert(interface_name.into(), TypeId::interface(idx));
});
definitions
.iter()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Union(union)) => Some(union.name.as_str()),
_ => None,
})
.enumerate()
.for_each(|(idx, union_name)| {
schema.names.insert(union_name.into(), TypeId::union(idx));
});
definitions
.iter()
.filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::InputObject(input)) => {
Some(input.name.as_str())
}
_ => None,
})
.enumerate()
.for_each(|(idx, input_name)| {
schema
.names
.insert(input_name.into(), TypeId::input(idx as u32));
});
}
fn ingest_union(schema: &mut Schema, union: &mut UnionType) {
let stored_union = super::StoredUnion {
name: std::mem::take(&mut union.name),
variants: union
.types
.iter()
.map(|name| schema.find_type_id(name))
.collect(),
};
schema.stored_unions.push(stored_union);
}
fn ingest_object(schema: &mut Schema, obj: &mut gurkle_parser::schema::ObjectType) {
let object_id = schema.find_type_id(&obj.name).as_object_id().unwrap();
let mut field_ids = Vec::with_capacity(obj.fields.len());
for field in obj.fields.iter_mut() {
let field = super::StoredField {
name: std::mem::take(&mut field.name),
r#type: resolve_field_type(schema, &field.field_type),
parent: super::StoredFieldParent::Object(object_id),
deprecation: find_deprecation(&field.directives),
};
field_ids.push(schema.push_field(field));
}
let object = super::StoredObject {
name: std::mem::take(&mut obj.name),
fields: field_ids,
implements_interfaces: obj
.implements_interfaces
.iter()
.map(|iface_name| schema.find_interface(iface_name))
.collect(),
};
schema.push_object(object);
}
fn ingest_scalar(schema: &mut Schema, scalar: &mut gurkle_parser::schema::ScalarType) {
let name = std::mem::take(&mut scalar.name);
let name_for_names = name.clone();
let scalar = super::StoredScalar { name };
let scalar_id = schema.push_scalar(scalar);
schema
.names
.insert(name_for_names, TypeId::Scalar(scalar_id));
}
fn ingest_enum(schema: &mut Schema, enm: &mut gurkle_parser::schema::EnumType) {
let enm = super::StoredEnum {
name: std::mem::take(&mut enm.name),
variants: enm
.values
.iter_mut()
.map(|value| std::mem::take(&mut value.name))
.collect(),
};
schema.push_enum(enm);
}
fn ingest_interface(schema: &mut Schema, interface: &mut gurkle_parser::schema::InterfaceType) {
let interface_id = schema
.find_type_id(&interface.name)
.as_interface_id()
.unwrap();
let mut field_ids = Vec::with_capacity(interface.fields.len());
for field in interface.fields.iter_mut() {
let field = super::StoredField {
name: std::mem::take(&mut field.name),
r#type: resolve_field_type(schema, &field.field_type),
parent: super::StoredFieldParent::Interface(interface_id),
deprecation: find_deprecation(&field.directives),
};
field_ids.push(schema.push_field(field));
}
let new_interface = super::StoredInterface {
name: std::mem::take(&mut interface.name),
fields: field_ids,
};
schema.push_interface(new_interface);
}
fn find_deprecation(directives: &[parser::Directive]) -> Option<Option<String>> {
directives
.iter()
.find(|directive| directive.name == "deprecated")
.map(|directive| {
directive
.arguments
.iter()
.find(|(name, _)| name == "reason")
.and_then(|(_, value)| match value {
gurkle_parser::query::Value::String(s) => Some(s.clone()),
_ => None,
})
})
}
fn ingest_input(schema: &mut Schema, input: &mut parser::InputObjectType) {
let input = super::StoredInputType {
name: std::mem::take(&mut input.name),
fields: input
.fields
.iter_mut()
.map(|val| {
let field_type = super::resolve_field_type(schema, &val.value_type);
(
std::mem::take(&mut val.name),
StoredInputFieldType {
qualifiers: field_type.qualifiers,
id: field_type.id,
},
)
})
.collect(),
};
schema.stored_inputs.push(input);
}
fn objects_mut(doc: &mut Document) -> impl Iterator<Item = &mut parser::ObjectType> {
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Object(obj)) => Some(obj),
_ => None,
})
}
fn interfaces_mut(doc: &mut Document) -> impl Iterator<Item = &mut parser::InterfaceType> {
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Interface(interface)) => Some(interface),
_ => None,
})
}
fn unions_mut(doc: &mut Document) -> impl Iterator<Item = &mut parser::UnionType> {
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Union(union)) => Some(union),
_ => None,
})
}
fn enums_mut(doc: &mut Document) -> impl Iterator<Item = &mut parser::EnumType> {
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::Enum(r#enum)) => Some(r#enum),
_ => None,
})
}
fn inputs_mut(doc: &mut Document) -> impl Iterator<Item = &mut parser::InputObjectType> {
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeDefinition(TypeDefinition::InputObject(input)) => Some(input),
_ => None,
})
}