Skip to content

Commit b6db430

Browse files
committed
Fix Book
1 parent a00f807 commit b6db430

File tree

2 files changed

+74
-60
lines changed

2 files changed

+74
-60
lines changed

book/src/types/scalars.md

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,57 @@ fn to_output<S: ScalarValue>(v: &Incremented) -> Value<S> {
105105
Customization of a [custom GraphQL scalar][2] value parsing is possible via `#[graphql(from_input_with = <fn path>)]` attribute:
106106
```rust
107107
# extern crate juniper;
108-
# use juniper::{GraphQLScalar, InputValue, ScalarValue};
108+
# use juniper::{GraphQLScalar, ScalarValue};
109109
#
110110
#[derive(GraphQLScalar)]
111111
#[graphql(from_input_with = Self::from_input, transparent)]
112112
struct UserId(String);
113113

114114
impl UserId {
115-
/// Checks whether the [`InputValue`] is a [`String`] beginning with `id: `
116-
/// and strips it.
117-
fn from_input<S>(input: &InputValue<S>) -> Result<Self, String>
118-
where
119-
S: ScalarValue
120-
{
121-
input.as_string_value()
122-
.ok_or_else(|| format!("Expected `String`, found: {input}"))
123-
.and_then(|str| {
124-
str.strip_prefix("id: ")
125-
.ok_or_else(|| {
126-
format!(
127-
"Expected `UserId` to begin with `id: `, \
128-
found: {input}",
129-
)
130-
})
115+
/// Checks whether the [`InputValue`] is a [`String`] beginning with `id: ` and strips it.
116+
fn from_input(
117+
input: &str,
118+
// ^^^^ any concrete type having `TryScalarValueTo` implementation could be used
119+
) -> Result<Self, Box<str>> {
120+
// ^^^^^^^^ must implement `IntoFieldError`
121+
input
122+
.strip_prefix("id: ")
123+
.ok_or_else(|| {
124+
format!("Expected `UserId` to begin with `id: `, found: {input}").into()
131125
})
132-
.map(|id| Self(id.to_owned()))
126+
.map(|id| Self(id.into()))
127+
}
128+
}
129+
#
130+
# fn main() {}
131+
```
132+
133+
The provided function is polymorphic by input and output types:
134+
```rust
135+
# extern crate juniper;
136+
# use juniper::{GraphQLScalar, Scalar, ScalarValue};
137+
#
138+
#[derive(GraphQLScalar)]
139+
#[graphql(from_input_with = Self::from_input, transparent)]
140+
struct UserId(String);
141+
142+
impl UserId {
143+
fn from_input(
144+
input: &Scalar<impl ScalarValue>,
145+
// ^^^^^^ for generic argument using `Scalar` transparent wrapper is required,
146+
// otherwise Rust won't be able to infer the required type
147+
) -> Self {
148+
// ^^^^ if the result is infallible, it's OK to not use `Result`
149+
Self(
150+
input
151+
.try_to_int().map(|i| i.to_string())
152+
.or_else(|| input.try_to_bool().map(|f| f.to_string()))
153+
.or_else(|| input.try_to_float().map(|b| b.to_string()))
154+
.or_else(|| input.try_to_string())
155+
.unwrap_or_else(|| {
156+
unreachable!("`ScalarValue` is at least one of primitive GraphQL types")
157+
}),
158+
)
133159
}
134160
}
135161
#
@@ -143,8 +169,7 @@ Customization of which tokens a [custom GraphQL scalar][0] type should be parsed
143169
```rust
144170
# extern crate juniper;
145171
# use juniper::{
146-
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
147-
# ScalarValue, ScalarToken, Value,
172+
# GraphQLScalar, ParseScalarResult, ParseScalarValue, Scalar, ScalarToken, ScalarValue, Value,
148173
# };
149174
#
150175
#[derive(GraphQLScalar)]
@@ -168,11 +193,11 @@ fn to_output<S: ScalarValue>(v: &StringOrInt) -> Value<S> {
168193
}
169194
}
170195

171-
fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<StringOrInt, String> {
172-
v.as_string_value()
173-
.map(|s| StringOrInt::String(s.into()))
174-
.or_else(|| v.as_int_value().map(StringOrInt::Int))
175-
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}"))
196+
fn from_input(v: &Scalar<impl ScalarValue>) -> Result<StringOrInt, Box<str>> {
197+
v.try_to_string()
198+
.map(StringOrInt::String)
199+
.or_else(|| v.try_to_int().map(StringOrInt::Int))
200+
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}").into())
176201
}
177202

