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
# 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
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 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])
- Made `LookAheadMethods::children()` method to return slice instead of `Vec`. ([#1200])
- 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])
- 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
@ -145,11 +155,11 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
[#1190]: /../../pull/1190
[#1193]: /../../pull/1193
[#1199]: /../../pull/1199
[#1200]: /../../pull/1200
[#1206]: /../../pull/1206
[#1207]: /../../pull/1207
[#1208]: /../../pull/1208
[#1209]: /../../pull/1209
[#1212]: /../../pull/1212
[#1215]: /../../pull/1215
[#1227]: /../../pull/1227
[#1228]: /../../pull/1228

File diff suppressed because it is too large Load diff

View file

@ -37,7 +37,7 @@ use crate::{
pub use self::{
look_ahead::{
Applies, ConcreteLookAheadSelection, LookAheadArgument, LookAheadMethods,
Applies, LookAheadArgument, LookAheadChildren, LookAheadList, LookAheadObject,
LookAheadSelection, LookAheadValue,
},
owned_executor::OwnedExecutor,
@ -698,48 +698,38 @@ where
};
self.parent_selection_set
.and_then(|p| {
// Search the parent's fields to find this field within the set
let found_field = p.iter().find(|&x| {
match *x {
// Search the parent's fields to find this field within the selection set.
p.iter().find_map(|x| {
match x {
Selection::Field(ref field) => {
let field = &field.item;
// TODO: support excludes.
let name = field.name.item;
let alias = field.alias.as_ref().map(|a| a.item);
alias.unwrap_or(name) == field_name
(alias.unwrap_or(name) == field_name).then(|| {
LookAheadSelection::new(
look_ahead::SelectionSource::Field(field),
self.variables,
self.fragments,
)
})
}
_ => false,
Selection::FragmentSpread(_) | Selection::InlineFragment(_) => None,
}
});
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`
if let Some(selection_set) = self.current_selection_set {
for c in selection_set {
LookAheadSelection::build_from_selection_with_parent(
c,
Some(&mut ret),
self.variables,
self.fragments,
);
}
}
ret
// 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::{
Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult,
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods,
LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables,
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadChildren,
LookAheadList, LookAheadObject, LookAheadSelection, LookAheadValue, OwnedExecutor,
Registry, ValuesStream, Variables,
},
introspection::IntrospectionFormat,
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.
///
/// ```rust
/// # use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue};
/// # use juniper::{graphql_interface, graphql_object, Executor, ScalarValue};
/// #
/// #[graphql_interface]
/// // NOTICE: Specifying `ScalarValue` as existing type parameter.
@ -1754,7 +1754,7 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// [`Executor`] does so.
///
/// ```
/// # use juniper::{graphql_object, Executor, GraphQLObject, LookAheadMethods as _, ScalarValue};
/// # use juniper::{graphql_object, Executor, GraphQLObject, ScalarValue};
/// #
/// struct Human {
/// name: String,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,9 @@ struct Query;
#[graphql_object]
impl Query {
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 {
city: City {
@ -27,7 +29,10 @@ struct User {
#[graphql_object]
impl User {
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
}
}
@ -39,7 +44,10 @@ struct City {
#[graphql_object]
impl City {
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
}
}