Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions juniper/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
### BC Breaks

- Made `executor::FieldPath` private. ([#1415])
- Changed the field names of `Object` from `String` to `ArcStr`: ([#1416])
- `Object::add_field()` takes a name of any type that converts into `ArcStr`.
- `Object::iter()`, `Object::iter_mut()`, and the `IntoIterator` implementation of `Object` return `ArcStr` names.

[#1415]: /../../pull/1415
[#1416]: /../../pull/1416



Expand Down
1 change: 1 addition & 0 deletions juniper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ chrono-tz = { version = "0.10", default-features = false, optional = true }
compact_str = "0.10"
derive_more = { version = "2.0", features = ["debug", "deref", "display", "error", "from", "into", "into_iterator", "try_into"] }
fnv = "1.0.5"
foldhash = "0.2"
futures = { version = "0.3.22", features = ["alloc"], default-features = false }
graphql-parser = { version = "0.4", optional = true }
indexmap = { version = "2.0", features = ["serde"] }
Expand Down
2 changes: 1 addition & 1 deletion juniper/src/integrations/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl<T: Serialize> Serialize for Object<T> {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
let mut map = ser.serialize_map(Some(self.field_count()))?;
for (f, v) in self.iter() {
map.serialize_key(f)?;
map.serialize_key(f.as_str())?;
map.serialize_value(v)?;
}
map.end()
Expand Down
2 changes: 1 addition & 1 deletion juniper/src/tests/subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ fn create_and_execute(query: String) -> Result<(Vec<String>, Vec<StreamItems>),
let Value::Scalar(stream) = value else {
panic!("subscription field `{name}` should resolve to a stream");
};
names.push(name);
names.push(name.to_string());
items.push(run(stream.collect::<Vec<_>>()));
}
Ok((names, items))
Expand Down
25 changes: 12 additions & 13 deletions juniper/src/types/async_await.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{future, ops::ControlFlow};

use arcstr::ArcStr;
use auto_enums::enum_derive;
use futures::stream::{FuturesOrdered, StreamExt as _};

Expand All @@ -12,7 +13,9 @@ use crate::{

use crate::BoxFuture;

use super::base::{Arguments, GraphQLType, GraphQLValue, is_excluded, merge_key_into};
use super::base::{
Arguments, GraphQLType, GraphQLValue, is_excluded, response_key, typename_response_key,
};

/// Extension of [`GraphQLValue`] trait with asynchronous queries/mutations resolvers.
///
Expand Down Expand Up @@ -162,7 +165,7 @@ where

#[derive(Debug)]
struct AsyncField<S> {
name: String,
name: ArcStr,
value: Option<Value<S>>,
}

Expand Down Expand Up @@ -220,7 +223,7 @@ where

if f.name.item == "__typename" {
object.add_field(
response_name,
typename_response_key(f),
Value::Scalar(instance.concrete_type_name(executor.context(), info).into()),
);
continue;
Expand Down Expand Up @@ -258,7 +261,7 @@ where
let pos = span.start;
let is_non_null = meta_field.field_type.is_non_null();

let response_name = response_name.to_string();
let response_key = response_key(f, meta_field);
async_values.push_back(AsyncValueFuture::Field1(async move {
// TODO: implement custom future type instead of
// two-level boxing.
Expand All @@ -283,7 +286,7 @@ where
}
};
AsyncValue::Field(AsyncField {
name: response_name,
name: response_key,
value,
})
}));
Expand Down Expand Up @@ -344,7 +347,7 @@ where
// a `Value::Null`.
async_values.push_back(AsyncValueFuture::Field2(future::ready(
AsyncValue::Field(AsyncField {
name: String::new(), // doesn't matter here
name: ArcStr::new(), // doesn't matter here
value: None,
}),
)));
Expand Down Expand Up @@ -403,7 +406,7 @@ where
// a `Value::Null`.
async_values.push_back(AsyncValueFuture::Field2(future::ready(
AsyncValue::Field(AsyncField {
name: String::new(), // doesn't matter here
name: ArcStr::new(), // doesn't matter here
value: None,
}),
)));
Expand Down Expand Up @@ -460,15 +463,11 @@ where
AsyncValue::Field(AsyncField {
name,
value: Some(value),
}) => merge_key_into(object, &name, value),
}) => object.merge_field(name, value),
AsyncValue::Field(AsyncField { value: None, .. }) | AsyncValue::Nested(Value::Null) => {
return ControlFlow::Break(());
}
AsyncValue::Nested(Value::Object(nested)) => {
for (name, value) in nested {
merge_key_into(object, &name, value);
}
}
AsyncValue::Nested(Value::Object(nested)) => object.merge(nested),
AsyncValue::Nested(_) => unreachable!(),
}
}
Expand Down
64 changes: 21 additions & 43 deletions juniper/src/types/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use indexmap::IndexMap;

use crate::{
FieldResult, GraphQLEnum, IntoFieldError,
ast::{Directive, FromInputValue, InputValue, Selection},
ast::{self, Directive, FromInputValue, InputValue, Selection},
executor::{ExecutionResult, Executor, Registry, Variables},
parser::Spanning,
schema::meta::{Argument, MetaType},
schema::meta::{Argument, Field, MetaType},
value::{DefaultScalarValue, Object, ScalarValue, Value},
};

Expand Down Expand Up @@ -443,7 +443,7 @@ where

if f.name.item == "__typename" {
result.add_field(
response_name,
typename_response_key(f),
Value::Scalar(instance.concrete_type_name(executor.context(), info).into()),
);
continue;
Expand All @@ -456,6 +456,7 @@ where
meta_type.name(),
)
});
let response_key = response_key(f, meta_field);