178203
fn parse_token<S: ScalarValue>(value: ScalarToken<'_>) -> ParseScalarResult<S> {
@@ -191,8 +216,7 @@ Instead of providing all custom functions separately, it's possible to provide a
191216
```rust
192217
# extern crate juniper;
193218
# use juniper::{
194-
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
195-
# ScalarValue, ScalarToken, Value,
219+
# GraphQLScalar, ParseScalarResult, ParseScalarValue, Scalar, ScalarToken, ScalarValue, Value,
196220
# };
197221
#
198222
#[derive(GraphQLScalar)]
@@ -212,11 +236,11 @@ mod string_or_int {
212236
}
213237
}
214238

215-
pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<StringOrInt, String> {
216-
v.as_string_value()
217-
.map(|s| StringOrInt::String(s.into()))
218-
.or_else(|| v.as_int_value().map(StringOrInt::Int))
219-
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}"))
239+
pub(super) fn from_input(v: &Scalar<impl ScalarValue>) -> Result<StringOrInt, Box<str>> {
240+
v.try_to_string()
241+
.map(StringOrInt::String)
242+
.or_else(|| v.try_to_int().map(StringOrInt::Int))
243+
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}").into())
220244
}
221245

222246
pub(super) fn parse_token<S: ScalarValue>(t: ScalarToken<'_>) -> ParseScalarResult<S> {
@@ -232,8 +256,7 @@ A regular `impl` block is also suitable for that:
232256
```rust
233257
# extern crate juniper;
234258
# use juniper::{
235-
# GraphQLScalar, InputValue, ParseScalarResult, ParseScalarValue,
236-
# ScalarValue, ScalarToken, Value,
259+
# GraphQLScalar, ParseScalarResult, ParseScalarValue, Scalar, ScalarToken, ScalarValue, Value,
237260
# };
238261
#
239262
#[derive(GraphQLScalar)]
@@ -251,14 +274,11 @@ impl StringOrInt {
251274
}
252275
}
253276

254-
fn from_input<S>(v: &InputValue<S>) -> Result<Self, String>
255-
where
256-
S: ScalarValue
257-
{
258-
v.as_string_value()
259-
.map(|s| Self::String(s.into()))
260-
.or_else(|| v.as_int_value().map(Self::Int))
261-
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}"))
277+
fn from_input(v: &Scalar<impl ScalarValue>) -> Result<Self, Box<str>> {
278+
v.try_to_string()
279+
.map(Self::String)
280+
.or_else(|| v.try_to_int().map(Self::Int))
281+
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}").into())
262282
}
263283

264284
fn parse_token<S>(value: ScalarToken<'_>) -> ParseScalarResult<S>
@@ -277,8 +297,7 @@ At the same time, any custom function still may be specified separately, if requ
277297
```rust
278298
# extern crate juniper;
279299
# use juniper::{
280-
# GraphQLScalar, InputValue, ParseScalarResult, ScalarValue,
281-
# ScalarToken, Value
300+
# GraphQLScalar, ParseScalarResult, Scalar, ScalarToken, ScalarValue, Value,
282301
# };
283302
#
284303
#[derive(GraphQLScalar)]
@@ -304,14 +323,11 @@ mod string_or_int {
304323
}
305324
}
306325

307-
pub(super) fn from_input<S>(v: &InputValue<S>) -> Result<StringOrInt, String>
308-
where
309-
S: ScalarValue,
310-
{
311-
v.as_string_value()
312-
.map(|s| StringOrInt::String(s.into()))
313-
.or_else(|| v.as_int_value().map(StringOrInt::Int))
314-
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}"))
326+
pub(super) fn from_input(v: &Scalar<impl ScalarValue>) -> Result<StringOrInt, Box<str>> {
327+
v.try_to_string()
328+
.map(StringOrInt::String)
329+
.or_else(|| v.try_to_int().map(StringOrInt::Int))
330+
.ok_or_else(|| format!("Expected `String` or `Int`, found: {v}").into())
315331
}
316332

317333
// No need in `parse_token()` function.
@@ -351,9 +367,10 @@ For implementing [custom scalars][2] on foreign types there is [`#[graphql_scala
351367
# }
352368
#
353369
# use juniper::DefaultScalarValue as CustomScalarValue;
354-
use juniper::{InputValue, ScalarValue, Value, graphql_scalar};
370+
use juniper::{ScalarValue, Value, graphql_scalar};
355371

356-
#[graphql_scalar(
372+
#[graphql_scalar]
373+
#[graphql(
357374
with = date_scalar,
358375
parse_token(String),
359376
scalar = CustomScalarValue,
@@ -369,10 +386,8 @@ mod date_scalar {
369386
Value::scalar(v.to_string())
370387
}
371388

372-
pub(super) fn from_input(v: &InputValue<CustomScalarValue>) -> Result<Date, String> {
373-
v.as_string_value()
374-
.ok_or_else(|| format!("Expected `String`, found: {v}"))
375-
.and_then(|s| s.parse().map_err(|e| format!("Failed to parse `Date`: {e}")))
389+
pub(super) fn from_input(s: &str) -> Result<Date, Box<str>> {
390+
s.parse().map_err(|e| format!("Failed to parse `Date`: {e}").into())
376391
}
377392
}
378393
#

juniper_codegen/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,7 @@ pub fn derive_enum(input: TokenStream) -> TokenStream {
445445
/// struct UserId(String);
446446
///
447447
/// impl UserId {
448-
/// /// Checks whether the [`ScalarValue`] is a [`String`] beginning with
449-
/// /// `id: ` and strips it.
448+
/// /// Checks whether the [`ScalarValue`] is a [`String`] beginning with `id: ` and strips it.
450449
/// fn from_input(
451450
/// input: &str,
452451
/// // ^^^^ any concrete type having `TryScalarValueTo` implementation could be used

0 commit comments

Comments
 (0)