2020-03-03 11:15:18 +00:00
|
|
|
use crate::registry::Registry;
|
2020-03-04 06:24:44 +00:00
|
|
|
use crate::{ErrorWithPosition, GQLInputValue, GQLType, QueryError, Result};
|
2020-03-01 10:54:34 +00:00
|
|
|
use fnv::FnvHasher;
|
2020-03-05 07:50:57 +00:00
|
|
|
use graphql_parser::query::{
|
2020-03-06 16:19:22 +00:00
|
|
|
Directive, Field, FragmentDefinition, SelectionSet, Value, VariableDefinition,
|
2020-03-05 07:50:57 +00:00
|
|
|
};
|
2020-03-01 10:54:34 +00:00
|
|
|
use std::any::{Any, TypeId};
|
2020-03-04 03:51:42 +00:00
|
|
|
use std::collections::{BTreeMap, HashMap};
|
2020-03-01 10:54:34 +00:00
|
|
|
use std::hash::BuildHasherDefault;
|
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
|
2020-03-09 10:05:52 +00:00
|
|
|
/// Variables of query
|
2020-03-14 03:46:20 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Variables(Value);
|
|
|
|
|
|
|
|
impl Default for Variables {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(Value::Object(Default::default()))
|
|
|
|
}
|
|
|
|
}
|
2020-03-01 10:54:34 +00:00
|
|
|
|
|
|
|
impl Deref for Variables {
|
2020-03-04 03:51:42 +00:00
|
|
|
type Target = BTreeMap<String, Value>;
|
2020-03-01 10:54:34 +00:00
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
2020-03-14 03:46:20 +00:00
|
|
|
if let Value::Object(obj) = &self.0 {
|
|
|
|
obj
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
}
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 00:39:56 +00:00
|
|
|
impl DerefMut for Variables {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
2020-03-14 03:46:20 +00:00
|
|
|
if let Value::Object(obj) = &mut self.0 {
|
|
|
|
obj
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
}
|
2020-03-05 00:39:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 03:51:42 +00:00
|
|
|
impl Variables {
|
2020-03-05 00:39:56 +00:00
|
|
|
pub(crate) fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
2020-03-04 03:51:42 +00:00
|
|
|
let gql_value = json_value_to_gql_value(value);
|
2020-03-14 03:46:20 +00:00
|
|
|
if let Value::Object(_) = gql_value {
|
|
|
|
Ok(Variables(gql_value))
|
2020-03-04 03:51:42 +00:00
|
|
|
} else {
|
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
}
|
2020-03-14 03:46:20 +00:00
|
|
|
|
|
|
|
pub(crate) fn set_upload(
|
|
|
|
&mut self,
|
|
|
|
var_path: &str,
|
|
|
|
filename: &str,
|
|
|
|
content_type: Option<&str>,
|
|
|
|
content: Vec<u8>,
|
|
|
|
) {
|
|
|
|
let mut it = var_path.split(".").peekable();
|
|
|
|
|
|
|
|
if let Some(first) = it.next() {
|
|
|
|
if first != "variables" {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut current = &mut self.0;
|
|
|
|
while let Some(s) = it.next() {
|
|
|
|
let has_next = it.peek().is_some();
|
|
|
|
|
|
|
|
if let Ok(idx) = s.parse::<i32>() {
|
|
|
|
if let Value::List(ls) = current {
|
|
|
|
if let Some(value) = ls.get_mut(idx as usize) {
|
|
|
|
if !has_next {
|
|
|
|
*value = Value::String(file_string(filename, content_type, &content));
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
current = value;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if let Value::Object(obj) = current {
|
|
|
|
if let Some(value) = obj.get_mut(s) {
|
|
|
|
if !has_next {
|
|
|
|
*value = Value::String(file_string(filename, content_type, &content));
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
current = value;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_string(filename: &str, content_type: Option<&str>, content: &[u8]) -> String {
|
|
|
|
if let Some(content_type) = content_type {
|
|
|
|
format!("file:{}:{}|", filename, content_type)
|
|
|
|
+ unsafe { std::str::from_utf8_unchecked(content) }
|
|
|
|
} else {
|
|
|
|
format!("file:{}|", filename) + unsafe { std::str::from_utf8_unchecked(content) }
|
|
|
|
}
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn json_value_to_gql_value(value: serde_json::Value) -> Value {
|
|
|
|
match value {
|
|
|
|
serde_json::Value::Null => Value::Null,
|
|
|
|
serde_json::Value::Bool(n) => Value::Boolean(n),
|
|
|
|
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
|
|
|
|
serde_json::Value::Number(n) => Value::Int((n.as_i64().unwrap() as i32).into()),
|
|
|
|
serde_json::Value::String(s) => Value::String(s),
|
|
|
|
serde_json::Value::Array(ls) => Value::List(
|
|
|
|
ls.into_iter()
|
|
|
|
.map(|value| json_value_to_gql_value(value))
|
|
|
|
.collect(),
|
|
|
|
),
|
|
|
|
serde_json::Value::Object(obj) => Value::Object(
|
|
|
|
obj.into_iter()
|
|
|
|
.map(|(name, value)| (name, json_value_to_gql_value(value)))
|
|
|
|
.collect(),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-01 10:54:34 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Data(HashMap<TypeId, Box<dyn Any + Sync + Send>, BuildHasherDefault<FnvHasher>>);
|
|
|
|
|
|
|
|
impl Data {
|
|
|
|
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
|
|
|
|
self.0.insert(TypeId::of::<D>(), Box::new(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 03:48:00 +00:00
|
|
|
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>;
|
2020-03-09 10:05:52 +00:00
|
|
|
|
|
|
|
/// Context object for resolve field.
|
2020-03-03 03:48:00 +00:00
|
|
|
pub type Context<'a> = ContextBase<'a, &'a Field>;
|
2020-03-01 10:54:34 +00:00
|
|
|
|
2020-03-03 03:48:00 +00:00
|
|
|
pub struct ContextBase<'a, T> {
|
2020-03-01 10:54:34 +00:00
|
|
|
pub(crate) item: T,
|
2020-03-14 03:46:20 +00:00
|
|
|
pub(crate) variables: &'a Variables,
|
2020-03-04 03:51:42 +00:00
|
|
|
pub(crate) variable_definitions: Option<&'a [VariableDefinition]>,
|
2020-03-03 11:15:18 +00:00
|
|
|
pub(crate) registry: &'a Registry,
|
2020-03-05 00:39:56 +00:00
|
|
|
pub(crate) data: &'a Data,
|
2020-03-14 03:46:20 +00:00
|
|
|
pub(crate) fragments: &'a HashMap<String, FragmentDefinition>,
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 03:48:00 +00:00
|
|
|
impl<'a, T> Deref for ContextBase<'a, T> {
|
2020-03-01 10:54:34 +00:00
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.item
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 03:48:00 +00:00
|
|
|
impl<'a, T> ContextBase<'a, T> {
|
2020-03-01 17:15:05 +00:00
|
|
|
#[doc(hidden)]
|
2020-03-03 03:48:00 +00:00
|
|
|
pub fn with_item<R>(&self, item: R) -> ContextBase<'a, R> {
|
|
|
|
ContextBase {
|
2020-03-01 10:54:34 +00:00
|
|
|
item,
|
|
|
|
variables: self.variables,
|
2020-03-04 03:51:42 +00:00
|
|
|
variable_definitions: self.variable_definitions,
|
2020-03-03 11:15:18 +00:00
|
|
|
registry: self.registry.clone(),
|
2020-03-05 00:39:56 +00:00
|
|
|
data: self.data,
|
2020-03-05 07:50:57 +00:00
|
|
|
fragments: self.fragments,
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 10:05:52 +00:00
|
|
|
/// Gets the global data defined in the `Schema`.
|
2020-03-07 10:03:19 +00:00
|
|
|
pub fn data<D: Any + Send + Sync>(&self) -> &D {
|
2020-03-05 00:39:56 +00:00
|
|
|
self.data
|
|
|
|
.0
|
|
|
|
.get(&TypeId::of::<D>())
|
|
|
|
.and_then(|d| d.downcast_ref::<D>())
|
2020-03-07 10:03:19 +00:00
|
|
|
.expect("The specified data type does not exist.")
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
2020-03-05 07:50:57 +00:00
|
|
|
|
2020-03-10 06:14:09 +00:00
|
|
|
fn var_value(&self, name: &str) -> Result<Value> {
|
|
|
|
let def = self
|
|
|
|
.variable_definitions
|
|
|
|
.and_then(|defs| defs.iter().find(|def| def.name == name));
|
|
|
|
if let Some(def) = def {
|
2020-03-14 03:46:20 +00:00
|
|
|
if let Some(var_value) = self.variables.get(&def.name) {
|
2020-03-10 06:14:09 +00:00
|
|
|
return Ok(var_value.clone());
|
|
|
|
} else if let Some(default) = &def.default_value {
|
|
|
|
return Ok(default.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Err(QueryError::VarNotDefined {
|
|
|
|
var_name: name.to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_input_value(&self, mut value: Value) -> Result<Value> {
|
|
|
|
match value {
|
|
|
|
Value::Variable(var_name) => self.var_value(&var_name),
|
|
|
|
Value::List(ref mut ls) => {
|
|
|
|
for value in ls {
|
|
|
|
if let Value::Variable(var_name) = value {
|
|
|
|
*value = self.var_value(&var_name)?;
|
|
|
|
}
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
2020-03-10 06:14:09 +00:00
|
|
|
Ok(value)
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
2020-03-10 06:14:09 +00:00
|
|
|
Value::Object(ref mut obj) => {
|
|
|
|
for (_, value) in obj {
|
|
|
|
if let Value::Variable(var_name) = value {
|
|
|
|
*value = self.var_value(&var_name)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(value)
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
2020-03-10 06:14:09 +00:00
|
|
|
_ => Ok(value),
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 06:24:44 +00:00
|
|
|
#[doc(hidden)]
|
2020-03-05 09:06:14 +00:00
|
|
|
pub fn is_skip(&self, directives: &[Directive]) -> Result<bool> {
|
|
|
|
for directive in directives {
|
2020-03-04 06:24:44 +00:00
|
|
|
if directive.name == "skip" {
|
|
|
|
if let Some(value) = directive
|
|
|
|
.arguments
|
|
|
|
.iter()
|
|
|
|
.find(|(name, _)| name == "if")
|
|
|
|
.map(|(_, value)| value)
|
|
|
|
{
|
|
|
|
let value = self.resolve_input_value(value.clone())?;
|
|
|
|
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
|
|
|
|
QueryError::ExpectedType {
|
|
|
|
expect: bool::qualified_type_name(),
|
|
|
|
actual: value,
|
|
|
|
}
|
|
|
|
.with_position(directive.position)
|
|
|
|
})?;
|
|
|
|
if res {
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(QueryError::RequiredDirectiveArgs {
|
|
|
|
directive: "@skip",
|
|
|
|
arg_name: "if",
|
|
|
|
arg_type: "Boolean!",
|
|
|
|
}
|
|
|
|
.with_position(directive.position)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
} else if directive.name == "include" {
|
|
|
|
if let Some(value) = directive
|
|
|
|
.arguments
|
|
|
|
.iter()
|
|
|
|
.find(|(name, _)| name == "if")
|
|
|
|
.map(|(_, value)| value)
|
|
|
|
{
|
|
|
|
let value = self.resolve_input_value(value.clone())?;
|
|
|
|
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
|
|
|
|
QueryError::ExpectedType {
|
|
|
|
expect: bool::qualified_type_name(),
|
|
|
|
actual: value,
|
|
|
|
}
|
|
|
|
.with_position(directive.position)
|
|
|
|
})?;
|
|
|
|
if !res {
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(QueryError::RequiredDirectiveArgs {
|
|
|
|
directive: "@include",
|
|
|
|
arg_name: "if",
|
|
|
|
arg_type: "Boolean!",
|
|
|
|
}
|
|
|
|
.with_position(directive.position)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(QueryError::UnknownDirective {
|
|
|
|
name: directive.name.clone(),
|
|
|
|
}
|
|
|
|
.with_position(directive.position)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(false)
|
|
|
|
}
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
2020-03-05 09:06:14 +00:00
|
|
|
|
|
|
|
impl<'a> ContextBase<'a, &'a Field> {
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn param_value<T: GQLInputValue, F: FnOnce() -> Value>(
|
|
|
|
&self,
|
|
|
|
name: &str,
|
2020-03-05 13:34:31 +00:00
|
|
|
default: F,
|
2020-03-05 09:06:14 +00:00
|
|
|
) -> Result<T> {
|
|
|
|
match self
|
|
|
|
.arguments
|
|
|
|
.iter()
|
|
|
|
.find(|(n, _)| n == name)
|
|
|
|
.map(|(_, v)| v)
|
|
|
|
.cloned()
|
|
|
|
{
|
|
|
|
Some(value) => {
|
|
|
|
let value = self.resolve_input_value(value)?;
|
|
|
|
let res = GQLInputValue::parse(&value).ok_or_else(|| {
|
|
|
|
QueryError::ExpectedType {
|
|
|
|
expect: T::qualified_type_name(),
|
|
|
|
actual: value,
|
|
|
|
}
|
|
|
|
.with_position(self.item.position)
|
|
|
|
})?;
|
|
|
|
Ok(res)
|
|
|
|
}
|
2020-03-05 13:34:31 +00:00
|
|
|
None => {
|
2020-03-05 09:06:14 +00:00
|
|
|
let value = default();
|
|
|
|
let res = GQLInputValue::parse(&value).ok_or_else(|| {
|
|
|
|
QueryError::ExpectedType {
|
|
|
|
expect: T::qualified_type_name(),
|
|
|
|
actual: value.clone(),
|
|
|
|
}
|
|
|
|
.with_position(self.item.position)
|
|
|
|
})?;
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-06 15:58:43 +00:00
|
|
|
|
2020-03-07 03:27:44 +00:00
|
|
|
#[doc(hidden)]
|
2020-03-06 15:58:43 +00:00
|
|
|
pub fn result_name(&self) -> String {
|
|
|
|
self.item.alias.clone().unwrap_or_else(|| self.name.clone())
|
|
|
|
}
|
2020-03-05 09:06:14 +00:00
|
|
|
}
|