use std::borrow::Cow;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::Arc;
use apollo_compiler::Name;
use hashbrown::DefaultHashBuilder;
use hashbrown::HashTable;
use itertools::Itertools;
use serde::Serialize;
use serde::ser::SerializeSeq;
use crate::error::FederationError;
use crate::operation::DirectiveList;
use crate::operation::Selection;
use crate::operation::SelectionId;
use crate::operation::SelectionSet;
use crate::operation::SiblingTypename;
use crate::operation::field_selection::FieldSelection;
use crate::operation::inline_fragment_selection::InlineFragmentSelection;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub(crate) enum SelectionKey<'a> {
Field {
response_name: &'a Name,
directives: &'a DirectiveList,
},
FragmentSpread {
fragment_name: &'a Name,
directives: &'a DirectiveList,
},
InlineFragment {
type_condition: Option<&'a Name>,
directives: &'a DirectiveList,
},
Defer {
deferred_id: SelectionId,
},
}
impl SelectionKey<'_> {
pub(crate) fn to_owned_key(self) -> OwnedSelectionKey {
match self {
Self::Field {
response_name,
directives,
} => OwnedSelectionKey::Field {
response_name: response_name.clone(),
directives: directives.clone(),
},
Self::FragmentSpread {
fragment_name,
directives,
} => OwnedSelectionKey::FragmentSpread {
fragment_name: fragment_name.clone(),
directives: directives.clone(),
},
Self::InlineFragment {
type_condition,
directives,
} => OwnedSelectionKey::InlineFragment {
type_condition: type_condition.cloned(),
directives: directives.clone(),
},
Self::Defer { deferred_id } => OwnedSelectionKey::Defer { deferred_id },
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(crate) enum OwnedSelectionKey {
Field {
response_name: Name,
directives: DirectiveList,
},
FragmentSpread {
fragment_name: Name,
directives: DirectiveList,
},
InlineFragment {
type_condition: Option<Name>,
directives: DirectiveList,
},
Defer {
deferred_id: SelectionId,
},
}
impl OwnedSelectionKey {
pub(crate) fn as_borrowed_key(&self) -> SelectionKey<'_> {
match self {
OwnedSelectionKey::Field {
response_name,
directives,
} => SelectionKey::Field {
response_name,
directives,
},
OwnedSelectionKey::FragmentSpread {
fragment_name,
directives,
} => SelectionKey::FragmentSpread {
fragment_name,
directives,
},
OwnedSelectionKey::InlineFragment {
type_condition,
directives,
} => SelectionKey::InlineFragment {
type_condition: type_condition.as_ref(),
directives,
},
OwnedSelectionKey::Defer { deferred_id } => SelectionKey::Defer {
deferred_id: *deferred_id,
},
}
}
}
#[cfg(test)]
impl<'a> SelectionKey<'a> {
pub(crate) fn field_name(name: &'a Name) -> Self {
static EMPTY_LIST: DirectiveList = DirectiveList::new();
SelectionKey::Field {
response_name: name,
directives: &EMPTY_LIST,
}
}
}
pub(crate) trait HasSelectionKey {
fn key(&self) -> SelectionKey<'_>;
}
#[derive(Clone)]
struct Bucket {
index: usize,
hash: u64,
}
#[derive(Clone)]
pub(crate) struct SelectionMap {
hash_builder: DefaultHashBuilder,
table: HashTable<Bucket>,
selections: Vec<Selection>,
}
impl std::fmt::Debug for SelectionMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_set().entries(self.values()).finish()
}
}
impl PartialEq for SelectionMap {
fn eq(&self, other: &Self) -> bool {
self.len() == other.len()
&& self
.values()
.all(|left| other.get(left.key()).is_some_and(|right| left == right))
}
}
impl Eq for SelectionMap {}
impl Hash for SelectionMap {
fn hash<H: Hasher>(&self, state: &mut H) {
self.values()
.sorted()
.for_each(|hash_key| hash_key.hash(state));
}
}
impl Serialize for SelectionMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for value in self.values() {
seq.serialize_element(value)?;
}
seq.end()
}
}
impl Default for SelectionMap {
fn default() -> Self {
Self::new()
}
}
pub(crate) type Values<'a> = std::slice::Iter<'a, Selection>;
pub(crate) type ValuesMut<'a> =
std::iter::Map<std::slice::IterMut<'a, Selection>, fn(&'a mut Selection) -> SelectionValue<'a>>;
pub(crate) type IntoValues = std::vec::IntoIter<Selection>;
fn key_eq(selections: &[Selection], key: SelectionKey<'_>) -> impl Fn(&Bucket) -> bool {
move |bucket| selections[bucket.index].key() == key
}
impl SelectionMap {
pub(crate) fn new() -> Self {
SelectionMap {
hash_builder: Default::default(),
table: HashTable::new(),
selections: Vec::new(),
}
}
pub(crate) fn len(&self) -> usize {
self.selections.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.selections.is_empty()
}
fn hash_key(&self, key: SelectionKey<'_>) -> u64 {
self.hash_builder.hash_one(key)
}
pub(crate) fn contains_key(&self, key: SelectionKey<'_>) -> bool {
let hash = self.hash_key(key);
self.table
.find(hash, key_eq(&self.selections, key))
.is_some()
}
pub(crate) fn get(&self, key: SelectionKey<'_>) -> Option<&Selection> {
let hash = self.hash_key(key);
let bucket = self.table.find(hash, key_eq(&self.selections, key))?;
Some(&self.selections[bucket.index])
}
pub(crate) fn get_mut(&mut self, key: SelectionKey<'_>) -> Option<SelectionValue<'_>> {
let hash = self.hash_key(key);
let bucket = self.table.find_mut(hash, key_eq(&self.selections, key))?;
Some(SelectionValue::new(&mut self.selections[bucket.index]))
}
fn raw_insert(&mut self, hash: u64, value: Selection) -> &mut Selection {
let index = self.selections.len();
self.table
.insert_unique(hash, Bucket { index, hash }, |existing| existing.hash);
self.selections.push(value);
&mut self.selections[index]
}
fn rebuild_table_no_grow(&mut self) {
assert!(self.table.capacity() >= self.selections.len());
self.table.clear();
for (index, selection) in self.selections.iter().enumerate() {
let hash = self.hash_key(selection.key());
self.table
.insert_unique(hash, Bucket { index, hash }, |existing| existing.hash);
}
}
fn decrement_table(&mut self, pivot: usize) {
for bucket in self.table.iter_mut() {
if bucket.index >= pivot {
bucket.index -= 1;
}
}
}
pub(crate) fn insert(&mut self, value: Selection) {
let hash = self.hash_key(value.key());
self.raw_insert(hash, value);
}
pub(crate) fn remove(&mut self, key: SelectionKey<'_>) -> Option<(usize, Selection)> {
let hash = self.hash_key(key);
let entry = self
.table
.find_entry(hash, key_eq(&self.selections, key))
.ok()?;
let (bucket, _) = entry.remove();
let selection = self.selections.remove(bucket.index);
self.decrement_table(bucket.index);
Some((bucket.index, selection))
}
pub(crate) fn retain(
&mut self,
mut predicate: impl FnMut(SelectionKey<'_>, &Selection) -> bool,
) {
self.selections.retain(|selection| {
let key = selection.key();
predicate(key, selection)
});
if self.selections.len() < self.table.len() {
self.rebuild_table_no_grow();
}
assert!(self.selections.len() == self.table.len());
}
pub(crate) fn values(&self) -> Values<'_> {
self.selections.iter()
}
pub(crate) fn values_mut(&mut self) -> ValuesMut<'_> {
self.selections.iter_mut().map(SelectionValue::new)
}
pub(crate) fn into_values(self) -> IntoValues {
self.selections.into_iter()
}
pub(super) fn entry<'a>(&'a mut self, key: SelectionKey<'a>) -> Entry<'a> {
let hash = self.hash_key(key);
let slot = self.table.find_entry(hash, key_eq(&self.selections, key));
match slot {
Ok(occupied) => {
let index = occupied.get().index;
let selection = &mut self.selections[index];
Entry::Occupied(OccupiedEntry(selection))
}
Err(_) => Entry::Vacant(VacantEntry {
map: self,
hash,
key,
}),
}
}
pub(crate) fn extend(&mut self, other: SelectionMap) {
for selection in other.into_values() {
self.insert(selection);
}
}
pub(crate) fn extend_ref(&mut self, other: &SelectionMap) {
for selection in other.values() {
self.insert(selection.clone());
}
}
pub(crate) fn filter_recursive_depth_first(
&self,
predicate: &mut dyn FnMut(&Selection) -> bool,
) -> Cow<'_, Self> {
fn recur_sub_selections<'sel>(
selection: &'sel Selection,
predicate: &mut dyn FnMut(&Selection) -> bool,
) -> Cow<'sel, Selection> {
match selection {
Selection::Field(field) => {
if let Some(sub_selections) = &field.selection_set {
match sub_selections.filter_recursive_depth_first(predicate) {
Cow::Borrowed(_) => Cow::Borrowed(selection),
Cow::Owned(new) => {
Cow::Owned(Selection::from_field(field.field.clone(), Some(new)))
}
}
} else {
Cow::Borrowed(selection)
}
}
Selection::InlineFragment(fragment) => match fragment
.selection_set
.filter_recursive_depth_first(predicate)
{
Cow::Borrowed(_) => Cow::Borrowed(selection),
Cow::Owned(selection_set) => Cow::Owned(Selection::InlineFragment(Arc::new(
InlineFragmentSelection::new(
fragment.inline_fragment.clone(),
selection_set,
),
))),
},
}
}
let mut iter = self.values();
let mut enumerated = (&mut iter).enumerate();
let mut new_map: Self;
loop {
let Some((index, selection)) = enumerated.next() else {
return Cow::Borrowed(self);
};
let filtered = recur_sub_selections(selection, predicate);
let keep = predicate(&filtered);
if keep && matches!(filtered, Cow::Borrowed(_)) {
continue;
}
new_map = self.selections[..index].iter().cloned().collect();
if keep {
new_map.insert(filtered.into_owned());
}
break;
}
for selection in iter {
let filtered = recur_sub_selections(selection, predicate);
if predicate(&filtered) {
new_map.insert(filtered.into_owned());
}
}
Cow::Owned(new_map)
}
}
impl<A> FromIterator<A> for SelectionMap
where
A: Into<Selection>,
{
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let mut map = Self::new();
for selection in iter {
map.insert(selection.into());
}
map
}
}
#[derive(Debug)]
pub(crate) enum SelectionValue<'a> {
Field(FieldSelectionValue<'a>),
InlineFragment(InlineFragmentSelectionValue<'a>),
}
impl<'a> SelectionValue<'a> {
fn new(selection: &'a mut Selection) -> Self {
match selection {
Selection::Field(field_selection) => {
SelectionValue::Field(FieldSelectionValue::new(field_selection))
}
Selection::InlineFragment(inline_fragment_selection) => SelectionValue::InlineFragment(
InlineFragmentSelectionValue::new(inline_fragment_selection),
),
}
}
pub(super) fn key(&self) -> SelectionKey<'_> {
match self {
Self::Field(field) => field.get().key(),
Self::InlineFragment(frag) => frag.get().key(),
}
}
#[cfg(test)]
pub(super) fn get_selection_set_mut(&mut self) -> Option<&mut SelectionSet> {
match self {
SelectionValue::Field(field) => field.get_selection_set_mut(),
SelectionValue::InlineFragment(frag) => Some(frag.get_selection_set_mut()),
}
}
}
#[derive(Debug)]
pub(crate) struct FieldSelectionValue<'a>(&'a mut Arc<FieldSelection>);
impl<'a> FieldSelectionValue<'a> {
pub(crate) fn new(field_selection: &'a mut Arc<FieldSelection>) -> Self {
Self(field_selection)
}
pub(crate) fn get(&self) -> &Arc<FieldSelection> {
self.0
}
pub(crate) fn get_sibling_typename_mut(&mut self) -> &mut Option<SiblingTypename> {
Arc::make_mut(self.0).field.sibling_typename_mut()
}
pub(crate) fn get_selection_set_mut(&mut self) -> Option<&mut SelectionSet> {
Arc::make_mut(self.0).selection_set.as_mut()
}
}
#[derive(Debug)]
pub(crate) struct InlineFragmentSelectionValue<'a>(&'a mut Arc<InlineFragmentSelection>);
impl<'a> InlineFragmentSelectionValue<'a> {
pub(crate) fn new(inline_fragment_selection: &'a mut Arc<InlineFragmentSelection>) -> Self {
Self(inline_fragment_selection)
}
pub(crate) fn get(&self) -> &Arc<InlineFragmentSelection> {
self.0
}
pub(crate) fn get_selection_set_mut(&mut self) -> &mut SelectionSet {
&mut Arc::make_mut(self.0).selection_set
}
}
pub(crate) enum Entry<'a> {
Occupied(OccupiedEntry<'a>),
Vacant(VacantEntry<'a>),
}
impl<'a> Entry<'a> {
pub(crate) fn or_insert(
self,
produce: impl FnOnce() -> Result<Selection, FederationError>,
) -> Result<SelectionValue<'a>, FederationError> {
match self {
Self::Occupied(entry) => Ok(entry.into_mut()),
Self::Vacant(entry) => entry.insert(produce()?),
}
}
}
pub(crate) struct OccupiedEntry<'a>(&'a mut Selection);
impl<'a> OccupiedEntry<'a> {
pub(crate) fn get(&self) -> &Selection {
self.0
}
pub(crate) fn into_mut(self) -> SelectionValue<'a> {
SelectionValue::new(self.0)
}
}
pub(crate) struct VacantEntry<'a> {
map: &'a mut SelectionMap,
hash: u64,
key: SelectionKey<'a>,
}
impl<'a> VacantEntry<'a> {
pub(crate) fn key(&self) -> SelectionKey<'a> {
self.key
}
pub(crate) fn insert(self, value: Selection) -> Result<SelectionValue<'a>, FederationError> {
if self.key() != value.key() {
return Err(FederationError::internal(format!(
"Key mismatch when inserting selection {value} into vacant entry "
)));
};
Ok(SelectionValue::new(self.map.raw_insert(self.hash, value)))
}
}