Skip to content

Commit d3e1697

Browse files
authored
Add ScalarValue::downcast_type() method for GraphQLScalar parsing optimization (#1329, #819)
- rename `TryScalarValueTo` trait as `TryToPrimitive` - introduce `FromScalarValue` conversion trait - generate `FromScalarValue` impls in `#[derive(GraphQLScalar)]` and `#[graphql_scalar]` macro expansions - rebase `ScalarValue::downcast_type()` method to `FromScalarValue` trait
1 parent 689f399 commit d3e1697

File tree

14 files changed

+496
-187
lines changed

14 files changed

+496
-187
lines changed

book/src/types/scalars.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl UserId {
115115
/// Checks whether the [`InputValue`] is a [`String`] beginning with `id: ` and strips it.
116116
fn from_input(
117117
input: &str,
118-
// ^^^^ any concrete type having `TryScalarValueTo` implementation could be used
118+
// ^^^^ any concrete type having `FromScalarValue` implementation could be used
119119
) -> Result<Self, Box<str>> {
120120
// ^^^^^^^^ must implement `IntoFieldError`
121121
input
@@ -145,7 +145,7 @@ impl UserId {
145145
// ^^^^^^ for generic argument using `Scalar` transparent wrapper is required,
146146
// otherwise Rust won't be able to infer the required type
147147
) -> Self {
148-
// ^^^^ if the result is infallible, it's OK to not use `Result`
148+
// ^^^^ if the result is infallible, it's OK to omit `Result`
149149
Self(
150150
input
151151
.try_to_int().map(|i| i.to_string())

juniper/CHANGELOG.md

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,18 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
7171
- Removed `as_float_value()`, `as_string_value()` and `as_scalar_value()` methods (use `as_scalar()` method and then `ScalarValue` methods instead).
7272
- `InputValue` enum: ([#1327])
7373
- Removed `as_float_value()`, `as_int_value()`, `as_string_value()` and `as_scalar_value()` methods (use `as_scalar()` method and then `ScalarValue` methods instead).
74-
- `ScalarValue` trait: ([#1327])
75-
- Switched from `From` conversions to `TryScalarValueTo` conversions.
76-
- Made to require `TryScalarValueTo` conversions for `bool`, `f64`, `i32`, `String` and `&str` types (could be derived with `#[value(<conversion>)]` attributes of `#[derive(ScalarValue)]` macro).
77-
- Made to require `TryInto<String>` conversion (could be derived with `derive_more::TryInto`).
78-
- Made `is_type()` method required and to accept `Any` type.
79-
- Renamed `as_bool()` method as `try_to_bool()` and made it defined by default as `TryScalarValueTo<bool>` alias.
80-
- Renamed `as_float()` method as `try_to_float()` and made it defined by default as `TryScalarValueTo<f64>` alias.
81-
- Renamed `as_int()` method as `try_to_int()` and made it defined by default as `TryScalarValueTo<i32>` alias.
82-
- Renamed `as_string()` method as `try_to_string()` and made it defined by default as `TryScalarValueTo<String>` alias.
83-
- Renamed `as_str()` method as `try_as_str()` and made it defined by default as `TryScalarValueTo<&str>` alias.
84-
- Renamed `into_string()` method as `try_into_string()` and made it defined by default as `TryInto<String>` alias.
74+
- `ScalarValue` trait:
75+
- Switched from `From` conversions to `TryToPrimitive` and `FromScalarValue` conversions. ([#1327], [#1329])
76+
- Made to require `TryToPrimitive` conversions for `bool`, `f64`, `i32`, `String` and `&str` types (could be derived with `#[value(<conversion>)]` attributes of `#[derive(ScalarValue)]` macro). ([#1327], [#1329])
77+
- Made to require `TryInto<String>` conversion (could be derived with `derive_more::TryInto`). ([#1327])
78+
- Made `is_type()` method required and to accept `Any` type. ([#1327])
79+
- Renamed `as_bool()` method as `try_to_bool()` and made it defined by default as `TryToPrimitive<bool>` alias. ([#1327])
80+
- Renamed `as_float()` method as `try_to_float()` and made it defined by default as `TryToPrimitive<f64>` alias. ([#1327])
81+
- Renamed `as_int()` method as `try_to_int()` and made it defined by default as `TryToPrimitive<i32>` alias. ([#1327])
82+
- Renamed `as_string()` method as `try_to_string()` and made it defined by default as `TryToPrimitive<String>` alias. ([#1327])
83+
- Renamed `as_str()` method as `try_as_str()` and made it defined by default as `TryToPrimitive<&str>` alias. ([#1327])
84+
- Renamed `into_string()` method as `try_into_string()` and made it defined by default as `TryInto<String>` alias. ([#1327])
85+
- Required new `downcast_type::<T>()` method (could be derived with `#[derive(ScalarValue)]` macro). ([#1329])
8586
- `#[derive(ScalarValue)]` macro: ([#1327])
8687
- Renamed `#[value(as_bool)]` attribute as `#[value(to_bool)]`.
8788
- Renamed `#[value(as_float)]` attribute as `#[value(to_float)]`.
@@ -90,9 +91,9 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
9091
- Removed `#[value(into_string)]` attribute.
9192
- Removed `#[value(allow_missing_attributes)]` attribute (now attributes can always be omitted).
9293
- `From` and `Display` implementations are not derived anymore (recommended way is to use [`derive_more` crate] for this).
93-
- `#[derive(GraphQLScalar)]` and `#[graphql_scalar]` macros: ([#1327])
94-
- Made provided `from_input()` function to accept `ScalarValue` (or anything `TryScalarValueTo`-convertible) directly instead of `InputValue`.
95-
- Removed `LocalBoxFuture` usage from `http::tests::WsIntegration` trait. ([todo])
94+
- `#[derive(GraphQLScalar)]` and `#[graphql_scalar]` macros:
95+
- Made provided `from_input()` function to accept `ScalarValue` (or anything `FromScalarValue`-convertible) directly instead of `InputValue`. ([#1327])
96+
- Removed `LocalBoxFuture` usage from `http::tests::WsIntegration` trait. ([4b14c015])
9697

9798
### Added
9899

@@ -109,14 +110,16 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
109110
- `String` scalar implementation for `arcstr::ArcStr`. ([#1247])
110111
- `String` scalar implementation for `compact_str::CompactString`. ([20609366])
111112
- `IntoValue` and `IntoInputValue` conversion traits allowing to work around orphan rules with custom `ScalarValue`. ([#1324])
113+
- `FromScalarValue` conversion trait. ([#1329])
114+
- `TryToPrimitive` conversion trait aiding `ScalarValue` trait. ([#1327], [#1329])
112115
- `ScalarValue` trait:
113116
- `from_displayable()` method allowing to specialize `ScalarValue` conversion from custom string types. ([#1324], [#819])
114-
- `try_to::<T>()` method defined by default as `TryScalarValueTo<T>` alias. ([#1327])
115-
- `TryScalarValueTo` conversion trait aiding `ScalarValue` trait. ([#1327])
116-
- `#[derive(GraphQLScalar)]` and `#[graphql_scalar]` macros: ([#1327])
117-
- Support for specifying concrete types as input argument in provided `from_input()` function.
118-
- Support for non-`Result` return type in provided `from_input()` function.
119-
- `Scalar` transparent wrapper for aiding type inference in `from_input()` function when input argument is generic `ScalarValue`.
117+
- `try_to::<T>()` method defined by default as `FromScalarValue<T>` alias. ([#1327], [#1329])
118+
- `#[derive(GraphQLScalar)]` and `#[graphql_scalar]` macros:
119+
- Support for specifying concrete types as input argument in provided `from_input()` function. ([#1327])
120+
- Support for non-`Result` return type in provided `from_input()` function. ([#1327])
121+
- `Scalar` transparent wrapper for aiding type inference in `from_input()` function when input argument is generic `ScalarValue`. ([#1327])
122+
- Generating of `FromScalarValue` implementation. ([#1329])
120123

121124
### Changed
122125

@@ -144,9 +147,10 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
144147
[#1324]: /../../pull/1324
145148
[#1326]: /../../pull/1326
146149
[#1327]: /../../pull/1327
150+
[#1329]: /../../pull/1329
147151
[1b1fc618]: /../../commit/1b1fc61879ffdd640d741e187dc20678bf7ab295
148152
[20609366]: /../../commit/2060936635609b0186d46d8fbd06eb30fce660e3
149-
[todo]: /../../commit/todo
153+
[4b14c015]: /../../commit/4b14c015018d31cb6df848efdee24d96416b76d9
150154

151155

152156

juniper/src/ast.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,24 +200,18 @@ pub type Document<'a, S> = [Definition<'a, S>];
200200
#[doc(hidden)]
201201
pub type OwnedDocument<'a, S> = Vec<Definition<'a, S>>;
202202

203-
/// Parsing of an unstructured input value into a Rust data type.
203+
/// Parsing of an unstructured [`InputValue`] into a Rust data type.
204204
///
205-
/// The conversion _can_ fail, and must in that case return [`Err`]. Thus not
206-
/// restricted in the definition of this trait, the returned [`Err`] should be
207-
/// convertible with [`IntoFieldError`] to fit well into the library machinery.
205+
/// The conversion _can_ fail, and must in that case return an [`Err`]. Thus, not restricted in the
206+
/// definition of this trait, the returned [`Err`] should be convertible with the [`IntoFieldError`]
207+
/// trait to fit well into the library machinery.
208208
///
209-
/// Implemented automatically by the convenience proc macro [`graphql_scalar!`]
210-
/// or by deriving `GraphQLEnum`.
211-
///
212-
/// Must be implemented manually when manually exposing new enums or scalars.
213-
///
214-
/// [`graphql_scalar!`]: macro@crate::graphql_scalar
215209
/// [`IntoFieldError`]: crate::IntoFieldError
216210
pub trait FromInputValue<S = DefaultScalarValue>: Sized {
217211
/// Type of this conversion error.
218212
///
219-
/// Thus not restricted, it should be convertible with [`IntoFieldError`] to
220-
/// fit well into the library machinery.
213+
/// Thus, not restricted, it should be convertible with the [`IntoFieldError`] trait to fit well
214+
/// into the library machinery.
221215
///
222216
/// [`IntoFieldError`]: crate::IntoFieldError
223217
type Error;
@@ -615,13 +609,13 @@ impl<'a, S> VariableDefinitions<'a, S> {
615609
}
616610

617611
#[cfg(test)]
618-
mod tests {
612+
mod spec_input_value_fmt {
619613
use crate::graphql_input_value;
620614

621615
use super::InputValue;
622616

623617
#[test]
624-
fn test_input_value_fmt() {
618+
fn correct() {
625619
let value: InputValue = graphql_input_value!(null);
626620
assert_eq!(value.to_string(), "null");
627621

juniper/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ pub use crate::{
103103
},
104104
validation::RuleError,
105105
value::{
106-
AnyExt, DefaultScalarValue, IntoValue, Object, ParseScalarResult, ParseScalarValue, Scalar,
107-
ScalarValue, TryScalarValueTo, Value, WrongInputScalarTypeError,
106+
AnyExt, DefaultScalarValue, FromScalarValue, IntoValue, Object, ParseScalarResult,
107+
ParseScalarValue, Scalar, ScalarValue, TryToPrimitive, Value, WrongInputScalarTypeError,
108108
},
109109
};
110110

juniper/src/types/pointers.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
async_await::GraphQLValueAsync,
1212
base::{Arguments, GraphQLType, GraphQLValue},
1313
},
14-
value::ScalarValue,
14+
value::{FromScalarValue, ScalarValue},
1515
};
1616

1717
impl<S, T> GraphQLType<S> for Box<T>
@@ -87,6 +87,18 @@ where
8787
}
8888
}
8989

90+
impl<'s, T, S> FromScalarValue<'s, S> for Box<T>
91+
where
92+
S: ScalarValue,
93+
T: FromScalarValue<'s, S> + 's,
94+
{
95+
type Error = T::Error;
96+
97+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
98+
T::from_scalar_value(v).map(Self::new)
99+
}
100+
}
101+
90102
impl<T, S> FromInputValue<S> for Box<T>
91103
where
92104
S: ScalarValue,
@@ -275,6 +287,18 @@ where
275287
}
276288
}
277289

290+
impl<'s, T, S> FromScalarValue<'s, S> for Arc<T>
291+
where
292+
S: ScalarValue,
293+
T: FromScalarValue<'s, S> + 's,
294+
{
295+
type Error = T::Error;
296+
297+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
298+
T::from_scalar_value(v).map(Self::new)
299+
}
300+
}
301+
278302
impl<T, S> FromInputValue<S> for Arc<T>
279303
where
280304
S: ScalarValue,

juniper/src/types/scalars.rs

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::{char, convert::identity, marker::PhantomData, rc::Rc, thread::JoinHandle};
1+
use std::{char, marker::PhantomData, rc::Rc, thread::JoinHandle};
22

33
use derive_more::with_trait::{Deref, Display, From, Into};
44
use serde::{Deserialize, Serialize};
55

66
use crate::{
7-
GraphQLScalar, Scalar,
7+
GraphQLScalar, IntoFieldError, Scalar,
88
ast::{InputValue, Selection, ToInputValue},
99
executor::{ExecutionResult, Executor, Registry},
1010
graphql_scalar,
@@ -16,7 +16,10 @@ use crate::{
1616
base::{GraphQLType, GraphQLValue},
1717
subscriptions::GraphQLSubscriptionValue,
1818
},
19-
value::{ParseScalarResult, ScalarValue, Value, WrongInputScalarTypeError},
19+
value::{
20+
FromScalarValue, ParseScalarResult, ScalarValue, TryToPrimitive, Value,
21+
WrongInputScalarTypeError,
22+
},
2023
};
2124

2225
/// An ID as defined by the GraphQL specification
@@ -56,12 +59,23 @@ impl ID {
5659
}
5760

5861
#[graphql_scalar]
59-
#[graphql(with = impl_string_scalar, from_input_with = identity::<String>)]
62+
#[graphql(with = impl_string_scalar, from_input_with = __builtin)]
6063
type String = std::string::String;
6164

6265
mod impl_string_scalar {
6366
use super::*;
6467

68+
impl<'s, S> FromScalarValue<'s, S> for String
69+
where
70+
S: TryToPrimitive<'s, Self, Error: IntoFieldError<S>> + 's,
71+
{
72+
type Error = S::Error;
73+
74+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
75+
v.try_to_primitive()
76+
}
77+
}
78+
6579
pub(super) fn to_output<S: ScalarValue>(v: &str) -> Value<S> {
6680
Value::scalar(v.to_owned())
6781
}
@@ -176,14 +190,20 @@ type ArcStr = arcstr::ArcStr;
176190

177191
mod impl_arcstr_scalar {
178192
use super::ArcStr;
179-
use crate::{IntoValue as _, ScalarValue, Value};
193+
use crate::{FromScalarValue, IntoValue as _, Scalar, ScalarValue, Value};
180194

181195
pub(super) fn to_output<S: ScalarValue>(v: &ArcStr) -> Value<S> {
182196
v.into_value()
183197
}
184198

185-
pub(super) fn from_input(s: &str) -> ArcStr {
186-
s.into()
199+
pub(super) fn from_input<S: ScalarValue>(
200+
v: &Scalar<S>,
201+
) -> Result<ArcStr, <&str as FromScalarValue<'_, S>>::Error> {
202+
if let Some(s) = v.downcast_type::<ArcStr>() {
203+
Ok(s.clone())
204+
} else {
205+
v.try_to::<&str>().map(ArcStr::from)
206+
}
187207
}
188208
}
189209

@@ -193,14 +213,20 @@ type CompactString = compact_str::CompactString;
193213

194214
mod impl_compactstring_scalar {
195215
use super::CompactString;
196-
use crate::{IntoValue as _, ScalarValue, Value};
216+
use crate::{FromScalarValue, IntoValue as _, Scalar, ScalarValue, Value};
197217

198218
pub(super) fn to_output<S: ScalarValue>(v: &CompactString) -> Value<S> {
199219
v.into_value()
200220
}
201221

202-
pub(super) fn from_input(s: &str) -> CompactString {
203-
s.into()
222+
pub(super) fn from_input<S: ScalarValue>(
223+
v: &Scalar<S>,
224+
) -> Result<CompactString, <&str as FromScalarValue<'_, S>>::Error> {
225+
if let Some(s) = v.downcast_type::<CompactString>() {
226+
Ok(s.clone())
227+
} else {
228+
v.try_to::<&str>().map(CompactString::from)
229+
}
204230
}
205231
}
206232

@@ -274,13 +300,35 @@ where
274300
}
275301
}
276302

303+
impl<'s, S> FromScalarValue<'s, S> for &'s str
304+
where
305+
S: TryToPrimitive<'s, Self, Error: IntoFieldError<S>> + 's,
306+
{
307+
type Error = S::Error;
308+
309+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
310+
v.try_to_primitive()
311+
}
312+
}
313+
277314
#[graphql_scalar]
278-
#[graphql(with = impl_boolean_scalar, from_input_with = identity::<Boolean>)]
315+
#[graphql(with = impl_boolean_scalar, from_input_with = __builtin)]
279316
type Boolean = bool;
280317

281318
mod impl_boolean_scalar {
282319
use super::*;
283320

321+
impl<'s, S> FromScalarValue<'s, S> for Boolean
322+
where
323+
S: TryToPrimitive<'s, Self, Error: IntoFieldError<S>> + 's,
324+
{
325+
type Error = S::Error;
326+
327+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
328+
v.try_to_primitive()
329+
}
330+
}
331+
284332
pub(super) fn to_output<S: ScalarValue>(v: &Boolean) -> Value<S> {
285333
Value::scalar(*v)
286334
}
@@ -292,12 +340,23 @@ mod impl_boolean_scalar {
292340
}
293341

294342
#[graphql_scalar]
295-
#[graphql(with = impl_int_scalar, from_input_with = identity::<Int>)]
343+
#[graphql(with = impl_int_scalar, from_input_with = __builtin)]
296344
type Int = i32;
297345

298346
mod impl_int_scalar {
299347
use super::*;
300348

349+
impl<'s, S> FromScalarValue<'s, S> for Int
350+
where
351+
S: TryToPrimitive<'s, Self, Error: IntoFieldError<S>> + 's,
352+
{
353+
type Error = S::Error;
354+
355+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
356+
v.try_to_primitive()
357+
}
358+
}
359+
301360
pub(super) fn to_output<S: ScalarValue>(v: &Int) -> Value<S> {
302361
Value::scalar(*v)
303362
}
@@ -314,12 +373,23 @@ mod impl_int_scalar {
314373
}
315374

316375
#[graphql_scalar]
317-
#[graphql(with = impl_float_scalar, from_input_with = identity::<Float>)]
376+
#[graphql(with = impl_float_scalar, from_input_with = __builtin)]
318377
type Float = f64;
319378

320379
mod impl_float_scalar {
321380
use super::*;
322381

382+
impl<'s, S> FromScalarValue<'s, S> for Float
383+
where
384+
S: TryToPrimitive<'s, Self, Error: IntoFieldError<S>> + 's,
385+
{
386+
type Error = S::Error;
387+
388+
fn from_scalar_value(v: &'s S) -> Result<Self, Self::Error> {
389+
v.try_to_primitive()
390+
}
391+
}
392+
323393
pub(super) fn to_output<S: ScalarValue>(v: &Float) -> Value<S> {
324394
Value::scalar(*v)
325395
}

juniper/src/value/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub(crate) use self::scalar::ScalarValueFmt;
1010
pub use self::{
1111
object::Object,
1212
scalar::{
13-
AnyExt, DefaultScalarValue, ParseScalarResult, ParseScalarValue, Scalar, ScalarValue,
14-
TryScalarValueTo, WrongInputScalarTypeError,
13+
AnyExt, DefaultScalarValue, FromScalarValue, ParseScalarResult, ParseScalarValue, Scalar,
14+
ScalarValue, TryToPrimitive, WrongInputScalarTypeError,
1515
},
1616
};
1717
use crate::{

0 commit comments

Comments
 (0)