@@ -476,22 +476,158 @@ pub trait ScalarValue:
476476
477477 /// Creates this [`ScalarValue`] from the provided [`Display`]able type.
478478 ///
479- /// This method should be implemented if [`ScalarValue`] implementation uses some custom string
480- /// type inside to enable efficient conversion from values of this type.
479+ /// # Usage
480+ ///
481+ /// This method cannot work with non-`'static` types due to [`Any`] `'static` restriction. For
482+ /// non-`'static` types the [`ScalarValue::from_displayable_non_static()`] method should be used
483+ /// instead. However, the [`Any`] here allows implementors to specialize some conversions to be
484+ /// cheaper for their [`ScalarValue`] implementation, and so, using this method is preferred
485+ /// whenever is possible.
486+ ///
487+ /// # Implementation
481488 ///
482489 /// Default implementation allocates by converting [`ToString`] and [`From<String>`].
483490 ///
484- /// # Example
491+ /// This method should be implemented if [`ScalarValue`] implementation uses some custom string
492+ /// type inside to enable efficient conversion from values of this type.
485493 ///
486- /// See the [example in trait documentation](ScalarValue#example) for how it can be used.
494+ /// ```rust
495+ /// # use std::any::Any;
496+ /// #
497+ /// use arcstr::ArcStr;
498+ /// use derive_more::with_trait::{Display, From, TryInto};
499+ /// use juniper::ScalarValue;
500+ /// use serde::{Deserialize, Serialize};
501+ ///
502+ /// #[derive(
503+ /// Clone, Debug, Deserialize, Display, From, PartialEq, ScalarValue, Serialize, TryInto,
504+ /// )]
505+ /// #[serde(untagged)]
506+ /// #[value(from_displayable_with = from_arcstr)]
507+ /// enum MyScalarValue {
508+ /// #[from]
509+ /// #[value(to_float, to_int)]
510+ /// Int(i32),
511+ ///
512+ /// #[from]
513+ /// #[value(to_float)]
514+ /// Float(f64),
515+ ///
516+ /// #[from(&str, String, ArcStr)]
517+ /// #[value(as_str, to_string)]
518+ /// String(ArcStr),
519+ ///
520+ /// #[from]
521+ /// #[value(to_bool)]
522+ /// Boolean(bool),
523+ /// }
524+ ///
525+ /// // Custom implementation of `ScalarValue::from_displayable()` method for specializing
526+ /// // an efficient conversions from `ArcStr` into `MyScalarValue`.
527+ /// fn from_arcstr<Str: Display + Any + ?Sized>(s: &Str) -> MyScalarValue {
528+ /// use juniper::AnyExt as _; // allows downcasting directly on types without `dyn`
529+ ///
530+ /// if let Some(s) = s.downcast_ref::<ArcStr>() {
531+ /// MyScalarValue::String(s.clone()) // `Clone`ing `ArcStr` is cheap
532+ /// } else {
533+ /// // We do not override `ScalarValue::from_displayable_non_static()` here,
534+ /// // since `arcstr` crate doesn't provide API for efficient conversion into
535+ /// // an `ArcStr` for any `Display`able type, unfortunately.
536+ /// // The closest possible way is to use `arcstr::format!("{s}")` expression.
537+ /// // However, it actually expands to `ArcStr::from(fmt::format(format_args!("{s}")))`,
538+ /// // where `fmt::format()` allocates a `String`, and thus, is fully equivalent to the
539+ /// // default implementation, which does `.to_string().into()` conversion.
540+ /// MyScalarValue::from_displayable_non_static(s)
541+ /// }
542+ /// }
543+ /// #
544+ /// # // `derive_more::TryInto` is not capable for transitive conversions yet,
545+ /// # // so this impl is manual as a custom string type is used instead of `String`.
546+ /// # impl TryFrom<MyScalarValue> for String {
547+ /// # type Error = MyScalarValue;
548+ /// #
549+ /// # fn try_from(value: MyScalarValue) -> Result<Self, Self::Error> {
550+ /// # if let MyScalarValue::String(s) = value {
551+ /// # Ok(s.to_string())
552+ /// # } else {
553+ /// # Err(value)
554+ /// # }
555+ /// # }
556+ /// # }
557+ /// ```
487558 #[ must_use]
488- fn from_displayable < Str : Display + Any + ?Sized > ( s : & Str ) -> Self {
489- s . to_string ( ) . into ( )
559+ fn from_displayable < T : Display + Any + ?Sized > ( value : & T ) -> Self {
560+ Self :: from_displayable_non_static ( value )
490561 }
491562
563+ /// Creates this [`ScalarValue`] from the provided non-`'static` [`Display`]able type.
564+ ///
565+ /// # Usage
566+ ///
567+ /// This method exists solely because [`Any`] requires `'static`, and so the
568+ /// [`ScalarValue::from_displayable()`] method cannot cover non-`'static` types. Always prefer
569+ /// to use the [`ScalarValue::from_displayable()`] method instead of this one, whenever it's
570+ /// possible, to allow possible cheap conversion specialization.
571+ ///
572+ /// # Implementation
573+ ///
574+ /// Default implementation allocates by converting [`ToString`] and [`From<String>`].
575+ ///
576+ /// This method should be implemented if [`ScalarValue`] implementation uses some custom string
577+ /// type inside to create its values efficiently without intermediate [`String`]-conversion.
578+ ///
579+ /// ```rust
580+ /// use compact_str::{CompactString, ToCompactString as _};
581+ /// use derive_more::with_trait::{Display, From, TryInto};
582+ /// use juniper::ScalarValue;
583+ /// use serde::{Deserialize, Serialize};
584+ ///
585+ /// #[derive(
586+ /// Clone, Debug, Deserialize, Display, From, PartialEq, ScalarValue, Serialize, TryInto,
587+ /// )]
588+ /// #[serde(untagged)]
589+ /// #[value(from_displayable_non_static_with = to_compact_string)]
590+ /// enum MyScalarValue {
591+ /// #[from]
592+ /// #[value(to_float, to_int)]
593+ /// Int(i32),
594+ ///
595+ /// #[from]
596+ /// #[value(to_float)]
597+ /// Float(f64),
598+ ///
599+ /// #[from(&str, String, CompactString)]
600+ /// #[value(as_str, to_string)]
601+ /// String(CompactString),
602+ ///
603+ /// #[from]
604+ /// #[value(to_bool)]
605+ /// Boolean(bool),
606+ /// }
607+ ///
608+ /// // Custom implementation of `ScalarValue::from_displayable_non_static()` method
609+ /// // for efficient writing into a `CompactString` as a `MyScalarValue::String`.
610+ /// fn to_compact_string<T: Display + ?Sized>(v: &T) -> MyScalarValue {
611+ /// v.to_compact_string().into()
612+ /// }
613+ /// #
614+ /// # // `derive_more::TryInto` is not capable for transitive conversions yet,
615+ /// # // so this impl is manual as a custom string type is used instead of `String`.
616+ /// # impl TryFrom<MyScalarValue> for String {
617+ /// # type Error = MyScalarValue;
618+ /// #
619+ /// # fn try_from(value: MyScalarValue) -> Result<Self, Self::Error> {
620+ /// # if let MyScalarValue::String(s) = value {
621+ /// # Ok(s.into())
622+ /// # } else {
623+ /// # Err(value)
624+ /// # }
625+ /// # }
626+ /// # }
627+ /// ```
492628 #[ must_use]
493- fn from_display < Str : Display + ?Sized > ( s : & Str ) -> Self {
494- s . to_string ( ) . into ( )
629+ fn from_displayable_non_static < T : Display + ?Sized > ( value : & T ) -> Self {
630+ value . to_string ( ) . into ( )
495631 }
496632}
497633
0 commit comments