Add Span to meta::Arguments and LookAheadArguments (#1206, #1209)

This commit is contained in:
Audun Halland 2023-11-09 18:18:46 +01:00 committed by GitHub
parent d0fc062892
commit e75cf26995
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 55 deletions

View file

@ -54,6 +54,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
- Upgraded [GraphiQL] to 3.0.9 version (requires new [`graphql-transport-ws` GraphQL over WebSocket Protocol] integration on server, see `juniper_warp/examples/subscription.rs`). ([#1188], [#1193], [#1204]) - Upgraded [GraphiQL] to 3.0.9 version (requires new [`graphql-transport-ws` GraphQL over WebSocket Protocol] integration on server, see `juniper_warp/examples/subscription.rs`). ([#1188], [#1193], [#1204])
- Made `LookAheadMethods::children()` method to return slice instead of `Vec`. ([#1200]) - Made `LookAheadMethods::children()` method to return slice instead of `Vec`. ([#1200])
- Abstracted `Spanning::start` and `Spanning::end` fields into separate struct `Span`. ([#1207], [#1208]) - Abstracted `Spanning::start` and `Spanning::end` fields into separate struct `Span`. ([#1207], [#1208])
- Added `Span` to `Arguments` and `LookAheadArguments`. ([#1206], [#1209])
### Added ### Added
@ -132,8 +133,10 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
[#1199]: /../../pull/1199 [#1199]: /../../pull/1199
[#1200]: /../../pull/1200 [#1200]: /../../pull/1200
[#1204]: /../../pull/1204 [#1204]: /../../pull/1204
[#1206]: /../../pull/1206
[#1207]: /../../pull/1207 [#1207]: /../../pull/1207
[#1208]: /../../pull/1208 [#1208]: /../../pull/1208
[#1209]: /../../pull/1209
[ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083 [ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083
[CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j [CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::{ use crate::{
ast::{Directive, Fragment, InputValue, Selection}, ast::{Directive, Fragment, InputValue, Selection},
parser::Spanning, parser::{Span, Spanning},
value::ScalarValue, value::ScalarValue,
}; };
@ -18,48 +18,71 @@ pub enum Applies<'a> {
OnlyType(&'a str), OnlyType(&'a str),
} }
/// A JSON-like value that can is used as argument in the query execution /// Shortcut for a [`Spanning`] containing a borrowed [`Span`].
type BorrowedSpanning<'a, T> = Spanning<T, &'a Span>;
/// JSON-like value that can be used as an argument in the query execution.
/// ///
/// In contrast to `InputValue` these values do only contain constants, /// In contrast to an [`InputValue`], these values do only contain constants,
/// meaning that variables are already resolved. /// meaning that variables are already resolved.
#[derive(Debug, Clone, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum LookAheadValue<'a, S: 'a> { pub enum LookAheadValue<'a, S: 'a> {
Null, Null,
Scalar(&'a S), Scalar(&'a S),
Enum(&'a str), Enum(&'a str),
List(Vec<LookAheadValue<'a, S>>), List(Vec<BorrowedSpanning<'a, LookAheadValue<'a, S>>>),
Object(Vec<(&'a str, LookAheadValue<'a, S>)>), Object(
Vec<(
BorrowedSpanning<'a, &'a str>,
BorrowedSpanning<'a, LookAheadValue<'a, S>>,
)>,
),
} }
impl<'a, S> LookAheadValue<'a, S> impl<'a, S> LookAheadValue<'a, S>
where where
S: ScalarValue, S: ScalarValue,
{ {
fn from_input_value(input_value: &'a InputValue<S>, vars: &'a Variables<S>) -> Self { fn from_input_value(
match *input_value { input_value: BorrowedSpanning<'a, &'a InputValue<S>>,
InputValue::Null => LookAheadValue::Null, vars: &'a Variables<S>,
InputValue::Scalar(ref s) => LookAheadValue::Scalar(s), ) -> BorrowedSpanning<'a, Self> {
InputValue::Enum(ref e) => LookAheadValue::Enum(e), Spanning {
InputValue::Variable(ref name) => vars span: input_value.span,
.get(name) item: match input_value.item {
.map(|v| Self::from_input_value(v, vars)) InputValue::Null => Self::Null,
.unwrap_or(LookAheadValue::Null), InputValue::Scalar(s) => Self::Scalar(s),
InputValue::List(ref l) => LookAheadValue::List( InputValue::Enum(e) => Self::Enum(e),
l.iter() InputValue::Variable(name) => vars
.map(|i| LookAheadValue::from_input_value(&i.item, vars)) .get(name)
.collect(), .map(|item| {
), let input_value = Spanning {
InputValue::Object(ref o) => LookAheadValue::Object( span: input_value.span,
o.iter() item,
.map(|(n, i)| { };
( Self::from_input_value(input_value, vars).item
&n.item as &str,
LookAheadValue::from_input_value(&i.item, vars),
)
}) })
.collect(), .unwrap_or(Self::Null),
), InputValue::List(l) => Self::List(
l.iter()
.map(|i| Self::from_input_value(i.as_ref(), vars))
.collect(),
),
InputValue::Object(o) => Self::Object(
o.iter()
.map(|(n, i)| {
(
Spanning {
span: &n.span,
item: n.item.as_str(),
},
Self::from_input_value(i.as_ref(), vars),
)
})
.collect(),
),
},
} }
} }
} }
@ -68,7 +91,7 @@ where
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LookAheadArgument<'a, S: 'a> { pub struct LookAheadArgument<'a, S: 'a> {
name: &'a str, name: &'a str,
value: LookAheadValue<'a, S>, value: BorrowedSpanning<'a, LookAheadValue<'a, S>>,
} }
impl<'a, S> LookAheadArgument<'a, S> impl<'a, S> LookAheadArgument<'a, S>
@ -81,7 +104,7 @@ where
) -> Self { ) -> Self {
LookAheadArgument { LookAheadArgument {
name: name.item, name: name.item,
value: LookAheadValue::from_input_value(&value.item, vars), value: LookAheadValue::from_input_value(value.as_ref(), vars),
} }
} }
@ -92,7 +115,12 @@ where
/// The value of the argument /// The value of the argument
pub fn value(&'a self) -> &LookAheadValue<'a, S> { pub fn value(&'a self) -> &LookAheadValue<'a, S> {
&self.value &self.value.item
}
/// The input source span of the argument
pub fn span(&self) -> &Span {
self.value.span
} }
} }
@ -145,7 +173,7 @@ where
.find(|item| item.0.item == "if") .find(|item| item.0.item == "if")
.map(|(_, v)| { .map(|(_, v)| {
if let LookAheadValue::Scalar(s) = if let LookAheadValue::Scalar(s) =
LookAheadValue::from_input_value(&v.item, vars) LookAheadValue::from_input_value(v.as_ref(), vars).item
{ {
s.as_bool().unwrap_or(false) s.as_bool().unwrap_or(false)
} else { } else {
@ -160,7 +188,7 @@ where
.find(|item| item.0.item == "if") .find(|item| item.0.item == "if")
.map(|(_, v)| { .map(|(_, v)| {
if let LookAheadValue::Scalar(b) = if let LookAheadValue::Scalar(b) =
LookAheadValue::from_input_value(&v.item, vars) LookAheadValue::from_input_value(v.as_ref(), vars).item
{ {
b.as_bool().map(::std::ops::Not::not).unwrap_or(false) b.as_bool().map(::std::ops::Not::not).unwrap_or(false)
} else { } else {
@ -472,12 +500,12 @@ impl<'a, S> LookAheadMethods<'a, S> for LookAheadSelection<'a, S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use std::{collections::HashMap, ops::Range};
use crate::{ use crate::{
ast::{Document, OwnedDocument}, ast::{Document, OwnedDocument},
graphql_vars, graphql_vars,
parser::UnlocatedParseResult, parser::{SourcePosition, UnlocatedParseResult},
schema::model::SchemaType, schema::model::SchemaType,
validation::test_harness::{MutationRoot, QueryRoot, SubscriptionRoot}, validation::test_harness::{MutationRoot, QueryRoot, SubscriptionRoot},
value::{DefaultScalarValue, ScalarValue}, value::{DefaultScalarValue, ScalarValue},
@ -509,6 +537,13 @@ mod tests {
fragments fragments
} }
fn span(range: Range<(usize, usize, usize)>) -> Span {
Span {
start: SourcePosition::new(range.start.0, range.start.1, range.start.2),
end: SourcePosition::new(range.end.0, range.end.1, range.end.2),
}
}
#[test] #[test]
fn check_simple_query() { fn check_simple_query() {
let docs = parse_document_source::<DefaultScalarValue>( let docs = parse_document_source::<DefaultScalarValue>(
@ -711,12 +746,17 @@ query Hero {
&fragments, &fragments,
) )
.unwrap(); .unwrap();
let span0 = span((32, 2, 18)..(38, 2, 24));
let span1 = span((77, 4, 24)..(81, 4, 28));
let expected = LookAheadSelection { let expected = LookAheadSelection {
name: "hero", name: "hero",
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "episode", name: "episode",
value: LookAheadValue::Enum("EMPIRE"), value: Spanning {
item: LookAheadValue::Enum("EMPIRE"),
span: &span0,
},
}], }],
applies_for: Applies::All, applies_for: Applies::All,
children: vec![ children: vec![
@ -732,7 +772,10 @@ query Hero {
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "uppercase", name: "uppercase",
value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)), value: Spanning {
item: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
span: &span1,
},
}], }],
children: Vec::new(), children: Vec::new(),
applies_for: Applies::All, applies_for: Applies::All,
@ -768,12 +811,16 @@ query Hero($episode: Episode) {
&fragments, &fragments,
) )
.unwrap(); .unwrap();
let span0 = span((51, 2, 18)..(59, 2, 26));
let expected = LookAheadSelection { let expected = LookAheadSelection {
name: "hero", name: "hero",
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "episode", name: "episode",
value: LookAheadValue::Enum("JEDI"), value: Spanning {
item: LookAheadValue::Enum("JEDI"),
span: &span0,
},
}], }],
applies_for: Applies::All, applies_for: Applies::All,
children: vec![ children: vec![
@ -821,12 +868,16 @@ query Hero($episode: Episode) {
&fragments, &fragments,
) )
.unwrap(); .unwrap();
let span0 = span((51, 2, 18)..(59, 2, 26));
let expected = LookAheadSelection { let expected = LookAheadSelection {
name: "hero", name: "hero",
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "episode", name: "episode",
value: LookAheadValue::Null, value: Spanning {
item: LookAheadValue::Null,
span: &span0,
},
}], }],
applies_for: Applies::All, applies_for: Applies::All,
children: vec![LookAheadSelection { children: vec![LookAheadSelection {
@ -1121,12 +1172,16 @@ fragment comparisonFields on Character {
&fragments, &fragments,
) )
.unwrap(); .unwrap();
let span0 = span((85, 2, 11)..(88, 2, 14));
let expected = LookAheadSelection { let expected = LookAheadSelection {
name: "hero", name: "hero",
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "id", name: "id",
value: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)), value: Spanning {
item: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
span: &span0,
},
}], }],
applies_for: Applies::All, applies_for: Applies::All,
children: vec![ children: vec![

View file

@ -52,15 +52,15 @@ impl Span {
/// Data structure used to wrap items into a [`Span`]. /// Data structure used to wrap items into a [`Span`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Spanning<T> { pub struct Spanning<T, Sp = Span> {
/// Wrapped item. /// Wrapped item.
pub item: T, pub item: T,
/// [`Span`] of the wrapped item. /// [`Span`] of the wrapped item.
pub span: Span, pub span: Sp,
} }
impl<T> Spanning<T> { impl<T> Spanning<T, Span> {
#[doc(hidden)] #[doc(hidden)]
pub fn new(span: Span, item: T) -> Self { pub fn new(span: Span, item: T) -> Self {
Self { item, span } Self { item, span }
@ -126,6 +126,14 @@ impl<T> Spanning<T> {
pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> { pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
f(self.item).map(|item| Spanning::new(self.span, item)) f(self.item).map(|item| Spanning::new(self.span, item))
} }
/// Converts into a [`Spanning`] containing a borrowed item and a borrowed [`Span`].
pub(crate) fn as_ref(&self) -> Spanning<&'_ T, &'_ Span> {
Spanning {
item: &self.item,
span: &self.span,
}
}
} }
impl<T: fmt::Display> fmt::Display for Spanning<T> { impl<T: fmt::Display> fmt::Display for Spanning<T> {

View file

@ -244,7 +244,8 @@ where
m.item m.item
.iter() .iter()
.filter_map(|(k, v)| { .filter_map(|(k, v)| {
v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) let val = v.item.clone().into_const(exec_vars)?;
Some((k.item, Spanning::new(v.span, val)))
}) })
.collect() .collect()
}), }),

View file

@ -68,13 +68,13 @@ pub enum TypeKind {
/// Field argument container /// Field argument container
#[derive(Debug)] #[derive(Debug)]
pub struct Arguments<'a, S = DefaultScalarValue> { pub struct Arguments<'a, S = DefaultScalarValue> {
args: Option<IndexMap<&'a str, InputValue<S>>>, args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
} }
impl<'a, S> Arguments<'a, S> { impl<'a, S> Arguments<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub fn new( pub fn new(
mut args: Option<IndexMap<&'a str, InputValue<S>>>, mut args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
meta_args: &'a Option<Vec<Argument<S>>>, meta_args: &'a Option<Vec<Argument<S>>>,
) -> Self ) -> Self
where where
@ -89,7 +89,7 @@ impl<'a, S> Arguments<'a, S> {
let arg_name = arg.name.as_str(); let arg_name = arg.name.as_str();
if args.get(arg_name).is_none() { if args.get(arg_name).is_none() {
if let Some(val) = arg.default_value.as_ref() { if let Some(val) = arg.default_value.as_ref() {
args.insert(arg_name, val.clone()); args.insert(arg_name, Spanning::unlocated(val.clone()));
} }
} }
} }
@ -117,10 +117,16 @@ impl<'a, S> Arguments<'a, S> {
self.args self.args
.as_ref() .as_ref()
.and_then(|args| args.get(name)) .and_then(|args| args.get(name))
.map(|spanning| &spanning.item)
.map(InputValue::convert) .map(InputValue::convert)
.transpose() .transpose()
.map_err(IntoFieldError::into_field_error) .map_err(IntoFieldError::into_field_error)
} }
/// Gets a direct reference to the [`Spanning`] argument [`InputValue`].
pub fn get_input_value(&self, name: &str) -> Option<&Spanning<InputValue<S>>> {
self.args.as_ref().and_then(|args| args.get(name))
}
} }
/// Primary trait used to resolve GraphQL values. /// Primary trait used to resolve GraphQL values.
@ -472,7 +478,8 @@ where
m.item m.item
.iter() .iter()
.filter_map(|(k, v)| { .filter_map(|(k, v)| {
v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) let val = v.item.clone().into_const(exec_vars)?;
Some((k.item, Spanning::new(v.span, val)))
}) })
.collect() .collect()
}), }),

View file

@ -316,7 +316,8 @@ where
m.item m.item
.iter() .iter()
.filter_map(|(k, v)| { .filter_map(|(k, v)| {
v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) let val = v.item.clone().into_const(exec_vars)?;
Some((k.item, Spanning::new(v.span, val)))
}) })
.collect() .collect()
}), }),

View file

@ -72,11 +72,8 @@ where
let mut remaining_required_fields = input_fields let mut remaining_required_fields = input_fields
.iter() .iter()
.filter_map(|f| { .filter_map(|f| {
if f.arg_type.is_non_null() && f.default_value.is_none() { (f.arg_type.is_non_null() && f.default_value.is_none())
Some(&f.name) .then_some(&f.name)
} else {
None
}
}) })
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();