diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 68cc94c8f..5449ac68a 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -53,6 +53,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi - Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#1081], [#528]) - 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]) +- Added `Spanning` to `Arguments` and `LookAheadArguments`. ([#1206]) ### Added @@ -131,6 +132,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi [#1199]: /../../pull/1199 [#1200]: /../../pull/1200 [#1204]: /../../pull/1204 +[#1206]: /../../pull/1206 [ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083 [CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j diff --git a/juniper/src/executor/look_ahead.rs b/juniper/src/executor/look_ahead.rs index 54643db7c..997ceadb6 100644 --- a/juniper/src/executor/look_ahead.rs +++ b/juniper/src/executor/look_ahead.rs @@ -28,37 +28,50 @@ pub enum LookAheadValue<'a, S: 'a> { Null, Scalar(&'a S), Enum(&'a str), - List(Vec>), - Object(Vec<(&'a str, LookAheadValue<'a, S>)>), + List(Vec>>), + Object(Vec<(Spanning<&'a str>, Spanning>)>), } impl<'a, S> LookAheadValue<'a, S> where S: ScalarValue, { - fn from_input_value(input_value: &'a InputValue, vars: &'a Variables) -> Self { - match *input_value { - InputValue::Null => LookAheadValue::Null, - InputValue::Scalar(ref s) => LookAheadValue::Scalar(s), - InputValue::Enum(ref e) => LookAheadValue::Enum(e), + fn from_input_value( + input_value: Spanning<&'a InputValue>, + vars: &'a Variables, + ) -> Spanning { + let start = &input_value.start; + let end = &input_value.end; + match input_value.item { + InputValue::Null => Spanning::start_end(start, end, LookAheadValue::Null), + InputValue::Scalar(ref s) => Spanning::start_end(start, end, LookAheadValue::Scalar(s)), + InputValue::Enum(ref e) => Spanning::start_end(start, end, LookAheadValue::Enum(e)), InputValue::Variable(ref name) => vars .get(name) - .map(|v| Self::from_input_value(v, vars)) - .unwrap_or(LookAheadValue::Null), - InputValue::List(ref l) => LookAheadValue::List( - l.iter() - .map(|i| LookAheadValue::from_input_value(&i.item, vars)) - .collect(), + .map(|v| Self::from_input_value(Spanning::start_end(start, end, v), vars)) + .unwrap_or(Spanning::start_end(start, end, LookAheadValue::Null)), + InputValue::List(ref l) => Spanning::start_end( + start, + end, + LookAheadValue::List( + l.iter() + .map(|i| LookAheadValue::from_input_value(i.as_ref(), vars)) + .collect(), + ), ), - InputValue::Object(ref o) => LookAheadValue::Object( - o.iter() - .map(|(n, i)| { - ( - &n.item as &str, - LookAheadValue::from_input_value(&i.item, vars), - ) - }) - .collect(), + InputValue::Object(ref o) => Spanning::start_end( + start, + end, + LookAheadValue::Object( + o.iter() + .map(|(n, i)| { + ( + n.as_ref().map(|n| n.as_str()), + LookAheadValue::from_input_value(i.as_ref(), vars), + ) + }) + .collect(), + ), ), } } @@ -68,7 +81,7 @@ where #[derive(Debug, Clone, PartialEq)] pub struct LookAheadArgument<'a, S: 'a> { name: &'a str, - value: LookAheadValue<'a, S>, + value: Spanning>, } impl<'a, S> LookAheadArgument<'a, S> @@ -81,7 +94,7 @@ where ) -> Self { LookAheadArgument { name: name.item, - value: LookAheadValue::from_input_value(&value.item, vars), + value: LookAheadValue::from_input_value(value.as_ref(), vars), } } @@ -92,6 +105,11 @@ where /// The value of the argument pub fn value(&'a self) -> &LookAheadValue<'a, S> { + &self.value.item + } + + /// The spanned value of the argument + pub fn spanned_value(&'a self) -> &Spanning> { &self.value } } @@ -145,7 +163,7 @@ where .find(|item| item.0.item == "if") .map(|(_, v)| { 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) } else { @@ -160,7 +178,7 @@ where .find(|item| item.0.item == "if") .map(|(_, v)| { 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) } else { @@ -477,7 +495,7 @@ mod tests { use crate::{ ast::{Document, OwnedDocument}, graphql_vars, - parser::UnlocatedParseResult, + parser::{SourcePosition, UnlocatedParseResult}, schema::model::SchemaType, validation::test_harness::{MutationRoot, QueryRoot, SubscriptionRoot}, value::{DefaultScalarValue, ScalarValue}, @@ -716,7 +734,11 @@ query Hero { alias: None, arguments: vec![LookAheadArgument { name: "episode", - value: LookAheadValue::Enum("EMPIRE"), + value: Spanning::start_end( + &SourcePosition::new(32, 2, 18), + &SourcePosition::new(38, 2, 24), + LookAheadValue::Enum("EMPIRE"), + ), }], applies_for: Applies::All, children: vec![ @@ -732,7 +754,11 @@ query Hero { alias: None, arguments: vec![LookAheadArgument { name: "uppercase", - value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)), + value: Spanning::start_end( + &SourcePosition::new(77, 4, 24), + &SourcePosition::new(81, 4, 28), + LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)), + ), }], children: Vec::new(), applies_for: Applies::All, @@ -773,7 +799,11 @@ query Hero($episode: Episode) { alias: None, arguments: vec![LookAheadArgument { name: "episode", - value: LookAheadValue::Enum("JEDI"), + value: Spanning::start_end( + &SourcePosition::new(51, 2, 18), + &SourcePosition::new(59, 2, 26), + LookAheadValue::Enum("JEDI"), + ), }], applies_for: Applies::All, children: vec![ @@ -826,7 +856,11 @@ query Hero($episode: Episode) { alias: None, arguments: vec![LookAheadArgument { name: "episode", - value: LookAheadValue::Null, + value: Spanning::start_end( + &SourcePosition::new(51, 2, 18), + &SourcePosition::new(59, 2, 26), + LookAheadValue::Null, + ), }], applies_for: Applies::All, children: vec![LookAheadSelection { @@ -1126,7 +1160,11 @@ fragment comparisonFields on Character { alias: None, arguments: vec![LookAheadArgument { name: "id", - value: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)), + value: Spanning::start_end( + &SourcePosition::new(85, 2, 11), + &SourcePosition::new(88, 2, 14), + LookAheadValue::Scalar(&DefaultScalarValue::Int(42)), + ), }], applies_for: Applies::All, children: vec![ diff --git a/juniper/src/parser/utils.rs b/juniper/src/parser/utils.rs index 645c30141..6d54f420d 100644 --- a/juniper/src/parser/utils.rs +++ b/juniper/src/parser/utils.rs @@ -90,6 +90,15 @@ impl Spanning { } } + /// Convert the item to a reference + pub fn as_ref(&self) -> Spanning<&T> { + Spanning { + item: &self.item, + start: self.start, + end: self.end, + } + } + /// Modifies the contents of the spanned item in case `f` returns [`Some`], /// or returns [`None`] otherwise. pub fn and_then Option>(self, f: F) -> Option> { diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 04f5eda96..e2bbebc2c 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -12,7 +12,7 @@ use crate::{ schema::model::SchemaType, types::base::TypeKind, value::{DefaultScalarValue, ParseScalarValue}, - FieldError, + FieldError, Spanning, }; /// Whether an item is deprecated, with context. @@ -202,7 +202,7 @@ pub struct Argument<'a, S> { #[doc(hidden)] pub arg_type: Type<'a>, #[doc(hidden)] - pub default_value: Option>, + pub default_value: Option>>, } impl<'a, S> Argument<'a, S> { @@ -739,7 +739,7 @@ impl<'a, S> Argument<'a, S> { /// Overwrites any previously set default value. #[must_use] pub fn default_value(mut self, val: InputValue) -> Self { - self.default_value = Some(val); + self.default_value = Some(Spanning::unlocated(val)); self } } diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 618227462..98729cae2 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -386,7 +386,7 @@ impl<'a, S: ScalarValue + 'a> Argument<'a, S> { #[graphql(name = "defaultValue")] fn default_value_(&self) -> Option { - self.default_value.as_ref().map(ToString::to_string) + self.default_value.as_ref().map(|v| v.item.to_string()) } } diff --git a/juniper/src/schema/translate/graphql_parser.rs b/juniper/src/schema/translate/graphql_parser.rs index 7612b610b..db8dbdc17 100644 --- a/juniper/src/schema/translate/graphql_parser.rs +++ b/juniper/src/schema/translate/graphql_parser.rs @@ -89,7 +89,7 @@ impl GraphQLParserTranslator { default_value: input .default_value .as_ref() - .map(|x| GraphQLParserTranslator::translate_value(x)), + .map(|x| GraphQLParserTranslator::translate_value(&x.item)), directives: vec![], } } diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs index 28fa6e904..42940a41e 100644 --- a/juniper/src/types/async_await.rs +++ b/juniper/src/types/async_await.rs @@ -245,7 +245,15 @@ where m.item .iter() .filter_map(|(k, v)| { - v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) + let value = v.item.clone().into_const(exec_vars)?; + Some(( + k.item, + Spanning { + item: value, + start: v.start, + end: v.end, + }, + )) }) .collect() }), diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index d1debc4b9..5eb531801 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -68,13 +68,13 @@ pub enum TypeKind { /// Field argument container #[derive(Debug)] pub struct Arguments<'a, S = DefaultScalarValue> { - args: Option>>, + args: Option>>>, } impl<'a, S> Arguments<'a, S> { #[doc(hidden)] pub fn new( - mut args: Option>>, + mut args: Option>>>, meta_args: &'a Option>>, ) -> Self where @@ -117,10 +117,16 @@ impl<'a, S> Arguments<'a, S> { self.args .as_ref() .and_then(|args| args.get(name)) + .map(|spanning| &spanning.item) .map(InputValue::convert) .transpose() .map_err(IntoFieldError::into_field_error) } + + /// Gets a direct reference to the spanned argument input value + pub fn get_input_value(&self, name: &str) -> Option<&Spanning>> { + self.args.as_ref().and_then(|args| args.get(name)) + } } /// Primary trait used to resolve GraphQL values. @@ -473,7 +479,15 @@ where m.item .iter() .filter_map(|(k, v)| { - v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) + let value = v.item.clone().into_const(exec_vars)?; + Some(( + k.item, + Spanning { + item: value, + start: v.start, + end: v.end, + }, + )) }) .collect() }), diff --git a/juniper/src/types/subscriptions.rs b/juniper/src/types/subscriptions.rs index 63fa14e44..7c5aea2ee 100644 --- a/juniper/src/types/subscriptions.rs +++ b/juniper/src/types/subscriptions.rs @@ -317,7 +317,15 @@ where m.item .iter() .filter_map(|(k, v)| { - v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) + let value = v.item.clone().into_const(exec_vars)?; + Some(( + k.item, + Spanning { + item: value, + start: v.start, + end: v.end, + }, + )) }) .collect() }),