2020-09-06 06:16:36 +00:00
|
|
|
use crate::base::Type;
|
2020-05-22 03:58:49 +00:00
|
|
|
use crate::extensions::Extensions;
|
2020-09-08 08:30:29 +00:00
|
|
|
use crate::parser::types::{
|
2020-09-22 18:59:48 +00:00
|
|
|
Directive, Field, FragmentDefinition, Name, OperationDefinition, SelectionSet,
|
|
|
|
Value as InputValue,
|
2020-05-19 08:24:15 +00:00
|
|
|
};
|
2020-09-06 05:38:31 +00:00
|
|
|
use crate::schema::SchemaEnv;
|
2020-09-08 08:21:27 +00:00
|
|
|
use crate::{FieldResult, InputValueType, Lookahead, Pos, Positioned, QueryError, Result, Value};
|
2020-04-24 02:05:41 +00:00
|
|
|
use fnv::FnvHashMap;
|
2020-09-12 16:07:46 +00:00
|
|
|
use serde::ser::{SerializeSeq, Serializer};
|
|
|
|
use serde::{Deserialize, Serialize};
|
2020-03-01 10:54:34 +00:00
|
|
|
use std::any::{Any, TypeId};
|
2020-09-22 18:59:48 +00:00
|
|
|
use std::collections::{BTreeMap, HashMap};
|
2020-09-06 05:38:31 +00:00
|
|
|
use std::convert::TryFrom;
|
2020-09-12 16:07:46 +00:00
|
|
|
use std::fmt::{self, Debug, Display, Formatter};
|
2020-09-06 05:38:31 +00:00
|
|
|
use std::ops::Deref;
|
2020-03-26 03:34:28 +00:00
|
|
|
use std::sync::atomic::AtomicUsize;
|
2020-04-23 02:26:16 +00:00
|
|
|
use std::sync::Arc;
|
2020-03-01 10:54:34 +00:00
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
/// Variables of a query.
|
2020-09-12 16:07:46 +00:00
|
|
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
|
|
|
#[serde(transparent)]
|
2020-09-08 08:21:27 +00:00
|
|
|
pub struct Variables(pub BTreeMap<Name, Value>);
|
2020-03-01 10:54:34 +00:00
|
|
|
|
2020-06-13 14:14:47 +00:00
|
|
|
impl Display for Variables {
|
2020-09-06 05:38:31 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
f.write_str("{")?;
|
|
|
|
for (i, (name, value)) in self.0.iter().enumerate() {
|
|
|
|
write!(f, "{}{}: {}", if i == 0 { "" } else { ", " }, name, value)?;
|
2020-03-14 03:46:20 +00:00
|
|
|
}
|
2020-09-06 05:38:31 +00:00
|
|
|
f.write_str("}")
|
2020-03-05 00:39:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 03:51:42 +00:00
|
|
|
impl Variables {
|
2020-09-12 16:07:46 +00:00
|
|
|
/// Get the variables from a GraphQL value.
|
2020-09-08 08:21:27 +00:00
|
|
|
///
|
2020-09-12 16:07:46 +00:00
|
|
|
/// If the value is not a map, then no variables will be returned.
|
|
|
|
#[must_use]
|
|
|
|
pub fn from_value(value: Value) -> Self {
|
|
|
|
match value {
|
|
|
|
Value::Object(obj) => Self(obj),
|
|
|
|
_ => Self::default(),
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-14 03:46:20 +00:00
|
|
|
|
2020-09-12 16:07:46 +00:00
|
|
|
/// Get the values from a JSON value.
|
|
|
|
///
|
|
|
|
/// If the value is not a map or the keys of a map are not valid GraphQL names, then no
|
|
|
|
/// variables will be returned.
|
|
|
|
#[must_use]
|
|
|
|
pub fn from_json(value: serde_json::Value) -> Self {
|
|
|
|
Value::from_json(value)
|
|
|
|
.map(Self::from_value)
|
|
|
|
.unwrap_or_default()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the variables as a GraphQL value.
|
|
|
|
#[must_use]
|
|
|
|
pub fn into_value(self) -> Value {
|
|
|
|
Value::Object(self.0)
|
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
pub(crate) fn variable_path(&mut self, path: &str) -> Option<&mut Value> {
|
|
|
|
let mut parts = path.strip_prefix("variables.")?.split('.');
|
2020-03-14 03:46:20 +00:00
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
let initial = self.0.get_mut(parts.next().unwrap())?;
|
|
|
|
|
|
|
|
parts.try_fold(initial, |current, part| match current {
|
|
|
|
Value::List(list) => part
|
|
|
|
.parse::<u32>()
|
|
|
|
.ok()
|
|
|
|
.and_then(|idx| usize::try_from(idx).ok())
|
|
|
|
.and_then(move |idx| list.get_mut(idx)),
|
|
|
|
Value::Object(obj) => obj.get_mut(part),
|
|
|
|
_ => None,
|
|
|
|
})
|
2020-03-14 03:46:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 16:07:46 +00:00
|
|
|
impl From<Variables> for Value {
|
|
|
|
fn from(variables: Variables) -> Self {
|
|
|
|
variables.into_value()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
/// Schema/Context data.
|
2020-09-12 16:07:46 +00:00
|
|
|
///
|
|
|
|
/// This is a type map, allowing you to store anything inside it.
|
2020-03-01 10:54:34 +00:00
|
|
|
#[derive(Default)]
|
2020-04-23 13:36:04 +00:00
|
|
|
pub struct Data(FnvHashMap<TypeId, Box<dyn Any + Sync + Send>>);
|
2020-03-01 10:54:34 +00:00
|
|
|
|
|
|
|
impl Data {
|
2020-09-12 16:07:46 +00:00
|
|
|
/// Insert data.
|
2020-03-01 10:54:34 +00:00
|
|
|
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
|
|
|
|
self.0.insert(TypeId::of::<D>(), Box::new(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 16:07:46 +00:00
|
|
|
impl Debug for Data {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
f.debug_tuple("Data").finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 03:56:08 +00:00
|
|
|
/// Context for `SelectionSet`
|
2020-05-10 02:59:51 +00:00
|
|
|
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a Positioned<SelectionSet>>;
|
2020-03-09 10:05:52 +00:00
|
|
|
|
2020-03-20 03:56:08 +00:00
|
|
|
/// Context object for resolve field
|
2020-05-10 02:59:51 +00:00
|
|
|
pub type Context<'a> = ContextBase<'a, &'a Positioned<Field>>;
|
2020-03-01 10:54:34 +00:00
|
|
|
|
2020-03-26 03:34:28 +00:00
|
|
|
/// The query path segment
|
|
|
|
#[derive(Clone)]
|
2020-03-26 10:30:29 +00:00
|
|
|
pub enum QueryPathSegment<'a> {
|
2020-03-26 03:34:28 +00:00
|
|
|
/// Index
|
|
|
|
Index(usize),
|
|
|
|
|
|
|
|
/// Field name
|
2020-03-26 10:30:29 +00:00
|
|
|
Name(&'a str),
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
|
2020-03-26 10:30:29 +00:00
|
|
|
/// The query path node
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct QueryPathNode<'a> {
|
|
|
|
/// Parent node
|
|
|
|
pub parent: Option<&'a QueryPathNode<'a>>,
|
2020-03-26 03:34:28 +00:00
|
|
|
|
2020-03-26 10:30:29 +00:00
|
|
|
/// Current path segment
|
|
|
|
pub segment: QueryPathSegment<'a>,
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-15 10:05:24 +00:00
|
|
|
impl<'a> serde::Serialize for QueryPathNode<'a> {
|
|
|
|
fn serialize<S>(
|
|
|
|
&self,
|
|
|
|
serializer: S,
|
|
|
|
) -> std::result::Result<<S as Serializer>::Ok, <S as Serializer>::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
let mut seq = serializer.serialize_seq(None)?;
|
|
|
|
self.for_each(|segment| match segment {
|
|
|
|
QueryPathSegment::Index(idx) => {
|
|
|
|
seq.serialize_element(&idx).ok();
|
|
|
|
}
|
|
|
|
QueryPathSegment::Name(name) => {
|
|
|
|
seq.serialize_element(name).ok();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
seq.end()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
impl<'a> Display for QueryPathNode<'a> {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
2020-03-30 02:45:41 +00:00
|
|
|
let mut first = true;
|
|
|
|
self.for_each(|segment| {
|
|
|
|
if !first {
|
|
|
|
write!(f, ".").ok();
|
|
|
|
}
|
|
|
|
match segment {
|
|
|
|
QueryPathSegment::Index(idx) => {
|
|
|
|
write!(f, "{}", *idx).ok();
|
|
|
|
}
|
|
|
|
QueryPathSegment::Name(name) => {
|
|
|
|
write!(f, "{}", name).ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
first = false;
|
|
|
|
});
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 10:30:29 +00:00
|
|
|
impl<'a> QueryPathNode<'a> {
|
2020-09-09 10:42:10 +00:00
|
|
|
/// Get the current field name.
|
|
|
|
pub fn field_name(&self) -> &str {
|
2020-03-26 10:30:29 +00:00
|
|
|
let mut p = self;
|
|
|
|
loop {
|
|
|
|
if let QueryPathSegment::Name(name) = &p.segment {
|
|
|
|
return name;
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
2020-03-26 10:30:29 +00:00
|
|
|
p = p.parent.unwrap();
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 10:30:29 +00:00
|
|
|
pub(crate) fn for_each<F: FnMut(&QueryPathSegment<'a>)>(&self, mut f: F) {
|
|
|
|
self.for_each_ref(&mut f);
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
|
2020-03-26 10:30:29 +00:00
|
|
|
fn for_each_ref<F: FnMut(&QueryPathSegment<'a>)>(&self, f: &mut F) {
|
|
|
|
if let Some(parent) = &self.parent {
|
|
|
|
parent.for_each_ref(f);
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
2020-03-26 10:30:29 +00:00
|
|
|
f(&self.segment);
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 07:01:19 +00:00
|
|
|
/// Represents the unique id of the resolve
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
pub struct ResolveId {
|
|
|
|
/// Parent id
|
|
|
|
pub parent: Option<usize>,
|
|
|
|
|
|
|
|
/// Current id
|
|
|
|
pub current: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ResolveId {
|
|
|
|
pub(crate) fn root() -> ResolveId {
|
|
|
|
ResolveId {
|
|
|
|
parent: None,
|
|
|
|
current: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
impl Display for ResolveId {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
2020-04-28 07:01:19 +00:00
|
|
|
if let Some(parent) = self.parent {
|
|
|
|
write!(f, "{}:{}", parent, self.current)
|
|
|
|
} else {
|
|
|
|
write!(f, "{}", self.current)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 03:56:08 +00:00
|
|
|
/// Query context
|
2020-03-25 03:39:28 +00:00
|
|
|
#[derive(Clone)]
|
2020-03-03 03:48:00 +00:00
|
|
|
pub struct ContextBase<'a, T> {
|
2020-04-02 02:21:04 +00:00
|
|
|
#[allow(missing_docs)]
|
|
|
|
pub path_node: Option<QueryPathNode<'a>>,
|
2020-04-28 07:01:19 +00:00
|
|
|
pub(crate) resolve_id: ResolveId,
|
|
|
|
pub(crate) inc_resolve_id: &'a AtomicUsize,
|
2020-05-20 00:18:28 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub item: T,
|
2020-09-13 17:52:36 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub schema_env: &'a SchemaEnv,
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub query_env: &'a QueryEnv,
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 05:49:39 +00:00
|
|
|
#[doc(hidden)]
|
2020-05-19 08:24:15 +00:00
|
|
|
pub struct QueryEnvInner {
|
2020-06-13 14:14:47 +00:00
|
|
|
pub extensions: spin::Mutex<Extensions>,
|
2020-04-06 05:49:39 +00:00
|
|
|
pub variables: Variables,
|
2020-09-22 18:59:48 +00:00
|
|
|
pub operation: Positioned<OperationDefinition>,
|
|
|
|
pub fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
2020-04-23 02:26:16 +00:00
|
|
|
pub ctx_data: Arc<Data>,
|
2020-04-06 05:49:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 08:24:15 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct QueryEnv(Arc<QueryEnvInner>);
|
|
|
|
|
|
|
|
impl Deref for QueryEnv {
|
|
|
|
type Target = QueryEnvInner;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl QueryEnv {
|
|
|
|
#[doc(hidden)]
|
2020-05-22 03:58:49 +00:00
|
|
|
pub fn new(
|
2020-06-13 14:14:47 +00:00
|
|
|
extensions: spin::Mutex<Extensions>,
|
2020-05-22 03:58:49 +00:00
|
|
|
variables: Variables,
|
2020-09-22 18:59:48 +00:00
|
|
|
operation: Positioned<OperationDefinition>,
|
|
|
|
fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
2020-05-22 03:58:49 +00:00
|
|
|
ctx_data: Arc<Data>,
|
|
|
|
) -> QueryEnv {
|
2020-05-19 08:24:15 +00:00
|
|
|
QueryEnv(Arc::new(QueryEnvInner {
|
2020-05-22 03:58:49 +00:00
|
|
|
extensions,
|
2020-05-19 08:24:15 +00:00
|
|
|
variables,
|
2020-09-22 18:59:48 +00:00
|
|
|
operation,
|
|
|
|
fragments,
|
2020-05-19 08:24:15 +00:00
|
|
|
ctx_data,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2020-04-06 05:49:39 +00:00
|
|
|
#[doc(hidden)]
|
2020-05-19 08:24:15 +00:00
|
|
|
pub fn create_context<'a, T>(
|
2020-04-06 05:49:39 +00:00
|
|
|
&'a self,
|
2020-05-19 08:24:15 +00:00
|
|
|
schema_env: &'a SchemaEnv,
|
2020-04-06 05:49:39 +00:00
|
|
|
path_node: Option<QueryPathNode<'a>>,
|
2020-04-23 02:26:16 +00:00
|
|
|
item: T,
|
2020-04-28 07:01:19 +00:00
|
|
|
inc_resolve_id: &'a AtomicUsize,
|
2020-04-06 05:49:39 +00:00
|
|
|
) -> ContextBase<'a, T> {
|
|
|
|
ContextBase {
|
|
|
|
path_node,
|
2020-04-28 07:01:19 +00:00
|
|
|
resolve_id: ResolveId::root(),
|
|
|
|
inc_resolve_id,
|
2020-04-06 05:49:39 +00:00
|
|
|
item,
|
2020-05-19 08:24:15 +00:00
|
|
|
schema_env,
|
|
|
|
query_env: self,
|
2020-04-06 05:49:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 03:48:00 +00:00
|
|
|
impl<'a, T> ContextBase<'a, T> {
|
2020-04-28 07:01:19 +00:00
|
|
|
fn get_child_resolve_id(&self) -> ResolveId {
|
|
|
|
let id = self
|
|
|
|
.inc_resolve_id
|
2020-03-26 03:34:28 +00:00
|
|
|
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
2020-04-28 07:01:19 +00:00
|
|
|
+ 1;
|
|
|
|
ResolveId {
|
|
|
|
parent: Some(self.resolve_id.current),
|
|
|
|
current: id,
|
|
|
|
}
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
2020-05-10 02:59:51 +00:00
|
|
|
pub fn with_field(
|
|
|
|
&'a self,
|
|
|
|
field: &'a Positioned<Field>,
|
|
|
|
) -> ContextBase<'a, &'a Positioned<Field>> {
|
2020-03-26 03:34:28 +00:00
|
|
|
ContextBase {
|
2020-03-26 10:30:29 +00:00
|
|
|
path_node: Some(QueryPathNode {
|
|
|
|
parent: self.path_node.as_ref(),
|
2020-09-06 05:38:31 +00:00
|
|
|
segment: QueryPathSegment::Name(&field.node.response_key().node),
|
2020-03-26 10:30:29 +00:00
|
|
|
}),
|
2020-03-26 03:34:28 +00:00
|
|
|
item: field,
|
2020-04-28 07:01:19 +00:00
|
|
|
resolve_id: self.get_child_resolve_id(),
|
|
|
|
inc_resolve_id: self.inc_resolve_id,
|
2020-05-19 08:24:15 +00:00
|
|
|
schema_env: self.schema_env,
|
|
|
|
query_env: self.query_env,
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn with_selection_set(
|
|
|
|
&self,
|
2020-05-10 02:59:51 +00:00
|
|
|
selection_set: &'a Positioned<SelectionSet>,
|
|
|
|
) -> ContextBase<'a, &'a Positioned<SelectionSet>> {
|
2020-03-03 03:48:00 +00:00
|
|
|
ContextBase {
|
2020-03-26 10:30:29 +00:00
|
|
|
path_node: self.path_node.clone(),
|
2020-03-26 03:34:28 +00:00
|
|
|
item: selection_set,
|
|
|
|
resolve_id: self.resolve_id,
|
2020-04-28 07:01:19 +00:00
|
|
|
inc_resolve_id: &self.inc_resolve_id,
|
2020-05-19 08:24:15 +00:00
|
|
|
schema_env: self.schema_env,
|
|
|
|
query_env: self.query_env,
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 03:19:18 +00:00
|
|
|
/// Gets the global data defined in the `Context` or `Schema`.
|
2020-05-22 06:02:28 +00:00
|
|
|
///
|
|
|
|
/// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained.
|
|
|
|
///
|
2020-09-06 05:38:31 +00:00
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// Returns a `FieldError` if the specified type data does not exist.
|
2020-07-06 23:54:57 +00:00
|
|
|
pub fn data<D: Any + Send + Sync>(&self) -> FieldResult<&D> {
|
|
|
|
self.data_opt::<D>()
|
|
|
|
.ok_or_else(|| format!("Data `{}` does not exist.", std::any::type_name::<D>()).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the global data defined in the `Context` or `Schema`.
|
|
|
|
///
|
2020-05-22 06:02:28 +00:00
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// It will panic if the specified data type does not exist.
|
2020-07-06 23:54:57 +00:00
|
|
|
pub fn data_unchecked<D: Any + Send + Sync>(&self) -> &D {
|
2020-04-23 06:52:22 +00:00
|
|
|
self.data_opt::<D>()
|
2020-07-06 23:54:57 +00:00
|
|
|
.unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::<D>()))
|
2020-04-23 06:52:22 +00:00
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
/// Gets the global data defined in the `Context` or `Schema` or `None` if the specified type data does not exist.
|
2020-04-23 06:52:22 +00:00
|
|
|
pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&D> {
|
2020-05-19 08:24:15 +00:00
|
|
|
self.query_env
|
|
|
|
.ctx_data
|
|
|
|
.0
|
|
|
|
.get(&TypeId::of::<D>())
|
|
|
|
.or_else(|| self.schema_env.data.0.get(&TypeId::of::<D>()))
|
2020-03-05 00:39:56 +00:00
|
|
|
.and_then(|d| d.downcast_ref::<D>())
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
2020-03-05 07:50:57 +00:00
|
|
|
|
2020-04-02 02:21:04 +00:00
|
|
|
fn var_value(&self, name: &str, pos: Pos) -> Result<Value> {
|
2020-09-06 05:38:31 +00:00
|
|
|
self.query_env
|
|
|
|
.operation
|
|
|
|
.node
|
2020-03-10 06:14:09 +00:00
|
|
|
.variable_definitions
|
2020-04-01 08:53:49 +00:00
|
|
|
.iter()
|
2020-09-06 05:38:31 +00:00
|
|
|
.find(|def| def.node.name.node == name)
|
|
|
|
.and_then(|def| {
|
|
|
|
self.query_env
|
|
|
|
.variables
|
|
|
|
.0
|
|
|
|
.get(&def.node.name.node)
|
|
|
|
.or_else(|| def.node.default_value())
|
|
|
|
})
|
|
|
|
.cloned()
|
|
|
|
.ok_or_else(|| {
|
|
|
|
QueryError::VarNotDefined {
|
|
|
|
var_name: name.to_owned(),
|
2020-06-11 15:00:47 +00:00
|
|
|
}
|
2020-09-06 05:38:31 +00:00
|
|
|
.into_error(pos)
|
|
|
|
})
|
2020-03-10 06:14:09 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
fn resolve_input_value(&self, value: Positioned<InputValue>) -> Result<Value> {
|
|
|
|
let pos = value.pos;
|
2020-09-08 08:30:29 +00:00
|
|
|
value
|
|
|
|
.node
|
|
|
|
.into_const_with(|name| self.var_value(&name, pos))
|
2020-03-04 03:51:42 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 06:52:54 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn is_ifdef(&self, directives: &[Positioned<Directive>]) -> bool {
|
2020-09-06 05:38:31 +00:00
|
|
|
directives
|
|
|
|
.iter()
|
|
|
|
.any(|directive| directive.node.name.node == "ifdef")
|
2020-08-06 06:52:54 +00:00
|
|
|
}
|
|
|
|
|
2020-03-04 06:24:44 +00:00
|
|
|
#[doc(hidden)]
|
2020-05-10 02:59:51 +00:00
|
|
|
pub fn is_skip(&self, directives: &[Positioned<Directive>]) -> Result<bool> {
|
2020-03-05 09:06:14 +00:00
|
|
|
for directive in directives {
|
2020-09-06 05:38:31 +00:00
|
|
|
let include = match &*directive.node.name.node {
|
|
|
|
"skip" => false,
|
|
|
|
"include" => true,
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
let condition_input = directive
|
2020-09-06 06:16:36 +00:00
|
|
|
.node
|
|
|
|
.get_argument("if")
|
|
|
|
.ok_or_else(|| {
|
|
|
|
QueryError::RequiredDirectiveArgs {
|
|
|
|
directive: if include { "@skip" } else { "@include" },
|
2020-03-04 06:24:44 +00:00
|
|
|
arg_name: "if",
|
|
|
|
arg_type: "Boolean!",
|
|
|
|
}
|
2020-09-06 06:16:36 +00:00
|
|
|
.into_error(directive.pos)
|
|
|
|
})?
|
|
|
|
.clone();
|
2020-09-06 05:38:31 +00:00
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
let pos = condition_input.pos;
|
|
|
|
let condition_input = self.resolve_input_value(condition_input)?;
|
2020-09-06 05:38:31 +00:00
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
if include
|
|
|
|
!= <bool as InputValueType>::parse(Some(condition_input))
|
2020-09-06 06:16:36 +00:00
|
|
|
.map_err(|e| e.into_error(pos, bool::qualified_type_name()))?
|
|
|
|
{
|
2020-09-06 05:38:31 +00:00
|
|
|
return Ok(true);
|
2020-03-04 06:24:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(false)
|
|
|
|
}
|
2020-03-01 10:54:34 +00:00
|
|
|
}
|
2020-03-05 09:06:14 +00:00
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
impl<'a> ContextBase<'a, &'a Positioned<SelectionSet>> {
|
2020-03-26 03:34:28 +00:00
|
|
|
#[doc(hidden)]
|
2020-05-10 02:59:51 +00:00
|
|
|
pub fn with_index(&'a self, idx: usize) -> ContextBase<'a, &'a Positioned<SelectionSet>> {
|
2020-03-26 03:34:28 +00:00
|
|
|
ContextBase {
|
2020-03-26 10:30:29 +00:00
|
|
|
path_node: Some(QueryPathNode {
|
|
|
|
parent: self.path_node.as_ref(),
|
|
|
|
segment: QueryPathSegment::Index(idx),
|
|
|
|
}),
|
2020-03-26 03:34:28 +00:00
|
|
|
item: self.item,
|
2020-04-28 07:01:19 +00:00
|
|
|
resolve_id: self.get_child_resolve_id(),
|
|
|
|
inc_resolve_id: self.inc_resolve_id,
|
2020-05-19 08:24:15 +00:00
|
|
|
schema_env: self.schema_env,
|
|
|
|
query_env: self.query_env,
|
2020-03-26 03:34:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
impl<'a> ContextBase<'a, &'a Positioned<Field>> {
|
2020-03-05 09:06:14 +00:00
|
|
|
#[doc(hidden)]
|
2020-05-26 10:34:43 +00:00
|
|
|
pub fn param_value<T: InputValueType>(
|
2020-03-05 09:06:14 +00:00
|
|
|
&self,
|
|
|
|
name: &str,
|
2020-05-26 10:34:43 +00:00
|
|
|
default: Option<fn() -> T>,
|
2020-03-05 09:06:14 +00:00
|
|
|
) -> Result<T> {
|
2020-09-06 05:38:31 +00:00
|
|
|
let value = self.item.node.get_argument(name).cloned();
|
|
|
|
if value.is_none() {
|
|
|
|
if let Some(default) = default {
|
2020-05-28 07:00:55 +00:00
|
|
|
return Ok(default());
|
2020-03-05 09:06:14 +00:00
|
|
|
}
|
2020-05-28 07:00:55 +00:00
|
|
|
}
|
2020-09-06 05:38:31 +00:00
|
|
|
let (pos, value) = match value {
|
2020-09-08 08:21:27 +00:00
|
|
|
Some(value) => (value.pos, Some(self.resolve_input_value(value)?)),
|
2020-09-06 05:38:31 +00:00
|
|
|
None => (Pos::default(), None),
|
2020-05-28 07:00:55 +00:00
|
|
|
};
|
2020-09-06 06:16:36 +00:00
|
|
|
InputValueType::parse(value).map_err(|e| e.into_error(pos, T::qualified_type_name()))
|
2020-03-06 15:58:43 +00:00
|
|
|
}
|
2020-05-14 09:35:25 +00:00
|
|
|
|
2020-05-14 14:13:28 +00:00
|
|
|
/// Creates a uniform interface to inspect the forthcoming selections.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// use async_graphql::*;
|
|
|
|
///
|
2020-09-18 00:52:13 +00:00
|
|
|
/// #[derive(SimpleObject)]
|
2020-05-14 14:13:28 +00:00
|
|
|
/// struct Detail {
|
|
|
|
/// c: i32,
|
|
|
|
/// d: i32,
|
|
|
|
/// }
|
|
|
|
///
|
2020-09-18 00:52:13 +00:00
|
|
|
/// #[derive(SimpleObject)]
|
2020-05-14 14:13:28 +00:00
|
|
|
/// struct MyObj {
|
|
|
|
/// a: i32,
|
|
|
|
/// b: i32,
|
|
|
|
/// detail: Detail,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// struct Query;
|
|
|
|
///
|
2020-09-18 00:52:13 +00:00
|
|
|
/// #[Object]
|
2020-05-14 14:13:28 +00:00
|
|
|
/// impl Query {
|
|
|
|
/// async fn obj(&self, ctx: &Context<'_>) -> MyObj {
|
|
|
|
/// if ctx.look_ahead().field("a").exists() {
|
|
|
|
/// // This is a query like `obj { a }`
|
|
|
|
/// } else if ctx.look_ahead().field("detail").field("c").exists() {
|
|
|
|
/// // This is a query like `obj { detail { c } }`
|
|
|
|
/// } else {
|
|
|
|
/// // This query doesn't have `a`
|
|
|
|
/// }
|
|
|
|
/// unimplemented!()
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub fn look_ahead(&self) -> Lookahead {
|
2020-09-22 18:59:48 +00:00
|
|
|
Lookahead::new(&self.query_env.fragments, &self.item.node)
|
2020-05-14 14:13:28 +00:00
|
|
|
}
|
2020-03-05 09:06:14 +00:00
|
|
|
}
|