Skip to content
1 change: 1 addition & 0 deletions valuable-serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ where
Value::Path(p) => Serialize::serialize(p, serializer),
#[cfg(feature = "std")]
Value::Error(e) => serializer.collect_str(e),
Value::Display(d) => serializer.collect_str(d),

v => unimplemented!("{:?}", v),
}
Expand Down
91 changes: 91 additions & 0 deletions valuable/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,27 @@ macro_rules! value {
$variant($ty),
)*

/// An arbitrary value that implements [`std::fmt::Display`].
///
/// # Examples
///
/// ```
/// use std::fmt;
/// use valuable::{Value, Valuable};
///
/// struct Meters(u32);
///
/// impl fmt::Display for Meters {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "{}m", self.0)
/// }
/// }
///
/// let meters = Meters(5);
/// let v = Value::Display(&meters);
/// ```
Display(&'a dyn fmt::Display),

/// A Rust `()` or `None` value.
///
/// # Examples
Expand Down Expand Up @@ -92,6 +113,12 @@ macro_rules! value {
}
}

impl<'a> From<&'a dyn fmt::Display> for Value<'a> {
fn from(src: &'a dyn fmt::Display) -> Self {
Self::Display(src)
}
}

impl fmt::Debug for Value<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
use Value::*;
Expand All @@ -104,6 +131,7 @@ macro_rules! value {
$(#[$attrs])*
$variant(v) => fmt::Debug::fmt(v, fmt),
)*
Display(d) => write!(fmt, "\"{}\"", d),
Unit => ().fmt(fmt),
}
}
Expand Down Expand Up @@ -690,6 +718,69 @@ macro_rules! convert {
_ => None,
}
}

/// Return a `&dyn Display` representation of `self`, if possible.
///
/// If this value is a [`Value::Display`] variant, or any primitive
/// `Value` variant, this method will return a `&dyn Display` trait
/// object. Otherwise, if the value is [`Structable`],
/// [`Enumerable`], [`Tuplable`], [`Listable`], [`Mappable`], or a
/// [`Value::Path`], this method will return `None`.
///
/// # Examples
///
/// ```
/// use std::fmt;
/// use valuable::{Value, Valuable};
///
/// struct Meters(u32);
///
/// impl fmt::Display for Meters {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "{}m", self.0)
/// }
/// }
///
/// let meters = Meters(5);
///
/// assert!(Value::Display(&meters).as_display().is_some());
/// assert!(Value::Tuplable(&(true, "hello")).as_display().is_none());
/// ```
pub fn as_display(&self) -> Option<&dyn fmt::Display> {
use Value::*;
match *self {
I8(ref v) => Some(v),
I16(ref v) => Some(v),
I32(ref v) => Some(v),
I64(ref v) => Some(v),
I128(ref v) => Some(v),
Isize(ref v) => Some(v),
U8(ref v) => Some(v),
U16(ref v) => Some(v),
U32(ref v) => Some(v),
U64(ref v) => Some(v),
U128(ref v) => Some(v),
Usize(ref v) => Some(v),
F32(ref v) => Some(v),
F64(ref v) => Some(v),
Bool(ref v) => Some(v),
Char(ref v) => Some(v),
String(ref v) => Some(v),

#[cfg(feature = "std")]
Error(ref v) => Some(v),

// XXX(eliza): this, sadly, does not work for `Path`s; the
// only way to return them as a `Display` impl is
// `Path::display`, which creates a value owned by _this_
// function; we can't return that as a trait object because
// we're borrowing it from the function's scope rather than
// from the value itself.
Comment on lines +773 to +778
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#43 (comment) is one of alternatives that can handle this case well.


Display(v) => Some(v),
_ => None,
}
}
}
}
}
Expand Down