use std::collections::HashMap;
use crate::query_planner::ast::minification::error::MinificationError;
use crate::query_planner::ast::minification::selection_id::{generate_selection_id, SelectionId};
use crate::query_planner::ast::selection_item::SelectionItem;
use crate::query_planner::ast::selection_set::SelectionSet;
use crate::query_planner::state::supergraph_state::SupergraphState;
pub struct Stats {
state: HashMap<SelectionId, usize>,
contains_duplicates: bool,
}
impl Stats {
pub fn from_operation(
selection_set: &SelectionSet,
supergraph: &SupergraphState,
root_type_name: &str,
) -> Result<Self, MinificationError> {
let mut stats = Self {
state: HashMap::new(),
contains_duplicates: false,
};
walk_and_collect_stats(&mut stats, supergraph, selection_set, root_type_name)?;
Ok(stats)
}
pub fn has_duplicates(&self) -> bool {
self.contains_duplicates
}
pub fn is_duplicated(&self, key: &SelectionId) -> bool {
self.state.get(key).unwrap_or(&0) > &1
}
pub fn increase(&mut self, key: SelectionId) -> &usize {
let occurrences = self.state.entry(key).or_insert_with(|| 0);
*occurrences += 1;
if *occurrences > 1 {
self.contains_duplicates = true;
}
occurrences
}
}
fn walk_and_collect_stats(
stats: &mut Stats,
supergraph: &SupergraphState,
selection_set: &SelectionSet,
type_name: &str,
) -> Result<(), MinificationError> {
if selection_set.items.is_empty() {
return Ok(());
}
let id = generate_selection_id(type_name, selection_set);
let occurrences = stats.increase(id);
if *occurrences > 1 {
return Ok(());
}
let type_def = supergraph
.definitions
.get(type_name)
.ok_or_else(|| MinificationError::TypeNotFound(type_name.to_string()))?;
for item in &selection_set.items {
match item {
SelectionItem::Field(field) => {
if field.is_introspection_field() {
continue;
}
if field.name == "_entities" {
for item in &field.selections.items {
match item {
SelectionItem::Field(field) => {
if field.is_introspection_field() {
continue;
}
return Err(MinificationError::UnsupportedFieldInEntities(
type_def.name().to_string(),
field.name.clone(),
));
}
SelectionItem::InlineFragment(fragment) => {
walk_and_collect_stats(
stats,
supergraph,
&fragment.selections,
&fragment.type_condition,
)?;
}
SelectionItem::FragmentSpread(_) => {
return Err(MinificationError::UnsupportedFragmentSpread);
}
}
}
continue;
}
let child_type_name = type_def
.fields()
.get(&field.name)
.ok_or_else(|| {
MinificationError::FieldNotFound(
field.name.clone(),
type_def.name().to_string(),
)
})?
.field_type
.inner_type();
walk_and_collect_stats(stats, supergraph, &field.selections, child_type_name)?;
}
SelectionItem::InlineFragment(fragment) => {
walk_and_collect_stats(
stats,
supergraph,
&fragment.selections,
&fragment.type_condition,
)?;
}
SelectionItem::FragmentSpread(_) => {
return Err(MinificationError::UnsupportedFragmentSpread);
}
}
}
Ok(())
}