let exec_vars = executor.variables();

Expand Down Expand Up @@ -489,15 +490,15 @@ where
sub_exec.push_non_null_violation_at(meta_type, f.name.item, span.start);
return false;
}
Ok(v) => merge_key_into(result, response_name, v),
Ok(v) => result.merge_field(response_key, v),
Err(e) => {
sub_exec.push_error_at(e, span.start);

if meta_field.field_type.is_non_null() {
return false;
}

result.add_field(response_name, Value::null());
result.add_field(response_key, Value::null());
}
}
}
Expand Down Expand Up @@ -533,9 +534,7 @@ where
);

if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
}
result.merge(object);
} else {
if let Err(e) = sub_result {
sub_exec.push_error_at(e, span.start);
Expand Down Expand Up @@ -579,9 +578,7 @@ where
);

if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
}
result.merge(object);
} else {
if let Err(e) = sub_result {
sub_exec.push_error_at(e, span.start);
Expand Down Expand Up @@ -642,40 +639,21 @@ where
false
}

/// Merges `response_name`/`value` pair into `result`
pub(crate) fn merge_key_into<S>(result: &mut Object<S>, response_name: &str, value: Value<S>) {
if let Some(v) = result.get_mut_field_value(response_name) {
match v {
Value::Object(dest_obj) => {
if let Value::Object(src_obj) = value {
merge_maps(dest_obj, src_obj);
}
}
Value::List(dest_list) => {
if let Value::List(src_list) = value {
dest_list.iter_mut().zip(src_list).for_each(|(d, s)| {
if let Value::Object(d_obj) = d {
if let Value::Object(s_obj) = s {
merge_maps(d_obj, s_obj);
}
}
});
}
}
_ => {}
}
return;
/// Returns the response key of the `field` selection with the `meta_field` schema definition.
///
/// A field without an alias reuses the name of the `meta_field`. Cloning the [`ArcStr`] name does
/// not allocate.
pub(crate) fn response_key<S>(field: &ast::Field<'_, S>, meta_field: &Field<S>) -> ArcStr {
match &field.alias {
Some(alias) => alias.item.into(),
None => meta_field.name.clone(),
}
result.add_field(response_name, value);
}

/// Merges `src` object's fields into `dest`
fn merge_maps<S>(dest: &mut Object<S>, src: Object<S>) {
for (key, value) in src {
if dest.contains_field(&key) {
merge_key_into(dest, &key, value);
} else {
dest.add_field(key, value);
}
/// Returns the response key of the `__typename` meta-field selection.
pub(crate) fn typename_response_key<S>(field: &ast::Field<'_, S>) -> ArcStr {
match &field.alias {
Some(alias) => alias.item.into(),
None => arcstr::literal!("__typename"),
}
}
18 changes: 5 additions & 13 deletions juniper/src/types/subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
ScalarValue, Selection, Value, ValuesStream,
http::GraphQLRequest,
parser::Spanning,
types::base::{GraphQLType, GraphQLValue, is_excluded, merge_key_into},
types::base::{GraphQLType, GraphQLValue, is_excluded, response_key},
};

/// Represents the result of executing a GraphQL operation (after parsing and validating has been
Expand Down Expand Up @@ -327,7 +327,7 @@ where
Ok(Value::Null) if is_non_null => {
return Value::Null;
}
Ok(v) => merge_key_into(&mut object, response_name, v),
Ok(v) => object.merge_field(response_key(f, meta_field), v),
Err(e) => {
sub_exec.push_error_at(e, span.start);

Expand Down Expand Up @@ -361,11 +361,7 @@ where
match obj {
Ok(val) => {
match val {
Value::Object(o) => {
for (k, v) in o {
merge_key_into(&mut object, &k, v);
}
}
Value::Object(o) => object.merge(o),
// since this was a wrapper of current function,
// we'll rather get an object or nothing
_ => unreachable!(),
Expand Down Expand Up @@ -394,9 +390,7 @@ where
.await;

if let Ok(Value::Object(obj)) = sub_result {
for (k, v) in obj {
merge_key_into(&mut object, &k, v);
}
object.merge(obj);
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, span.start);
}
Expand All @@ -406,9 +400,7 @@ where
.await;

if let Ok(Value::Object(obj)) = sub_result {
for (k, v) in obj {
merge_key_into(&mut object, &k, v);
}
object.merge(obj);
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, span.start);
}
Expand Down
2 changes: 1 addition & 1 deletion juniper/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<S: Clone> ToInputValue<S> for Value<S> {
o.iter()
.map(|(k, v)| {
(
Spanning::unlocated(k.clone()),
Spanning::unlocated(k.as_str().into()),
Spanning::unlocated(v.to_input_value()),
)
})
Expand Down
Loading
Loading