Skip to content

Commit 0ebd19a

Browse files
ilslvtyranron
andauthored
Redesign #[derive(GraphQLScalar)] and #[graphql_scalar] macros (#1017)
- `#[derive(GraphQLScalar)]`: - support generic scalars - support structs with single named field - support for overriding resolvers - `#[graphql_scalar]`: - support `transparent` argument Co-authored-by: Kai Ren <[email protected]>
1 parent 63198cd commit 0ebd19a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1922
-185
lines changed

docs/book/content/types/scalars.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Often, you might need a custom scalar that just wraps an existing type.
5252
This can be done with the newtype pattern and a custom derive, similar to how
5353
serde supports this pattern with `#[serde(transparent)]`.
5454

55-
```rust,ignore
55+
```rust
5656
# extern crate juniper;
5757
#
5858
#[derive(juniper::GraphQLScalar)]
@@ -70,7 +70,7 @@ struct User {
7070
`#[derive(GraphQLScalar)]` is mostly interchangeable with `#[graphql_scalar]`
7171
attribute:
7272

73-
```rust,ignore
73+
```rust
7474
# extern crate juniper;
7575
# use juniper::graphql_scalar;
7676
#
@@ -91,7 +91,7 @@ That's it, you can now use `UserId` in your schema.
9191

9292
The macro also allows for more customization:
9393

94-
```rust,ignore
94+
```rust
9595
# extern crate juniper;
9696
/// You can use a doc comment to specify a description.
9797
#[derive(juniper::GraphQLScalar)]
@@ -112,7 +112,7 @@ All the methods used from newtype's field can be replaced with attributes:
112112

113113
### `#[graphql(to_output_with = <fn>)]` attribute
114114

115-
```rust,ignore
115+
```rust
116116
# use juniper::{GraphQLScalar, ScalarValue, Value};
117117
#
118118
#[derive(GraphQLScalar)]
@@ -129,7 +129,7 @@ fn to_output<S: ScalarValue>(v: &Incremented) -> Value<S> {
129129

130130
### `#[graphql(from_input_with = <fn>)]` attribute
131131

132-
```rust,ignore
132+
```rust
133133
# use juniper::{GraphQLScalar, InputValue, ScalarValue};
134134
#
135135
#[derive(GraphQLScalar)]
@@ -164,7 +164,7 @@ impl UserId {
164164

165165
### `#[graphql(parse_token_with = <fn>]` or `#[graphql(parse_token(<types>)]` attributes
166166

167-
```rust,ignore
167+
```rust
168168
# use juniper::{
169169
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
170170
# ScalarValue, ScalarToken, Value
@@ -226,7 +226,7 @@ Instead of providing all custom resolvers, you can provide path to the `to_outpu
226226
Path can be simply `with = Self` (default path where macro expects resolvers to be),
227227
in case there is an impl block with custom resolvers:
228228

229-
```rust,ignore
229+
```rust
230230
# use juniper::{
231231
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
232232
# ScalarValue, ScalarToken, Value
@@ -253,7 +253,7 @@ impl StringOrInt {
253253
{
254254
v.as_string_value()
255255
.map(|s| Self::String(s.to_owned()))
256-
.or_else(|| v.as_int_value().map(|i| Self::Int(i)))
256+
.or_else(|| v.as_int_value().map(Self::Int))
257257
.ok_or_else(|| format!("Expected `String` or `Int`, found: {}", v))
258258
}
259259

@@ -271,7 +271,7 @@ impl StringOrInt {
271271

272272
Or it can be path to a module, where custom resolvers are located.
273273

274-
```rust,ignore
274+
```rust
275275
# use juniper::{
276276
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
277277
# ScalarValue, ScalarToken, Value
@@ -303,7 +303,7 @@ mod string_or_int {
303303
{
304304
v.as_string_value()
305305
.map(|s| StringOrInt::String(s.to_owned()))
306-
.or_else(|| v.as_int_value().map(|i| StringOrInt::Int(i)))
306+
.or_else(|| v.as_int_value().map(StringOrInt::Int))
307307
.ok_or_else(|| format!("Expected `String` or `Int`, found: {}", v))
308308
}
309309

@@ -321,7 +321,7 @@ mod string_or_int {
321321

322322
Also, you can partially override `#[graphql(with)]` attribute with other custom scalars.
323323

324-
```rust,ignore
324+
```rust
325325
# use juniper::{GraphQLScalar, InputValue, ParseScalarResult, ScalarValue, ScalarToken, Value};
326326
#
327327
#[derive(GraphQLScalar)]
@@ -348,7 +348,7 @@ impl StringOrInt {
348348
{
349349
v.as_string_value()
350350
.map(|s| Self::String(s.to_owned()))
351-
.or_else(|| v.as_int_value().map(|i| Self::Int(i)))
351+
.or_else(|| v.as_int_value().map(Self::Int))
352352
.ok_or_else(|| format!("Expected `String` or `Int`, found: {}", v))
353353
}
354354
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use juniper::graphql_scalar;
2+
3+
#[graphql_scalar(specified_by_url = "not an url", transparent)]
4+
struct ScalarSpecifiedByUrl(i32);
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Invalid URL: relative URL without a base
2+
--> fail/scalar/derive_input/attr_invalid_url.rs:3:37
3+
|
4+
3 | #[graphql_scalar(specified_by_url = "not an url", transparent)]
5+
| ^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use juniper::graphql_scalar;
2+
3+
#[graphql_scalar(with = Self, transparent)]
4+
struct Scalar;
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: GraphQL scalar `with = <path>` attribute can\'t be combined with `transparent`. You can specify custom resolvers with `to_output`, `from_input`, `parse_token` attributes and still use `transparent` for unspecified ones.
2+
--> fail/scalar/derive_input/attr_transparent_and_with.rs:3:25
3+
|
4+
3 | #[graphql_scalar(with = Self, transparent)]
5+
| ^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use juniper::graphql_scalar;
2+
3+
#[graphql_scalar(transparent)]
4+
struct Scalar {
5+
id: i32,
6+
another: i32,
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g., Test { test: i32 } because of `transparent` attribute
2+
--> fail/scalar/derive_input/attr_transparent_multiple_named_fields.rs:4:1
3+
|
4+
4 | / struct Scalar {
5+
5 | | id: i32,
6+
6 | | another: i32,
7+
7 | | }
8+
| |_^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use juniper::graphql_scalar;
2+
3+
#[graphql_scalar(transparent)]
4+
struct Scalar(i32, i32);
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g., Test(i32) because of `transparent` attribute
2+
--> fail/scalar/derive_input/attr_transparent_multiple_unnamed_fields.rs:4:1
3+
|
4+
4 | struct Scalar(i32, i32);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use juniper::graphql_scalar;
2+
3+
#[graphql_scalar(transparent)]
4+
struct ScalarSpecifiedByUrl;
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g.: `Test(i32)`, `Test { test: i32 }` because of `transparent` attribute
2+
--> fail/scalar/derive_input/attr_transparent_unit_struct.rs:4:1
3+
|
4+
4 | struct ScalarSpecifiedByUrl;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use juniper::GraphQLScalar;
2+
3+
#[derive(GraphQLScalar)]
4+
#[graphql(specified_by_url = "not an url", transparent)]
5+
struct ScalarSpecifiedByUrl(i64);
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Invalid URL: relative URL without a base
2+
--> fail/scalar/derive_input/derive_invalid_url.rs:4:30
3+
|
4+
4 | #[graphql(specified_by_url = "not an url", transparent)]
5+
| ^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use juniper::GraphQLScalar;
2+
3+
#[derive(GraphQLScalar)]
4+
#[graphql(with = Self, transparent)]
5+
struct Scalar;
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: GraphQL scalar `with = <path>` attribute can\'t be combined with `transparent`. You can specify custom resolvers with `to_output`, `from_input`, `parse_token` attributes and still use `transparent` for unspecified ones.
2+
--> fail/scalar/derive_input/derive_transparent_and_with.rs:4:18
3+
|
4+
4 | #[graphql(with = Self, transparent)]
5+
| ^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use juniper::GraphQLScalar;
2+
3+
#[derive(GraphQLScalar)]
4+
#[graphql(transparent)]
5+
struct Scalar {
6+
id: i32,
7+
another: i32,
8+
}
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g., Test { test: i32 } because of `transparent` attribute
2+
--> fail/scalar/derive_input/derive_transparent_multiple_named_fields.rs:4:1
3+
|
4+
4 | / #[graphql(transparent)]
5+
5 | | struct Scalar {
6+
6 | | id: i32,
7+
7 | | another: i32,
8+
8 | | }
9+
| |_^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use juniper::GraphQLScalar;
2+
3+
#[derive(GraphQLScalar)]
4+
#[graphql(transparent)]
5+
struct Scalar(i32, i32);
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g., Test(i32) because of `transparent` attribute
2+
--> fail/scalar/derive_input/derive_transparent_multiple_unnamed_fields.rs:4:1
3+
|
4+
4 | / #[graphql(transparent)]
5+
5 | | struct Scalar(i32, i32);
6+
| |________________________^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use juniper::GraphQLScalar;
2+
3+
#[derive(GraphQLScalar)]
4+
#[graphql(transparent)]
5+
struct ScalarSpecifiedByUrl;
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: GraphQL scalar expected exactly 1 field, e.g.: `Test(i32)`, `Test { test: i32 }` because of `transparent` attribute
2+
--> fail/scalar/derive_input/derive_transparent_unit_struct.rs:4:1
3+
|
4+
4 | / #[graphql(transparent)]
5+
5 | | struct ScalarSpecifiedByUrl;
6+
| |____________________________^

integration_tests/codegen_fail/fail/scalar/derive_input/impl_invalid_url.rs

-16
This file was deleted.

integration_tests/codegen_fail/fail/scalar/derive_input/impl_invalid_url.stderr

-17
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Invalid URL: relative URL without a base
2-
--> fail/scalar/type_alias/impl_invalid_url.rs:6:24
2+
--> fail/scalar/type_alias/attr_invalid_url.rs:6:24
33
|
44
6 | specified_by_url = "not an url",
55
| ^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: GraphQL scalar all custom resolvers have to be provided via `with` or combination of `to_output_with`, `from_input_with`, `parse_token_with` attributes
2-
--> fail/scalar/type_alias/impl_with_not_all_resolvers.rs:6:1
1+
error: GraphQL scalar all custom resolvers have to be provided via `with` or combination of `to_output_with`, `from_input_with`, `parse_token_with` attribute arguments
2+
--> fail/scalar/type_alias/attr_with_not_all_resolvers.rs:6:1
33
|
44
6 | type CustomScalar = Scalar;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: GraphQL scalar all custom resolvers have to be provided via `with` or combination of `to_output_with`, `from_input_with`, `parse_token_with` attributes
2-
--> fail/scalar/type_alias/impl_without_resolvers.rs:6:1
1+
error: GraphQL scalar all custom resolvers have to be provided via `with` or combination of `to_output_with`, `from_input_with`, `parse_token_with` attribute arguments
2+
--> fail/scalar/type_alias/attr_without_resolvers.rs:6:1
33
|
44
6 | type CustomScalar = Scalar;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

integration_tests/juniper_tests/src/codegen/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod object_attr;
66
mod object_derive;
77
mod scalar_attr_derive_input;
88
mod scalar_attr_type_alias;
9+
mod scalar_derive;
910
mod subscription_attr;
1011
mod union_attr;
1112
mod union_derive;

0 commit comments

Comments
 (0)