Skip to content

Commit e75cf26

Browse files
authored
Add Span to meta::Arguments and LookAheadArguments (#1206, #1209)
1 parent d0fc062 commit e75cf26

File tree

7 files changed

+127
-55
lines changed

7 files changed

+127
-55
lines changed

juniper/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
5454
- 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])
5555
- Made `LookAheadMethods::children()` method to return slice instead of `Vec`. ([#1200])
5656
- Abstracted `Spanning::start` and `Spanning::end` fields into separate struct `Span`. ([#1207], [#1208])
57+
- Added `Span` to `Arguments` and `LookAheadArguments`. ([#1206], [#1209])
5758

5859
### Added
5960

@@ -132,8 +133,10 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
132133
[#1199]: /../../pull/1199
133134
[#1200]: /../../pull/1200
134135
[#1204]: /../../pull/1204
136+
[#1206]: /../../pull/1206
135137
[#1207]: /../../pull/1207
136138
[#1208]: /../../pull/1208
139+
[#1209]: /../../pull/1209
137140
[ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083
138141
[CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j
139142

juniper/src/executor/look_ahead.rs

+96-41
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashMap;
22

33
use crate::{
44
ast::{Directive, Fragment, InputValue, Selection},
5-
parser::Spanning,
5+
parser::{Span, Spanning},
66
value::ScalarValue,
77
};
88

@@ -18,48 +18,71 @@ pub enum Applies<'a> {
1818
OnlyType(&'a str),
1919
}
2020

21-
/// A JSON-like value that can is used as argument in the query execution
21+
/// Shortcut for a [`Spanning`] containing a borrowed [`Span`].
22+
type BorrowedSpanning<'a, T> = Spanning<T, &'a Span>;
23+
24+
/// JSON-like value that can be used as an argument in the query execution.
2225
///
23-
/// In contrast to `InputValue` these values do only contain constants,
26+
/// In contrast to an [`InputValue`], these values do only contain constants,
2427
/// meaning that variables are already resolved.
25-
#[derive(Debug, Clone, PartialEq)]
28+
#[derive(Clone, Debug, PartialEq)]
2629
#[allow(missing_docs)]
2730
pub enum LookAheadValue<'a, S: 'a> {
2831
Null,
2932
Scalar(&'a S),
3033
Enum(&'a str),
31-
List(Vec<LookAheadValue<'a, S>>),
32-
Object(Vec<(&'a str, LookAheadValue<'a, S>)>),
34+
List(Vec<BorrowedSpanning<'a, LookAheadValue<'a, S>>>),
35+
Object(
36+
Vec<(
37+
BorrowedSpanning<'a, &'a str>,
38+
BorrowedSpanning<'a, LookAheadValue<'a, S>>,
39+
)>,
40+
),
3341
}
3442

3543
impl<'a, S> LookAheadValue<'a, S>
3644
where
3745
S: ScalarValue,
3846
{
39-
fn from_input_value(input_value: &'a InputValue<S>, vars: &'a Variables<S>) -> Self {
40-
match *input_value {
41-
InputValue::Null => LookAheadValue::Null,
42-
InputValue::Scalar(ref s) => LookAheadValue::Scalar(s),
43-
InputValue::Enum(ref e) => LookAheadValue::Enum(e),
44-
InputValue::Variable(ref name) => vars
45-
.get(name)
46-
.map(|v| Self::from_input_value(v, vars))
47-
.unwrap_or(LookAheadValue::Null),
48-
InputValue::List(ref l) => LookAheadValue::List(
49-
l.iter()
50-
.map(|i| LookAheadValue::from_input_value(&i.item, vars))
51-
.collect(),
52-
),
53-
InputValue::Object(ref o) => LookAheadValue::Object(
54-
o.iter()
55-
.map(|(n, i)| {
56-
(
57-
&n.item as &str,
58-
LookAheadValue::from_input_value(&i.item, vars),
59-
)
47+
fn from_input_value(
48+
input_value: BorrowedSpanning<'a, &'a InputValue<S>>,
49+
vars: &'a Variables<S>,
50+
) -> BorrowedSpanning<'a, Self> {
51+
Spanning {
52+
span: input_value.span,
53+
item: match input_value.item {
54+
InputValue::Null => Self::Null,
55+
InputValue::Scalar(s) => Self::Scalar(s),
56+
InputValue::Enum(e) => Self::Enum(e),
57+
InputValue::Variable(name) => vars
58+
.get(name)
59+
.map(|item| {
60+
let input_value = Spanning {
61+
span: input_value.span,
62+
item,
63+
};
64+
Self::from_input_value(input_value, vars).item
6065
})
61-
.collect(),
62-
),
66+
.unwrap_or(Self::Null),
67+
InputValue::List(l) => Self::List(
68+
l.iter()
69+
.map(|i| Self::from_input_value(i.as_ref(), vars))
70+
.collect(),
71+
),
72+
InputValue::Object(o) => Self::Object(
73+
o.iter()
74+
.map(|(n, i)| {
75+
(
76+
Spanning {
77+
span: &n.span,
78+
item: n.item.as_str(),
79+
},
80+
Self::from_input_value(i.as_ref(), vars),
81+
)
82+
})
83+
.collect(),
84+
),
85+
},
6386
}
6487
}
6588
}
@@ -68,7 +91,7 @@ where
6891
#[derive(Debug, Clone, PartialEq)]
6992
pub struct LookAheadArgument<'a, S: 'a> {
7093
name: &'a str,
71-
value: LookAheadValue<'a, S>,
94+
value: BorrowedSpanning<'a, LookAheadValue<'a, S>>,
7295
}
7396

7497
impl<'a, S> LookAheadArgument<'a, S>
@@ -81,7 +104,7 @@ where
81104
) -> Self {
82105
LookAheadArgument {
83106
name: name.item,
84-
value: LookAheadValue::from_input_value(&value.item, vars),
107+
value: LookAheadValue::from_input_value(value.as_ref(), vars),
85108
}
86109
}
87110

@@ -92,7 +115,12 @@ where
92115

93116
/// The value of the argument
94117
pub fn value(&'a self) -> &LookAheadValue<'a, S> {
95-
&self.value
118+
&self.value.item
119+
}
120+
121+
/// The input source span of the argument
122+
pub fn span(&self) -> &Span {
123+
self.value.span
96124
}
97125
}
98126

@@ -145,7 +173,7 @@ where
145173
.find(|item| item.0.item == "if")
146174
.map(|(_, v)| {
147175
if let LookAheadValue::Scalar(s) =
148-
LookAheadValue::from_input_value(&v.item, vars)
176+
LookAheadValue::from_input_value(v.as_ref(), vars).item
149177
{
150178
s.as_bool().unwrap_or(false)
151179
} else {
@@ -160,7 +188,7 @@ where
160188
.find(|item| item.0.item == "if")
161189
.map(|(_, v)| {
162190
if let LookAheadValue::Scalar(b) =
163-
LookAheadValue::from_input_value(&v.item, vars)
191+
LookAheadValue::from_input_value(v.as_ref(), vars).item
164192
{
165193
b.as_bool().map(::std::ops::Not::not).unwrap_or(false)
166194
} else {
@@ -472,12 +500,12 @@ impl<'a, S> LookAheadMethods<'a, S> for LookAheadSelection<'a, S> {
472500

473501
#[cfg(test)]
474502
mod tests {
475-
use std::collections::HashMap;
503+
use std::{collections::HashMap, ops::Range};
476504

477505
use crate::{
478506
ast::{Document, OwnedDocument},
479507
graphql_vars,
480-
parser::UnlocatedParseResult,
508+
parser::{SourcePosition, UnlocatedParseResult},
481509
schema::model::SchemaType,
482510
validation::test_harness::{MutationRoot, QueryRoot, SubscriptionRoot},
483511
value::{DefaultScalarValue, ScalarValue},
@@ -509,6 +537,13 @@ mod tests {
509537
fragments
510538
}
511539

540+
fn span(range: Range<(usize, usize, usize)>) -> Span {
541+
Span {
542+
start: SourcePosition::new(range.start.0, range.start.1, range.start.2),
543+
end: SourcePosition::new(range.end.0, range.end.1, range.end.2),
544+
}
545+
}
546+
512547
#[test]
513548
fn check_simple_query() {
514549
let docs = parse_document_source::<DefaultScalarValue>(
@@ -711,12 +746,17 @@ query Hero {
711746
&fragments,
712747
)
713748
.unwrap();
749+
let span0 = span((32, 2, 18)..(38, 2, 24));
750+
let span1 = span((77, 4, 24)..(81, 4, 28));
714751
let expected = LookAheadSelection {
715752
name: "hero",
716753
alias: None,
717754
arguments: vec![LookAheadArgument {
718755
name: "episode",
719-
value: LookAheadValue::Enum("EMPIRE"),
756+
value: Spanning {
757+
item: LookAheadValue::Enum("EMPIRE"),
758+
span: &span0,
759+
},
720760
}],
721761
applies_for: Applies::All,
722762
children: vec![
@@ -732,7 +772,10 @@ query Hero {
732772
alias: None,
733773
arguments: vec![LookAheadArgument {
734774
name: "uppercase",
735-
value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
775+
value: Spanning {
776+
item: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
777+
span: &span1,
778+
},
736779
}],
737780
children: Vec::new(),
738781
applies_for: Applies::All,
@@ -768,12 +811,16 @@ query Hero($episode: Episode) {
768811
&fragments,
769812
)
770813
.unwrap();
814+
let span0 = span((51, 2, 18)..(59, 2, 26));
771815
let expected = LookAheadSelection {
772816
name: "hero",
773817
alias: None,
774818
arguments: vec![LookAheadArgument {
775819
name: "episode",
776-
value: LookAheadValue::Enum("JEDI"),
820+
value: Spanning {
821+
item: LookAheadValue::Enum("JEDI"),
822+
span: &span0,
823+
},
777824
}],
778825
applies_for: Applies::All,
779826
children: vec![
@@ -821,12 +868,16 @@ query Hero($episode: Episode) {
821868
&fragments,
822869
)
823870
.unwrap();
871+
let span0 = span((51, 2, 18)..(59, 2, 26));
824872
let expected = LookAheadSelection {
825873
name: "hero",
826874
alias: None,
827875
arguments: vec![LookAheadArgument {
828876
name: "episode",
829-
value: LookAheadValue::Null,
877+
value: Spanning {
878+
item: LookAheadValue::Null,
879+
span: &span0,
880+
},
830881
}],
831882
applies_for: Applies::All,
832883
children: vec![LookAheadSelection {
@@ -1121,12 +1172,16 @@ fragment comparisonFields on Character {
11211172
&fragments,
11221173
)
11231174
.unwrap();
1175+
let span0 = span((85, 2, 11)..(88, 2, 14));
11241176
let expected = LookAheadSelection {
11251177
name: "hero",
11261178
alias: None,
11271179
arguments: vec![LookAheadArgument {
11281180
name: "id",
1129-
value: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
1181+
value: Spanning {
1182+
item: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
1183+
span: &span0,
1184+
},
11301185
}],
11311186
applies_for: Applies::All,
11321187
children: vec![

juniper/src/parser/utils.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ impl Span {
5252

5353
/// Data structure used to wrap items into a [`Span`].
5454
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55-
pub struct Spanning<T> {
55+
pub struct Spanning<T, Sp = Span> {
5656
/// Wrapped item.
5757
pub item: T,
5858

5959
/// [`Span`] of the wrapped item.
60-
pub span: Span,
60+
pub span: Sp,
6161
}
6262

63-
impl<T> Spanning<T> {
63+
impl<T> Spanning<T, Span> {
6464
#[doc(hidden)]
6565
pub fn new(span: Span, item: T) -> Self {
6666
Self { item, span }
@@ -126,6 +126,14 @@ impl<T> Spanning<T> {
126126
pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
127127
f(self.item).map(|item| Spanning::new(self.span, item))
128128
}
129+
130+
/// Converts into a [`Spanning`] containing a borrowed item and a borrowed [`Span`].
131+
pub(crate) fn as_ref(&self) -> Spanning<&'_ T, &'_ Span> {
132+
Spanning {
133+
item: &self.item,
134+
span: &self.span,
135+
}
136+
}
129137
}
130138

131139
impl<T: fmt::Display> fmt::Display for Spanning<T> {

juniper/src/types/async_await.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ where
244244
m.item
245245
.iter()
246246
.filter_map(|(k, v)| {
247-
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
247+
let val = v.item.clone().into_const(exec_vars)?;
248+
Some((k.item, Spanning::new(v.span, val)))
248249
})
249250
.collect()
250251
}),

juniper/src/types/base.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ pub enum TypeKind {
6868
/// Field argument container
6969
#[derive(Debug)]
7070
pub struct Arguments<'a, S = DefaultScalarValue> {
71-
args: Option<IndexMap<&'a str, InputValue<S>>>,
71+
args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
7272
}
7373

7474
impl<'a, S> Arguments<'a, S> {
7575
#[doc(hidden)]
7676
pub fn new(
77-
mut args: Option<IndexMap<&'a str, InputValue<S>>>,
77+
mut args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
7878
meta_args: &'a Option<Vec<Argument<S>>>,
7979
) -> Self
8080
where
@@ -89,7 +89,7 @@ impl<'a, S> Arguments<'a, S> {
8989
let arg_name = arg.name.as_str();
9090
if args.get(arg_name).is_none() {
9191
if let Some(val) = arg.default_value.as_ref() {
92-
args.insert(arg_name, val.clone());
92+
args.insert(arg_name, Spanning::unlocated(val.clone()));
9393
}
9494
}
9595
}
@@ -117,10 +117,16 @@ impl<'a, S> Arguments<'a, S> {
117117
self.args
118118
.as_ref()
119119
.and_then(|args| args.get(name))
120+
.map(|spanning| &spanning.item)
120121
.map(InputValue::convert)
121122
.transpose()
122123
.map_err(IntoFieldError::into_field_error)
123124
}
125+
126+
/// Gets a direct reference to the [`Spanning`] argument [`InputValue`].
127+
pub fn get_input_value(&self, name: &str) -> Option<&Spanning<InputValue<S>>> {
128+
self.args.as_ref().and_then(|args| args.get(name))
129+
}
124130
}
125131

126132
/// Primary trait used to resolve GraphQL values.
@@ -472,7 +478,8 @@ where
472478
m.item
473479
.iter()
474480
.filter_map(|(k, v)| {
475-
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
481+
let val = v.item.clone().into_const(exec_vars)?;
482+
Some((k.item, Spanning::new(v.span, val)))
476483
})
477484
.collect()
478485
}),

juniper/src/types/subscriptions.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ where
316316
m.item
317317
.iter()
318318
.filter_map(|(k, v)| {
319-
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
319+
let val = v.item.clone().into_const(exec_vars)?;
320+
Some((k.item, Spanning::new(v.span, val)))
320321
})
321322
.collect()
322323
}),

0 commit comments

Comments
 (0)