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(Serialize, Deserialize, 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> = Context<'a, &'a SelectionSet>; pub type ContextField<'a> = Context<'a, &'a Field>; pub struct Context<'a, T> { pub(crate) item: T, pub(crate) data: Option<&'a Data>, pub(crate) variables: Option<&'a Variables>, } impl<'a, T> Deref for Context<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { &self.item } } impl<'a, T> Context<'a, T> { pub fn with_item(&self, item: R) -> Context<'a, R> { Context { 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> Context<'a, &'a Field> { pub fn param_value(&self, name: &str) -> Result { let value = self .arguments .iter() .find(|(n, _)| n == name) .map(|(_, v)| v) .cloned() .unwrap_or(Value::Null); let value = match (value, &self.variables) { (Value::Variable(name), Some(vars)) => match vars.get(&name).cloned() { Some(value) => value, None => { return Err(QueryError::VarNotDefined { var_name: name.clone(), } .into()); } }, (Value::Variable(name), None) => { return Err(QueryError::VarNotDefined { var_name: name.clone(), } .into()); } (value, _) => value, }; let res = GQLInputValue::parse(value).map_err(|err| err.with_position(self.item.position))?; Ok(res) } }