use std::collections::{HashMap, HashSet};
use super::graph::SchemaDependencyGraph;
use crate::schema::{CompiledSchema, FieldType};
impl SchemaDependencyGraph {
#[must_use]
#[allow(clippy::cognitive_complexity)] pub fn build(schema: &CompiledSchema) -> Self {
let mut outgoing: HashMap<String, HashSet<String>> = HashMap::new();
let mut incoming: HashMap<String, HashSet<String>> = HashMap::new();
let mut all_types: HashSet<String> = HashSet::new();
let mut root_types: HashSet<String> = HashSet::new();
for type_def in &schema.types {
all_types.insert(type_def.name.to_string());
outgoing.entry(type_def.name.to_string()).or_default();
incoming.entry(type_def.name.to_string()).or_default();
}
for enum_def in &schema.enums {
all_types.insert(enum_def.name.clone());
outgoing.entry(enum_def.name.clone()).or_default();
incoming.entry(enum_def.name.clone()).or_default();
}
for input_def in &schema.input_types {
all_types.insert(input_def.name.clone());
outgoing.entry(input_def.name.clone()).or_default();
incoming.entry(input_def.name.clone()).or_default();
}
for interface_def in &schema.interfaces {
all_types.insert(interface_def.name.clone());
outgoing.entry(interface_def.name.clone()).or_default();
incoming.entry(interface_def.name.clone()).or_default();
}
for union_def in &schema.unions {
all_types.insert(union_def.name.clone());
outgoing.entry(union_def.name.clone()).or_default();
incoming.entry(union_def.name.clone()).or_default();
}
if !schema.queries.is_empty() {
root_types.insert("Query".to_string());
all_types.insert("Query".to_string());
outgoing.entry("Query".to_string()).or_default();
incoming.entry("Query".to_string()).or_default();
}
if !schema.mutations.is_empty() {
root_types.insert("Mutation".to_string());
all_types.insert("Mutation".to_string());
outgoing.entry("Mutation".to_string()).or_default();
incoming.entry("Mutation".to_string()).or_default();
}
if !schema.subscriptions.is_empty() {
root_types.insert("Subscription".to_string());
all_types.insert("Subscription".to_string());
outgoing.entry("Subscription".to_string()).or_default();
incoming.entry("Subscription".to_string()).or_default();
}
for type_def in &schema.types {
for field in &type_def.fields {
if let Some(ref_type) = Self::extract_referenced_type(&field.field_type) {
if all_types.contains(&ref_type) {
outgoing
.entry(type_def.name.to_string())
.or_default()
.insert(ref_type.clone());
incoming
.entry(ref_type.clone())
.or_default()
.insert(type_def.name.to_string());
}
}
}
for interface_name in &type_def.implements {
if all_types.contains(interface_name) {
outgoing
.entry(type_def.name.to_string())
.or_default()
.insert(interface_name.clone());
incoming
.entry(interface_name.clone())
.or_default()
.insert(type_def.name.to_string());
}
}
}
for interface_def in &schema.interfaces {
for field in &interface_def.fields {
if let Some(ref_type) = Self::extract_referenced_type(&field.field_type) {
if all_types.contains(&ref_type) {
outgoing
.entry(interface_def.name.clone())
.or_default()
.insert(ref_type.clone());
incoming
.entry(ref_type.clone())
.or_default()
.insert(interface_def.name.clone());
}
}
}
}
for union_def in &schema.unions {
for member_type in &union_def.member_types {
if all_types.contains(member_type) {
outgoing.entry(union_def.name.clone()).or_default().insert(member_type.clone());
incoming.entry(member_type.clone()).or_default().insert(union_def.name.clone());
}
}
}
for input_def in &schema.input_types {
for field in &input_def.fields {
let parsed = FieldType::parse(&field.field_type);
if let Some(ref_type) = Self::extract_referenced_type(&parsed) {
if all_types.contains(&ref_type) {
outgoing
.entry(input_def.name.clone())
.or_default()
.insert(ref_type.clone());
incoming
.entry(ref_type.clone())
.or_default()
.insert(input_def.name.clone());
}
}
}
}
for query in &schema.queries {
let parsed = FieldType::parse(&query.return_type);
if let Some(ref_type) = Self::extract_referenced_type(&parsed) {
if all_types.contains(&ref_type) {
outgoing.entry("Query".to_string()).or_default().insert(ref_type.clone());
incoming.entry(ref_type.clone()).or_default().insert("Query".to_string());
}
}
}
for mutation in &schema.mutations {
let parsed = FieldType::parse(&mutation.return_type);
if let Some(ref_type) = Self::extract_referenced_type(&parsed) {
if all_types.contains(&ref_type) {
outgoing.entry("Mutation".to_string()).or_default().insert(ref_type.clone());
incoming.entry(ref_type.clone()).or_default().insert("Mutation".to_string());
}
}
}
for subscription in &schema.subscriptions {
let parsed = FieldType::parse(&subscription.return_type);
if let Some(ref_type) = Self::extract_referenced_type(&parsed) {
if all_types.contains(&ref_type) {
outgoing
.entry("Subscription".to_string())
.or_default()
.insert(ref_type.clone());
incoming
.entry(ref_type.clone())
.or_default()
.insert("Subscription".to_string());
}
}
}
Self {
outgoing,
incoming,
all_types,
root_types,
}
}
pub(super) fn extract_referenced_type(field_type: &FieldType) -> Option<String> {
match field_type {
FieldType::Object(name)
| FieldType::Enum(name)
| FieldType::Input(name)
| FieldType::Interface(name)
| FieldType::Union(name) => Some(name.clone()),
FieldType::List(inner) => Self::extract_referenced_type(inner),
_ => None, }
}
}