Rework look-ahead machinery as lazy-evaluated (#1212)

- make `LookAheadValue::List` to contain new iterable `LookAheadList` type
- make `LookAheadValue::Object` to contain new iterable `LookAheadObject` type
- remove `LookAheadMethods` trait and redundant `ConcreteLookAheadSelection` struct making all APIs accessible as inherent methods on `LookAheadSelection` and `LookAheadChildren` decoupled types
- move `LookAheadMethods::child_names()` to `LookAheadChildren::names()`
- move `LookAheadMethods::has_children()` to `LookAheadChildren::is_empty()`
- move `LookAheadMethods::select_child()` to `LookAheadChildren::select()`
- move `LookAheadSelection::for_explicit_type()` to `LookAheadSelection::children_for_explicit_type()`
- make `LookAheadSelection::arguments()` returning iterator over `LookAheadArgument`
- make `LookAheadSelection::children()` returning `LookAheadChildren`

Co-authored-by: Kai Ren <tyranron@gmail.com>
This commit is contained in:
Audun Halland 2024-01-16 20:17:07 +01:00 committed by GitHub
parent e64287cfc8
commit c54137f7b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1293 additions and 1017 deletions

View file

@ -391,7 +391,7 @@ This requires to explicitly parametrize over [`ScalarValue`][3], as [`Executor`]
```rust ```rust
# extern crate juniper; # extern crate juniper;
use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue}; use juniper::{graphql_interface, graphql_object, Executor, ScalarValue};
#[graphql_interface(for = Human, Scalar = S)] // notice specifying `ScalarValue` as existing type parameter #[graphql_interface(for = Human, Scalar = S)] // notice specifying `ScalarValue` as existing type parameter
trait Character<S: ScalarValue> { trait Character<S: ScalarValue> {

View file

@ -52,12 +52,22 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
- Removed `scalar-naivetime` [Cargo feature]. - Removed `scalar-naivetime` [Cargo feature].
- Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#1081], [#528]) - Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#1081], [#528])
- Upgraded [GraphiQL] to 3.1.0 version (requires new [`graphql-transport-ws` GraphQL over WebSocket Protocol] integration on server, see `juniper_warp/examples/subscription.rs`). ([#1188], [#1193], [#1235]) - Upgraded [GraphiQL] to 3.1.0 version (requires new [`graphql-transport-ws` GraphQL over WebSocket Protocol] integration on server, see `juniper_warp/examples/subscription.rs`). ([#1188], [#1193], [#1235])
- 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])
- Removed `graphql-parser-integration` and `graphql-parser` [Cargo feature]s by merging them into `schema-language` [Cargo feature]. ([#1237]) - Removed `graphql-parser-integration` and `graphql-parser` [Cargo feature]s by merging them into `schema-language` [Cargo feature]. ([#1237])
- Renamed `RootNode::as_schema_language()` method as `RootNode::as_sdl()`. ([#1237]) - Renamed `RootNode::as_schema_language()` method as `RootNode::as_sdl()`. ([#1237])
- Renamed `RootNode::as_parser_document()` method as `RootNode::as_document()`. ([#1237]) - Renamed `RootNode::as_parser_document()` method as `RootNode::as_document()`. ([#1237])
- Reworked look-ahead machinery: ([#1212])
- Turned from eagerly-evaluated into lazy-evaluated:
- Made `LookAheadValue::List` to contain new iterable `LookAheadList` type.
- Made `LookAheadValue::Object` to contain new iterable `LookAheadObject` type.
- Removed `LookAheadMethods` trait and redundant `ConcreteLookAheadSelection` type, making all APIs accessible as inherent methods on `LookAheadSelection` and `LookAheadChildren` decoupled types:
- Moved `LookAheadMethods::child_names()` to `LookAheadChildren::names()`.
- Moved `LookAheadMethods::has_children()` to `LookAheadChildren::is_empty()`.
- Moved `LookAheadMethods::select_child()` to `LookAheadChildren::select()`.
- Moved `LookAheadSelection::for_explicit_type()` to `LookAheadSelection::children_for_explicit_type()`.
- Made `LookAheadSelection::arguments()` returning iterator over `LookAheadArgument`.
- Made `LookAheadSelection::children()` returning `LookAheadChildren`.
- Added `Span` to `Arguments` and `LookAheadArguments`. ([#1206], [#1209])
### Added ### Added
@ -145,11 +155,11 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
[#1190]: /../../pull/1190 [#1190]: /../../pull/1190
[#1193]: /../../pull/1193 [#1193]: /../../pull/1193
[#1199]: /../../pull/1199 [#1199]: /../../pull/1199
[#1200]: /../../pull/1200
[#1206]: /../../pull/1206 [#1206]: /../../pull/1206
[#1207]: /../../pull/1207 [#1207]: /../../pull/1207
[#1208]: /../../pull/1208 [#1208]: /../../pull/1208
[#1209]: /../../pull/1209 [#1209]: /../../pull/1209
[#1212]: /../../pull/1212
[#1215]: /../../pull/1215 [#1215]: /../../pull/1215
[#1227]: /../../pull/1227 [#1227]: /../../pull/1227
[#1228]: /../../pull/1228 [#1228]: /../../pull/1228

File diff suppressed because it is too large Load diff

View file

@ -37,7 +37,7 @@ use crate::{
pub use self::{ pub use self::{
look_ahead::{ look_ahead::{
Applies, ConcreteLookAheadSelection, LookAheadArgument, LookAheadMethods, Applies, LookAheadArgument, LookAheadChildren, LookAheadList, LookAheadObject,
LookAheadSelection, LookAheadValue, LookAheadSelection, LookAheadValue,
}, },
owned_executor::OwnedExecutor, owned_executor::OwnedExecutor,
@ -698,48 +698,38 @@ where
}; };
self.parent_selection_set self.parent_selection_set
.and_then(|p| { .and_then(|p| {
// Search the parent's fields to find this field within the set // Search the parent's fields to find this field within the selection set.
let found_field = p.iter().find(|&x| { p.iter().find_map(|x| {
match *x { match x {
Selection::Field(ref field) => { Selection::Field(ref field) => {
let field = &field.item; let field = &field.item;
// TODO: support excludes. // TODO: support excludes.
let name = field.name.item; let name = field.name.item;
let alias = field.alias.as_ref().map(|a| a.item); let alias = field.alias.as_ref().map(|a| a.item);
alias.unwrap_or(name) == field_name
}
_ => false,
}
});
if let Some(p) = found_field {
LookAheadSelection::build_from_selection(p, self.variables, self.fragments)
} else {
None
}
})
.unwrap_or_else(|| {
// We didn't find a field in the parent's selection matching
// this field, which means we're inside a FragmentSpread
let mut ret = LookAheadSelection {
name: field_name,
alias: None,
arguments: Vec::new(),
children: Vec::new(),
applies_for: Applies::All,
};
// Add in all the children - this will mutate `ret` (alias.unwrap_or(name) == field_name).then(|| {
if let Some(selection_set) = self.current_selection_set { LookAheadSelection::new(
for c in selection_set { look_ahead::SelectionSource::Field(field),
LookAheadSelection::build_from_selection_with_parent(
c,
Some(&mut ret),
self.variables, self.variables,
self.fragments, self.fragments,
); )
})
} }
Selection::FragmentSpread(_) | Selection::InlineFragment(_) => None,
} }
ret })
})
.unwrap_or_else(|| {
// We didn't find this field in the parent's selection matching it, which means
// we're inside a `FragmentSpread`.
LookAheadSelection::new(
look_ahead::SelectionSource::Spread {
field_name,
set: self.current_selection_set,
},
self.variables,
self.fragments,
)
}) })
} }

View file

@ -71,8 +71,9 @@ pub use crate::{
}, },
executor::{ executor::{
Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult, Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult,
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods, FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadChildren,
LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables, LookAheadList, LookAheadObject, LookAheadSelection, LookAheadValue, OwnedExecutor,
Registry, ValuesStream, Variables,
}, },
introspection::IntrospectionFormat, introspection::IntrospectionFormat,
macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult},

View file

@ -1247,7 +1247,7 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// However, this requires to explicitly parametrize over [`ScalarValue`], as [`Executor`] does so. /// However, this requires to explicitly parametrize over [`ScalarValue`], as [`Executor`] does so.
/// ///
/// ```rust /// ```rust
/// # use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue}; /// # use juniper::{graphql_interface, graphql_object, Executor, ScalarValue};
/// # /// #
/// #[graphql_interface] /// #[graphql_interface]
/// // NOTICE: Specifying `ScalarValue` as existing type parameter. /// // NOTICE: Specifying `ScalarValue` as existing type parameter.
@ -1754,7 +1754,7 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// [`Executor`] does so. /// [`Executor`] does so.
/// ///
/// ``` /// ```
/// # use juniper::{graphql_object, Executor, GraphQLObject, LookAheadMethods as _, ScalarValue}; /// # use juniper::{graphql_object, Executor, GraphQLObject, ScalarValue};
/// # /// #
/// struct Human { /// struct Human {
/// name: String, /// name: String,

View file

@ -2655,8 +2655,6 @@ mod inferred_custom_context_from_field {
} }
mod executor { mod executor {
use juniper::LookAheadMethods as _;
use super::*; use super::*;
#[graphql_interface(for = [Human, Droid], scalar = S)] #[graphql_interface(for = [Human, Droid], scalar = S)]

View file

@ -1929,8 +1929,6 @@ mod inferred_custom_context_from_field {
} }
mod executor { mod executor {
use juniper::LookAheadMethods as _;
use super::*; use super::*;
struct Human; struct Human;

View file

@ -1697,8 +1697,6 @@ mod inferred_custom_context_from_field {
} }
mod executor { mod executor {
use juniper::LookAheadMethods as _;
use super::*; use super::*;
struct Human; struct Human;

View file

@ -5,8 +5,7 @@
//! Original author of this test is [@davidpdrsn](https://github.com/davidpdrsn). //! Original author of this test is [@davidpdrsn](https://github.com/davidpdrsn).
use juniper::{ use juniper::{
graphql_object, graphql_vars, EmptyMutation, EmptySubscription, Executor, graphql_object, graphql_vars, EmptyMutation, EmptySubscription, Executor, RootNode, ScalarValue,
LookAheadMethods as _, RootNode, ScalarValue,
}; };
pub struct Context; pub struct Context;
@ -19,13 +18,21 @@ pub struct Query;
impl Query { impl Query {
fn users<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<User> { fn users<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<User> {
let lh = executor.look_ahead(); let lh = executor.look_ahead();
assert_eq!(lh.field_name(), "users"); assert_eq!(lh.field_name(), "users");
_ = lh.children();
vec![User] vec![User]
} }
fn countries<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<Country> { fn countries<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<Country> {
let lh = executor.look_ahead(); let lh = executor.look_ahead();
assert_eq!(lh.field_name(), "countries"); assert_eq!(lh.field_name(), "countries");
_ = lh.children();
vec![Country] vec![Country]
} }
} }

View file

@ -12,8 +12,10 @@ struct Query;
#[graphql_object] #[graphql_object]
impl Query { impl Query {
fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> { fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> {
assert_eq!(executor.look_ahead().field_name(), "users");
// This doesn't cause a panic. // This doesn't cause a panic.
executor.look_ahead(); _ = executor.look_ahead().children();
vec![User { vec![User {
country: Country { id: 1 }, country: Country { id: 1 },
@ -28,8 +30,10 @@ struct User {
#[graphql_object] #[graphql_object]
impl User { impl User {
fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country { fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country {
assert_eq!(executor.look_ahead().field_name(), "country");
// This panics! // This panics!
executor.look_ahead(); _ = executor.look_ahead().children();
&self.country &self.country
} }

View file

@ -10,7 +10,9 @@ struct Query;
#[graphql_object] #[graphql_object]
impl Query { impl Query {
fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> { fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> {
executor.look_ahead(); assert_eq!(executor.look_ahead().field_name(), "users");
_ = executor.look_ahead().children();
vec![User { vec![User {
city: City { city: City {
@ -27,7 +29,10 @@ struct User {
#[graphql_object] #[graphql_object]
impl User { impl User {
fn city<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &City { fn city<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &City {
executor.look_ahead(); assert_eq!(executor.look_ahead().field_name(), "city");
_ = executor.look_ahead().children();
&self.city &self.city
} }
} }
@ -39,7 +44,10 @@ struct City {
#[graphql_object] #[graphql_object]
impl City { impl City {
fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country { fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country {
executor.look_ahead(); assert_eq!(executor.look_ahead().field_name(), "country");
_ = executor.look_ahead().children();
&self.country &self.country
} }
} }