Skip to content

Commit 23f9c6a

Browse files
committed
Support in macro
1 parent cb43188 commit 23f9c6a

File tree

8 files changed

+134
-62
lines changed

8 files changed

+134
-62
lines changed

juniper/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ void = { version = "1.0.2", optional = true }
8181
[dev-dependencies]
8282
bencher = "0.1.2"
8383
chrono = { version = "0.4.30", features = ["alloc"], default-features = false }
84+
compact_str = { version = "0.9", features = ["serde"] }
8485
jiff = { version = "0.2", features = ["tzdb-bundle-always"], default-features = false }
8586
pretty_assertions = "1.0.0"
8687
serde_json = "1.0.18"

juniper/src/ast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ where
572572

573573
impl<S: ScalarValue> IntoInputValue<S> for &ArcStr {
574574
fn into_input_value(self) -> InputValue<S> {
575-
InputValue::scalar(S::from_custom_string(self))
575+
InputValue::scalar(S::from_displayable(self))
576576
}
577577
}
578578

@@ -584,7 +584,7 @@ impl<S: ScalarValue> IntoInputValue<S> for ArcStr {
584584

585585
impl<S: ScalarValue> IntoInputValue<S> for &CompactString {
586586
fn into_input_value(self) -> InputValue<S> {
587-
InputValue::scalar(S::from_custom_string(self))
587+
InputValue::scalar(S::from_displayable(self))
588588
}
589589
}
590590

juniper/src/types/scalars.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ mod impl_arcstr_scalar {
181181
use super::ArcStr;
182182

183183
pub(super) fn to_output<S: ScalarValue>(v: &ArcStr) -> Value<S> {
184-
Value::scalar(S::from_custom_string(v))
184+
Value::scalar(S::from_displayable(v))
185185
}
186186

187187
pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<ArcStr, String> {
@@ -201,7 +201,7 @@ mod impl_compactstring_scalar {
201201
use super::CompactString;
202202

203203
pub(super) fn to_output<S: ScalarValue>(v: &CompactString) -> Value<S> {
204-
Value::scalar(S::from_custom_string(v))
204+
Value::scalar(S::from_displayable(v))
205205
}
206206

207207
pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<CompactString, String> {

juniper/src/value/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::{any::TypeId, borrow::Cow, fmt, mem};
55

66
use arcstr::ArcStr;
77
use compact_str::CompactString;
8-
use derive_more::with_trait::From;
98

109
use crate::{
1110
ast::{InputValue, ToInputValue},
@@ -27,10 +26,9 @@ pub use self::{
2726
/// information since they are generated by resolving fields and values rather
2827
/// than parsing a source query.
2928
#[expect(missing_docs, reason = "self-explanatory")]
30-
#[derive(Clone, Debug, From, PartialEq)]
29+
#[derive(Clone, Debug, PartialEq)]
3130
pub enum Value<S = DefaultScalarValue> {
3231
Null,
33-
#[from(ignore)]
3432
Scalar(S),
3533
List(Vec<Value<S>>),
3634
Object(Object<S>),
@@ -297,7 +295,7 @@ where
297295

298296
impl<S: ScalarValue> IntoValue<S> for &ArcStr {
299297
fn into_value(self) -> Value<S> {
300-
Value::scalar(S::from_custom_string(self))
298+
Value::scalar(S::from_displayable(self))
301299
}
302300
}
303301

@@ -309,7 +307,7 @@ impl<S: ScalarValue> IntoValue<S> for ArcStr {
309307

310308
impl<S: ScalarValue> IntoValue<S> for &CompactString {
311309
fn into_value(self) -> Value<S> {
312-
Value::scalar(S::from_custom_string(self))
310+
Value::scalar(S::from_displayable(self))
313311
}
314312
}
315313

juniper/src/value/scalar.rs

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use derive_more::with_trait::From;
88
use serde::{Serialize, de::DeserializeOwned};
99

1010
use crate::parser::{ParseError, ScalarToken};
11+
#[cfg(doc)]
12+
use crate::{InputValue, Value};
1113

1214
pub use juniper_codegen::ScalarValue;
1315

@@ -20,44 +22,68 @@ pub trait ParseScalarValue<S = DefaultScalarValue> {
2022
fn from_str(value: ScalarToken<'_>) -> ParseScalarResult<S>;
2123
}
2224

23-
/// A trait marking a type that could be used as internal representation of
24-
/// scalar values in juniper
25+
/// Type that could be used as internal representation of scalar values (e.g. inside [`Value`] and
26+
/// [`InputValue`]).
2527
///
26-
/// The main objective of this abstraction is to allow other libraries to
27-
/// replace the default representation with something that better fits their
28-
/// needs.
29-
/// There is a custom derive (`#[derive(`[`ScalarValue`]`)]`) available that
30-
/// implements most of the required traits automatically for a enum representing
31-
/// a scalar value. However, [`Serialize`] and [`Deserialize`] implementations
28+
/// This abstraction allows other libraries and user code to replace the default representation with
29+
/// something that better fits their needs than [`DefaultScalarValue`].
30+
///
31+
/// # Deriving
32+
///
33+
/// There is a custom derive (`#[derive(`[`ScalarValue`](macro@crate::ScalarValue)`)]`) available,
34+
/// that implements most of the required traits automatically for an enum representing a
35+
/// [`ScalarValue`]. However, [`Serialize`] and [`Deserialize`] implementations
3236
/// are expected to be provided.
3337
///
34-
/// # Implementing a new scalar value representation
35-
/// The preferred way to define a new scalar value representation is
36-
/// defining a enum containing a variant for each type that needs to be
37-
/// represented at the lowest level.
38-
/// The following example introduces an new variant that is able to store 64 bit
39-
/// integers.
38+
/// # Example
39+
///
40+
/// The preferred way to define a new [`ScalarValue`] representation is defining an enum containing
41+
/// a variant for each type that needs to be represented at the lowest level.
42+
///
43+
/// The following example introduces a new variant that is able to store 64-bit integers, and uses
44+
/// a [`CompactString`] for a string representation.
4045
///
4146
/// ```rust
42-
/// # use std::fmt;
47+
/// # use std::{any::Any, fmt};
4348
/// #
44-
/// # use serde::{de, Deserialize, Deserializer, Serialize};
49+
/// # use compact_str::CompactString;
4550
/// # use juniper::ScalarValue;
51+
/// # use serde::{de, Deserialize, Deserializer, Serialize};
4652
/// #
4753
/// #[derive(Clone, Debug, PartialEq, ScalarValue, Serialize)]
4854
/// #[serde(untagged)]
55+
/// #[value(from_displayable_with = from_compact_str)]
4956
/// enum MyScalarValue {
5057
/// #[value(as_float, as_int)]
5158
/// Int(i32),
5259
/// Long(i64),
5360
/// #[value(as_float)]
5461
/// Float(f64),
5562
/// #[value(as_str, as_string, into_string)]
56-
/// String(String),
63+
/// String(CompactString),
5764
/// #[value(as_bool)]
5865
/// Boolean(bool),
5966
/// }
6067
///
68+
/// // Custom implementation of `ScalarValue::from_displayable()` method
69+
/// // for efficient conversions from `CompactString` into `MyScalarValue`.
70+
/// fn from_compact_str<Str: fmt::Display + Any + ?Sized>(s: &Str) -> MyScalarValue {
71+
/// use juniper::AnyExt as _; // allows downcasting directly on types without `dyn`
72+
///
73+
/// if let Some(s) = s.downcast_ref::<CompactString>() {
74+
/// MyScalarValue::String(s.clone())
75+
/// } else {
76+
/// s.to_string().into()
77+
/// }
78+
/// }
79+
///
80+
/// // Macro cannot infer and generate this impl if a custom string type is used.
81+
/// impl From<String> for MyScalarValue {
82+
/// fn from(value: String) -> Self {
83+
/// Self::String(value.into())
84+
/// }
85+
/// }
86+
///
6187
/// impl<'de> Deserialize<'de> for MyScalarValue {
6288
/// fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
6389
/// struct Visitor;
@@ -115,7 +141,7 @@ pub trait ParseScalarValue<S = DefaultScalarValue> {
115141
/// }
116142
///
117143
/// fn visit_string<E: de::Error>(self, s: String) -> Result<Self::Value, E> {
118-
/// Ok(MyScalarValue::String(s))
144+
/// Ok(MyScalarValue::String(s.into()))
119145
/// }
120146
/// }
121147
///
@@ -124,6 +150,7 @@ pub trait ParseScalarValue<S = DefaultScalarValue> {
124150
/// }
125151
/// ```
126152
///
153+
/// [`CompactString`]: compact_str::CompactString
127154
/// [`Deserialize`]: trait@serde::Deserialize
128155
/// [`Serialize`]: trait@serde::Serialize
129156
pub trait ScalarValue:
@@ -228,14 +255,18 @@ pub trait ScalarValue:
228255
}
229256
}
230257

231-
/// Creates this [`ScalarValue`] from the provided custom string type.
258+
/// Creates this [`ScalarValue`] from the provided [`fmt::Display`] type.
232259
///
233260
/// This method should be implemented if [`ScalarValue`] implementation uses some custom string
234261
/// type inside to enable efficient conversion from values of this type.
235262
///
236263
/// Default implementation allocates by converting [`ToString`] and [`From`]`<`[`String`]`>`.
264+
///
265+
/// # Example
266+
///
267+
/// See the [example in trait documentation](ScalarValue#example) for how it can be used.
237268
#[must_use]
238-
fn from_custom_string<Str: fmt::Display + Any + ?Sized>(s: &Str) -> Self {
269+
fn from_displayable<Str: fmt::Display + Any + ?Sized>(s: &Str) -> Self {
239270
s.to_string().into()
240271
}
241272
}

juniper_codegen/src/lib.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -415,24 +415,23 @@ pub fn derive_enum(input: TokenStream) -> TokenStream {
415415
/// struct UserId(String);
416416
/// ```
417417
///
418-
/// All of the methods inherited from `Newtype`'s field may also be overridden
418+
/// All the methods inherited from `Newtype`'s field may also be overridden
419419
/// with the attributes described below.
420420
///
421421
/// # Custom resolving
422422
///
423423
/// Customization of a [GraphQL scalar][0] type resolving is possible via
424424
/// `#[graphql(to_output_with = <fn path>)]` attribute:
425425
/// ```rust
426-
/// # use juniper::{GraphQLScalar, ScalarValue, Value};
426+
/// # use juniper::{GraphQLScalar, IntoValue as _, ScalarValue, Value};
427427
/// #
428428
/// #[derive(GraphQLScalar)]
429429
/// #[graphql(to_output_with = to_output, transparent)]
430430
/// struct Incremented(i32);
431431
///
432432
/// /// Increments [`Incremented`] before converting into a [`Value`].
433433
/// fn to_output<S: ScalarValue>(v: &Incremented) -> Value<S> {
434-
/// let inc = v.0 + 1;
435-
/// Value::from(inc)
434+
/// (v.0 + 1).into_value()
436435
/// }
437436
/// ```
438437
///
@@ -772,22 +771,25 @@ pub fn graphql_scalar(attr: TokenStream, body: TokenStream) -> TokenStream {
772771
})
773772
}
774773

775-
/// `#[derive(ScalarValue)]` macro for deriving a [`ScalarValue`]
776-
/// implementation.
774+
/// `#[derive(ScalarValue)]` macro for deriving a [`ScalarValue`] implementation.
777775
///
778776
/// To derive a [`ScalarValue`] on enum you should mark the corresponding enum
779777
/// variants with `as_int`, `as_float`, `as_string`, `into_string`, `as_str` and
780-
/// `as_bool` attribute argumentes (names correspond to [`ScalarValue`] required
778+
/// `as_bool` attribute arguments (names correspond to [`ScalarValue`] required
781779
/// methods).
782780
///
781+
/// Additional `from_displayable_with` argument could be used to specify a custom
782+
/// implementation to override the default `ScalarValue::from_displayable()` method.
783+
///
783784
/// ```rust
784-
/// # use std::fmt;
785+
/// # use std::{any::Any, fmt};
785786
/// #
786787
/// # use serde::{de, Deserialize, Deserializer, Serialize};
787788
/// # use juniper::ScalarValue;
788789
/// #
789790
/// #[derive(Clone, Debug, PartialEq, ScalarValue, Serialize)]
790791
/// #[serde(untagged)]
792+
/// #[value(from_displayable_with = from_custom_str)]
791793
/// enum MyScalarValue {
792794
/// #[value(as_float, as_int)]
793795
/// Int(i32),
@@ -804,6 +806,23 @@ pub fn graphql_scalar(attr: TokenStream, body: TokenStream) -> TokenStream {
804806
/// #[value(as_bool)]
805807
/// Boolean(bool),
806808
/// }
809+
///
810+
/// // Custom implementation of `ScalarValue::from_displayable()` method for
811+
/// // possible efficient conversions into `MyScalarValue` from custom string types.
812+
/// fn from_custom_str<Str: fmt::Display + Any + ?Sized>(s: &Str) -> MyScalarValue {
813+
/// use juniper::AnyExt as _; // allows downcasting directly on types without `dyn`
814+
///
815+
/// // Imagine this is some custom optimized string type.
816+
/// struct CustomString(String);
817+
///
818+
/// // We specialize the conversion for this type without going through expensive
819+
/// // `ToString` -> `From<String>` conversion with allocation.
820+
/// if let Some(s) = s.downcast_ref::<CustomString>() {
821+
/// MyScalarValue::String(s.0.clone())
822+
/// } else {
823+
/// s.to_string().into()
824+
/// }
825+
/// }
807826
///
808827
/// impl<'de> Deserialize<'de> for MyScalarValue {
809828
/// fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
@@ -1361,7 +1380,7 @@ pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
13611380
///
13621381
/// For more info and possibilities see [`#[graphql_interface]`][0] macro.
13631382
///
1364-
/// [0]: crate::graphql_interface
1383+
/// [0]: macro@crate::graphql_interface
13651384
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
13661385
#[proc_macro_derive(GraphQLInterface, attributes(graphql))]
13671386
pub fn derive_interface(body: TokenStream) -> TokenStream {

0 commit comments

Comments
 (0)