use core::{fmt, str::FromStr};
#[derive(Clone)]
pub struct Value<'v>(value_bag::ValueBag<'v>);
impl<'v> Value<'v> {
#[track_caller]
pub fn capture_display(value: &'v (impl fmt::Display + 'static)) -> Self {
Value(value_bag::ValueBag::capture_display(value))
}
#[track_caller]
pub fn from_display(value: &'v impl fmt::Display) -> Self {
Value(value_bag::ValueBag::from_display(value))
}
#[track_caller]
pub fn capture_debug(value: &'v (impl fmt::Debug + 'static)) -> Self {
Value(value_bag::ValueBag::capture_debug(value))
}
#[track_caller]
pub fn from_debug(value: &'v impl fmt::Debug) -> Self {
Value(value_bag::ValueBag::from_debug(value))
}
#[cfg(feature = "serde")]
#[track_caller]
pub fn capture_serde(value: &'v (impl serde::Serialize + 'static)) -> Self {
Value(value_bag::ValueBag::capture_serde1(value))
}
#[cfg(feature = "serde")]
#[track_caller]
pub fn from_serde(value: &'v impl serde::Serialize) -> Self {
Value(value_bag::ValueBag::from_serde1(value))
}
#[cfg(feature = "sval")]
#[track_caller]
pub fn capture_sval(value: &'v (impl sval::Value + 'static)) -> Self {
Value(value_bag::ValueBag::capture_sval2(value))
}
#[cfg(feature = "sval")]
#[track_caller]
pub fn from_sval(value: &'v impl sval::Value) -> Self {
Value(value_bag::ValueBag::from_sval2(value))
}
#[cfg(feature = "std")]
#[track_caller]
pub fn capture_error(value: &'v (impl std::error::Error + 'static)) -> Self {
Value(value_bag::ValueBag::capture_error(value))
}
#[track_caller]
pub fn from_any(value: &'v impl ToValue) -> Self {
value.to_value()
}
#[track_caller]
pub const fn null() -> Self {
Value(value_bag::ValueBag::empty())
}
pub fn is_null(&self) -> bool {
self.0.is_empty()
}
pub fn by_ref<'b>(&'b self) -> Value<'b> {
Value(self.0.by_ref())
}
pub fn cast<'a, T: FromValue<'v>>(self) -> Option<T> {
T::from_value(self)
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.0.downcast_ref()
}
pub fn parse<T: FromStr>(&self) -> Option<T> {
struct Extract<T>(Option<T>);
impl<'v, T: FromStr> value_bag::visit::Visit<'v> for Extract<T> {
fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), value_bag::Error> {
#[cfg(feature = "alloc")]
{
use alloc::string::ToString;
self.0 = value.to_string().parse().ok();
Ok(())
}
#[cfg(not(feature = "alloc"))]
{
let _ = value;
Ok(())
}
}
fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> {
self.0 = value.parse().ok();
Ok(())
}
}
let mut visitor = Extract(None);
let _ = self.0.visit(&mut visitor);
visitor.0
}
pub fn to_borrowed_str(&self) -> Option<&'v str> {
self.0.to_borrowed_str()
}
#[cfg(feature = "std")]
pub fn to_borrowed_error(&self) -> Option<&'v (dyn std::error::Error + 'static)> {
self.0.to_borrowed_error()
}
pub fn as_f64(&self) -> f64 {
let r = self.0.as_f64();
if r.is_nan() {
self.parse::<f64>().unwrap_or(f64::NAN)
} else {
r
}
}
}
impl<'v> fmt::Debug for Value<'v> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<'v> fmt::Display for Value<'v> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "std")]
{
use std::iter;
if let Some(err) = self.to_borrowed_error() {
return if let Some(root) =
iter::successors(err.source(), |ref err| err.source()).last()
{
write!(f, "{err} ({root})")
} else {
fmt::Display::fmt(err, f)
};
}
}
fmt::Display::fmt(&self.0, f)
}
}
#[cfg(feature = "sval")]
impl<'v> sval::Value for Value<'v> {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
self.0.stream(stream)
}
}
#[cfg(feature = "sval")]
impl<'v> sval_ref::ValueRef<'v> for Value<'v> {
fn stream_ref<S: sval::Stream<'v> + ?Sized>(&self, stream: &mut S) -> sval::Result {
self.0.stream_ref(stream)
}
}
#[cfg(feature = "serde")]
impl<'v> serde::Serialize for Value<'v> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
pub trait ToValue {
fn to_value(&self) -> Value<'_>;
}
pub trait FromValue<'v> {
fn from_value(value: Value<'v>) -> Option<Self>
where
Self: Sized;
}
impl<'a, T: ToValue + ?Sized> ToValue for &'a T {
fn to_value(&self) -> Value<'_> {
(**self).to_value()
}
}
impl<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Value<'_> {
match self {
Some(v) => v.to_value(),
None => Value::null(),
}
}
}
impl<'v> ToValue for Value<'v> {
fn to_value(&self) -> Value<'_> {
self.by_ref()
}
}
impl<'v> FromValue<'v> for Value<'v> {
fn from_value(value: Value<'v>) -> Option<Self> {
Some(value)
}
}
macro_rules! impl_primitive {
($($t:ty,)*) => {
$(
impl ToValue for $t {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<const N: usize> ToValue for [$t; N] {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<'v> FromValue<'v> for $t {
fn from_value(value: Value<'v>) -> Option<Self> {
value.0.try_into().ok()
}
}
impl<'v> From<$t> for Value<'v> {
fn from(value: $t) -> Self {
Value(value.into())
}
}
impl<'v> From<Option<$t>> for Value<'v> {
fn from(value: Option<$t>) -> Self {
Value(value_bag::ValueBag::from_option(value))
}
}
impl<'v, const N: usize> From<&'v [$t; N]> for Value<'v> {
fn from(value: &'v [$t; N]) -> Self {
Value(value_bag::ValueBag::from(value))
}
}
)*
};
}
macro_rules! impl_ref {
($(& $l:lifetime $t:ty,)*) => {
$(
impl ToValue for $t {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<$l, const N: usize> ToValue for [&$l $t; N] {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<$l> FromValue<$l> for &$l $t {
fn from_value(value: Value<$l>) -> Option<Self> {
value.0.try_into().ok()
}
}
impl<$l> From<&$l $t> for Value<$l> {
fn from(value: &$l $t) -> Self {
Value(value.into())
}
}
impl<$l> From<Option<&$l $t>> for Value<$l> {
fn from(value: Option<&$l $t>) -> Self {
Value(value_bag::ValueBag::from_option(value))
}
}
impl<$l, 'a, const N: usize> From<&'v [&'a $t; N]> for Value<'v> {
fn from(value: &$l [&'a $t; N]) -> Self {
Value(value_bag::ValueBag::from(value))
}
}
)*
};
}
impl_primitive!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f64, bool,);
impl_ref!(&'v str,);
impl ToValue for dyn fmt::Debug {
fn to_value(&self) -> Value<'_> {
Value(value_bag::ValueBag::from_dyn_debug(self))
}
}
impl ToValue for dyn fmt::Display {
fn to_value(&self) -> Value<'_> {
Value(value_bag::ValueBag::from_dyn_display(self))
}
}
#[cfg(feature = "std")]
impl ToValue for dyn std::error::Error + 'static {
fn to_value(&self) -> Value<'_> {
Value(value_bag::ValueBag::from_dyn_error(self))
}
}
#[cfg(feature = "std")]
impl<'v> FromValue<'v> for &'v (dyn std::error::Error + 'static) {
fn from_value(value: Value<'v>) -> Option<Self> {
value.to_borrowed_error()
}
}
#[cfg(feature = "alloc")]
mod alloc_support {
use super::*;
use alloc::{borrow::Cow, string::String, vec::Vec};
impl<'v> Value<'v> {
pub fn to_f64_sequence(&self) -> Option<Vec<f64>> {
#[derive(Default)]
struct Seq(Vec<f64>);
impl Extend<Option<f64>> for Seq {
fn extend<T: IntoIterator<Item = Option<f64>>>(&mut self, iter: T) {
self.0
.extend(iter.into_iter().map(|v| v.unwrap_or(f64::NAN)));
}
}
self.0.to_f64_seq::<Seq>().map(|seq| seq.0)
}
}
#[derive(Clone)]
pub struct OwnedValue(value_bag::OwnedValueBag);
impl<'v> Value<'v> {
pub fn to_owned(&self) -> OwnedValue {
OwnedValue(self.0.to_owned())
}
pub fn to_shared(&self) -> OwnedValue {
OwnedValue(self.0.to_shared())
}
pub fn to_cow_str(&self) -> Option<Cow<'v, str>> {
self.0.to_str()
}
}
impl OwnedValue {
pub fn by_ref<'v>(&'v self) -> Value<'v> {
Value(self.0.by_ref())
}
}
impl fmt::Debug for OwnedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for OwnedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<'a> From<&'a OwnedValue> for Value<'a> {
fn from(value: &'a OwnedValue) -> Self {
value.by_ref()
}
}
impl<'a> From<Value<'a>> for OwnedValue {
fn from(value: Value<'a>) -> Self {
value.to_owned()
}
}
impl ToValue for OwnedValue {
fn to_value(&self) -> Value<'_> {
self.by_ref()
}
}
#[cfg(feature = "sval")]
impl sval::Value for OwnedValue {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(
&'sval self,
stream: &mut S,
) -> sval::Result {
self.0.stream(stream)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for OwnedValue {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl ToValue for String {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<'v> FromValue<'v> for String {
fn from_value(value: Value<'v>) -> Option<Self> {
value.0.try_into().ok()
}
}
impl<'a> From<&'a String> for Value<'a> {
fn from(value: &'a String) -> Self {
Value(value.into())
}
}
impl<'a> From<Option<&'a String>> for Value<'a> {
fn from(value: Option<&'a String>) -> Self {
Value(value_bag::ValueBag::from_option(value))
}
}
impl<'v> ToValue for Cow<'v, str> {
fn to_value(&self) -> Value<'_> {
Value(self.into())
}
}
impl<'v> FromValue<'v> for Cow<'v, str> {
fn from_value(value: Value<'v>) -> Option<Self> {
value.0.try_into().ok()
}
}
impl<'a, 'v> From<&'a Cow<'v, str>> for Value<'a> {
fn from(value: &'a Cow<'v, str>) -> Self {
Value(value.into())
}
}
impl<'a, 'v> From<Option<&'a Cow<'v, str>>> for Value<'a> {
fn from(value: Option<&'a Cow<'v, str>>) -> Self {
Value(value_bag::ValueBag::from_option(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_f64_sequence() {
for (case, expected) in [
(Value::from(&[0.0, 1.0, 2.0]), Some(vec![0.0, 1.0, 2.0])),
(Value::from(0.0), None),
(Value::from(&[0, 1, 2]), Some(vec![0.0, 1.0, 2.0])),
(Value::from(&[] as &[f64; 0]), Some(vec![])),
(Value::from(&[true, false]), Some(vec![f64::NAN, f64::NAN])),
] {
let actual = case.to_f64_sequence();
assert_eq!(expected.is_some(), actual.is_some());
let (Some(expected), Some(actual)) = (expected, actual) else {
continue;
};
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.into_iter().zip(actual) {
if expected.is_nan() {
assert!(actual.is_nan());
} else {
assert_eq!(expected, actual);
}
}
}
}
}
}
#[cfg(feature = "alloc")]
pub use self::alloc_support::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse() {
for (case, expected) in [
(Value::from("1"), Some(1)),
(Value::from("x"), None),
#[cfg(feature = "alloc")]
(Value::from_display(&"1"), Some(1)),
#[cfg(feature = "alloc")]
(Value::from(1), Some(1)),
#[cfg(feature = "alloc")]
(Value::from(1.0), Some(1)),
] {
assert_eq!(expected, case.parse::<i32>());
}
}
#[test]
fn as_f64() {
for (case, expected) in [
(Value::from(1.0), 1.0),
(Value::from(2), 2.0),
(Value::from(true), f64::NAN),
(Value::from("1.0"), 1.0),
(Value::from("x"), f64::NAN),
] {
if expected.is_nan() {
assert!(case.as_f64().is_nan());
} else {
assert_eq!(expected, case.as_f64());
}
}
}
#[cfg(feature = "std")]
#[test]
fn error_display() {
#[derive(Debug)]
struct Error {
msg: String,
source: Option<Box<Error>>,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.msg, f)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|err| &**err as &(dyn std::error::Error + 'static))
}
}
assert_eq!(
"outer",
Value::capture_error(&Error {
msg: "outer".into(),
source: None,
})
.to_string(),
);
assert_eq!(
"outer (inner)",
Value::capture_error(&Error {
msg: "outer".into(),
source: Some(Box::new(Error {
msg: "inner".into(),
source: None,
})),
})
.to_string(),
);
assert_eq!(
"outer (root)",
Value::capture_error(&Error {
msg: "outer".into(),
source: Some(Box::new(Error {
msg: "inner".into(),
source: Some(Box::new(Error {
msg: "root".into(),
source: None,
})),
})),
})
.to_string(),
);
}
}