Skip to content

Commit f66296d

Browse files
authored
Replace ScalarValue::Visitor with DeserializeOwned requirement (#985)
- remove `Serialize` impl from `#[derive(GraphQLScalarValue)]` macro expansion
1 parent 168114f commit f66296d

File tree

14 files changed

+577
-765
lines changed

14 files changed

+577
-765
lines changed

integration_tests/juniper_tests/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ juniper_subscriptions = { path = "../../juniper_subscriptions" }
1212

1313
[dev-dependencies]
1414
async-trait = "0.1.39"
15+
serde = { version = "1.0", features = ["derive"] }
1516
serde_json = "1.0"
1617
fnv = "1.0"
1718
tokio = { version = "1", features = ["rt", "macros", "time"] }

integration_tests/juniper_tests/src/custom_scalar.rs

+78-87
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
use std::{fmt, pin::Pin};
1+
use std::{convert::TryInto as _, fmt, pin::Pin};
22

33
use futures::{stream, Stream};
44
use juniper::{
55
execute, graphql_object, graphql_scalar, graphql_subscription,
66
parser::{ParseError, ScalarToken, Spanning, Token},
7-
serde::de,
7+
serde::{de, Deserialize, Deserializer, Serialize},
88
EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult,
99
RootNode, ScalarValue, Value, Variables,
1010
};
1111

12-
#[derive(GraphQLScalarValue, Clone, Debug, PartialEq)]
12+
#[derive(GraphQLScalarValue, Clone, Debug, PartialEq, Serialize)]
13+
#[serde(untagged)]
1314
pub(crate) enum MyScalarValue {
1415
Int(i32),
1516
Long(i64),
@@ -19,18 +20,16 @@ pub(crate) enum MyScalarValue {
1920
}
2021

2122
impl ScalarValue for MyScalarValue {
22-
type Visitor = MyScalarValueVisitor;
23-
2423
fn as_int(&self) -> Option<i32> {
25-
match *self {
26-
Self::Int(ref i) => Some(*i),
24+
match self {
25+
Self::Int(i) => Some(*i),
2726
_ => None,
2827
}
2928
}
3029

3130
fn as_string(&self) -> Option<String> {
32-
match *self {
33-
Self::String(ref s) => Some(s.clone()),
31+
match self {
32+
Self::String(s) => Some(s.clone()),
3433
_ => None,
3534
}
3635
}
@@ -43,100 +42,92 @@ impl ScalarValue for MyScalarValue {
4342
}
4443

4544
fn as_str(&self) -> Option<&str> {
46-
match *self {
47-
Self::String(ref s) => Some(s.as_str()),
45+
match self {
46+
Self::String(s) => Some(s.as_str()),
4847
_ => None,
4948
}
5049
}
5150

5251
fn as_float(&self) -> Option<f64> {
53-
match *self {
54-
Self::Int(ref i) => Some(f64::from(*i)),
55-
Self::Float(ref f) => Some(*f),
52+
match self {
53+
Self::Int(i) => Some(f64::from(*i)),
54+
Self::Float(f) => Some(*f),
5655
_ => None,
5756
}
5857
}
5958

6059
fn as_boolean(&self) -> Option<bool> {
61-
match *self {
62-
Self::Boolean(ref b) => Some(*b),
60+
match self {
61+
Self::Boolean(b) => Some(*b),
6362
_ => None,
6463
}
6564
}
6665
}
6766

68-
#[derive(Debug, Default)]
69-
pub(crate) struct MyScalarValueVisitor;
70-
71-
impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
72-
type Value = MyScalarValue;
73-
74-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
75-
formatter.write_str("a valid input value")
76-
}
77-
78-
fn visit_bool<E>(self, value: bool) -> Result<MyScalarValue, E> {
79-
Ok(MyScalarValue::Boolean(value))
80-
}
81-
82-
fn visit_i32<E>(self, value: i32) -> Result<MyScalarValue, E>
83-
where
84-
E: de::Error,
85-
{
86-
Ok(MyScalarValue::Int(value))
87-
}
88-
89-
fn visit_i64<E>(self, value: i64) -> Result<MyScalarValue, E>
90-
where
91-
E: de::Error,
92-
{
93-
if value <= i64::from(i32::max_value()) {
94-
self.visit_i32(value as i32)
95-
} else {
96-
Ok(MyScalarValue::Long(value))
97-
}
98-
}
99-
100-
fn visit_u32<E>(self, value: u32) -> Result<MyScalarValue, E>
101-
where
102-
E: de::Error,
103-
{
104-
if value <= i32::max_value() as u32 {
105-
self.visit_i32(value as i32)
106-
} else {
107-
self.visit_u64(value as u64)
108-
}
109-
}
110-
111-
fn visit_u64<E>(self, value: u64) -> Result<MyScalarValue, E>
112-
where
113-
E: de::Error,
114-
{
115-
if value <= i64::max_value() as u64 {
116-
self.visit_i64(value as i64)
117-
} else {
118-
// Browser's JSON.stringify serialize all numbers having no
119-
// fractional part as integers (no decimal point), so we
120-
// must parse large integers as floating point otherwise
121-
// we would error on transferring large floating point
122-
// numbers.
123-
Ok(MyScalarValue::Float(value as f64))
67+
impl<'de> Deserialize<'de> for MyScalarValue {
68+
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
69+
struct Visitor;
70+
71+
impl<'de> de::Visitor<'de> for Visitor {
72+
type Value = MyScalarValue;
73+
74+
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
75+
f.write_str("a valid input value")
76+
}
77+
78+
fn visit_bool<E: de::Error>(self, b: bool) -> Result<Self::Value, E> {
79+
Ok(MyScalarValue::Boolean(b))
80+
}
81+
82+
fn visit_i32<E: de::Error>(self, n: i32) -> Result<Self::Value, E> {
83+
Ok(MyScalarValue::Int(n))
84+
}
85+
86+
fn visit_i64<E: de::Error>(self, b: i64) -> Result<Self::Value, E> {
87+
if b <= i64::from(i32::MAX) {
88+
self.visit_i32(b.try_into().unwrap())
89+
} else {
90+
Ok(MyScalarValue::Long(b))
91+
}
92+
}
93+
94+
fn visit_u32<E: de::Error>(self, n: u32) -> Result<Self::Value, E> {
95+
if n <= i32::MAX as u32 {
96+
self.visit_i32(n.try_into().unwrap())
97+
} else {
98+
self.visit_u64(n.into())
99+
}
100+
}
101+
102+
fn visit_u64<E: de::Error>(self, n: u64) -> Result<Self::Value, E> {
103+
if n <= i64::MAX as u64 {
104+
self.visit_i64(n.try_into().unwrap())
105+
} else {
106+
// Browser's `JSON.stringify()` serializes all numbers
107+
// having no fractional part as integers (no decimal point),
108+
// so we must parse large integers as floating point,
109+
// otherwise we would error on transferring large floating
110+
// point numbers.
111+
// TODO: Use `FloatToInt` conversion once stabilized:
112+
// https://github.com/rust-lang/rust/issues/67057
113+
Ok(MyScalarValue::Float(n as f64))
114+
}
115+
}
116+
117+
fn visit_f64<E: de::Error>(self, f: f64) -> Result<Self::Value, E> {
118+
Ok(MyScalarValue::Float(f))
119+
}
120+
121+
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
122+
self.visit_string(s.into())
123+
}
124+
125+
fn visit_string<E: de::Error>(self, s: String) -> Result<Self::Value, E> {
126+
Ok(MyScalarValue::String(s))
127+
}
124128
}
125-
}
126-
127-
fn visit_f64<E>(self, value: f64) -> Result<MyScalarValue, E> {
128-
Ok(MyScalarValue::Float(value))
129-
}
130-
131-
fn visit_str<E>(self, value: &str) -> Result<MyScalarValue, E>
132-
where
133-
E: de::Error,
134-
{
135-
self.visit_string(value.into())
136-
}
137129

138-
fn visit_string<E>(self, value: String) -> Result<MyScalarValue, E> {
139-
Ok(MyScalarValue::String(value))
130+
de.deserialize_any(Visitor)
140131
}
141132
}
142133

@@ -169,7 +160,7 @@ struct TestType;
169160
#[graphql_object(scalar = MyScalarValue)]
170161
impl TestType {
171162
fn long_field() -> i64 {
172-
i64::from(i32::max_value()) + 1
163+
i64::from(i32::MAX) + 1
173164
}
174165

175166
fn long_with_arg(long_arg: i64) -> i64 {

juniper/CHANGELOG.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
## Breaking Changes
44

5-
- `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971)
6-
- `rename = "<policy>"` attribute's argument renamed to `rename_all = "<policy>"`. ([#971](https://github.com/graphql-rust/juniper/pull/971)
7-
- Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979)
5+
- Replaced `Visitor` associated type with `DeserializeOwned` requirement in `ScalarValue` trait. ([#985](https://github.com/graphql-rust/juniper/pull/985))
6+
- Removed `Serialize` implementation from `#[derive(GraphQLScalarValue)]`macro, now should be provided explicitly. ([#985](https://github.com/graphql-rust/juniper/pull/985))
7+
- `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971))
8+
- `rename = "<policy>"` attribute's argument renamed to `rename_all = "<policy>"`. ([#971](https://github.com/graphql-rust/juniper/pull/971))
9+
- Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979))
810

911
## Features
1012

1113
- Support using Rust array as GraphQL list. ([#966](https://github.com/graphql-rust/juniper/pull/966), [#918](https://github.com/graphql-rust/juniper/issues/918))
1214
- Expose `GraphQLRequest` fields. ([#750](https://github.com/graphql-rust/juniper/issues/750))
13-
- `#[graphql_interface]` macro now supports `rename_all = "<policy>"` argument influencing its fields and their arguments. ([#971](https://github.com/graphql-rust/juniper/pull/971)
15+
- `#[graphql_interface]` macro now supports `rename_all = "<policy>"` argument influencing its fields and their arguments. ([#971](https://github.com/graphql-rust/juniper/pull/971))
1416

1517
## Fixes
1618

0 commit comments

Comments
 (0)