use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result}; use fnv::FnvHasher; use graphql_parser::query::{Field, SelectionSet, Value}; use std::any::{Any, TypeId}; use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::ops::{Deref, DerefMut}; #[derive(Default)] pub struct Variables(HashMap); impl Deref for Variables { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Variables { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[derive(Default)] pub struct Data(HashMap, BuildHasherDefault>); impl Data { pub fn insert(&mut self, data: D) { self.0.insert(TypeId::of::(), Box::new(data)); } pub fn remove(&mut self) { self.0.remove(&TypeId::of::()); } } pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>; pub type Context<'a> = ContextBase<'a, &'a Field>; pub struct ContextBase<'a, T> { pub(crate) item: T, pub(crate) data: Option<&'a Data>, pub(crate) variables: Option<&'a Variables>, } impl<'a, T> Deref for ContextBase<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { &self.item } } impl<'a, T> ContextBase<'a, T> { #[doc(hidden)] pub fn with_item(&self, item: R) -> ContextBase<'a, R> { ContextBase { item, data: self.data, variables: self.variables, } } pub fn data(&self) -> Option<&D> { self.data.and_then(|data| { data.0 .get(&TypeId::of::()) .and_then(|d| d.downcast_ref::()) }) } } impl<'a> ContextBase<'a, &'a Field> { #[doc(hidden)] pub fn param_value(&self, name: &str) -> Result { let value = self .arguments .iter() .find(|(n, _)| n == name) .map(|(_, v)| v) .cloned(); if let Some(Value::Variable(var_name)) = &value { if let Some(vars) = &self.variables { if let Some(var_value) = vars.get(&*var_name).cloned() { let res = GQLInputValue::parse_from_json(var_value) .map_err(|err| err.with_position(self.item.position))?; return Ok(res); } } return Err(QueryError::VarNotDefined { var_name: var_name.clone(), } .into()); }; let res = GQLInputValue::parse(value.unwrap_or(Value::Null)) .map_err(|err| err.with_position(self.item.position))?; Ok(res) } }