blob: aaa4b07c68399a4584de3df9fabece63733d9842 [file] [log] [blame]
//! Coerce a `Value` into some concrete types.
//!
//! These operations are cheap when the captured value is a simple primitive,
//! but may end up executing arbitrary caller code if the value is complex.
//! They will also attempt to downcast erased types into a primitive where possible.
use std::any::TypeId;
use std::fmt;
use super::{Erased, Inner, Primitive, Visitor};
use crate::kv::value::{Error, Value};
impl<'v> Value<'v> {
/// Try get a `usize` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_usize(&self) -> Option<usize> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as usize)
}
/// Try get a `u8` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_u8(&self) -> Option<u8> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u8)
}
/// Try get a `u16` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_u16(&self) -> Option<u16> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u16)
}
/// Try get a `u32` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_u32(&self) -> Option<u32> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u32)
}
/// Try get a `u64` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_u64(&self) -> Option<u64> {
self.inner.cast().into_primitive().into_u64()
}
/// Try get a `isize` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_isize(&self) -> Option<isize> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as isize)
}
/// Try get a `i8` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_i8(&self) -> Option<i8> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i8)
}
/// Try get a `i16` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_i16(&self) -> Option<i16> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i16)
}
/// Try get a `i32` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_i32(&self) -> Option<i32> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i32)
}
/// Try get a `i64` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_i64(&self) -> Option<i64> {
self.inner.cast().into_primitive().into_i64()
}
/// Try get a `f32` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_f32(&self) -> Option<f32> {
self.inner
.cast()
.into_primitive()
.into_f64()
.map(|v| v as f32)
}
/// Try get a `f64` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_f64(&self) -> Option<f64> {
self.inner.cast().into_primitive().into_f64()
}
/// Try get a `bool` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_bool(&self) -> Option<bool> {
self.inner.cast().into_primitive().into_bool()
}
/// Try get a `char` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones.
pub fn to_char(&self) -> Option<char> {
self.inner.cast().into_primitive().into_char()
}
/// Try get a `str` from this value.
///
/// This method is cheap for primitive types. It won't allocate an owned
/// `String` if the value is a complex type.
pub fn to_borrowed_str(&self) -> Option<&str> {
self.inner.cast().into_primitive().into_borrowed_str()
}
}
impl<'v> Inner<'v> {
/// Cast the inner value to another type.
fn cast(self) -> Cast<'v> {
struct CastVisitor<'v>(Cast<'v>);
impl<'v> Visitor<'v> for CastVisitor<'v> {
fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> {
Ok(())
}
fn u64(&mut self, v: u64) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Unsigned(v));
Ok(())
}
fn i64(&mut self, v: i64) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Signed(v));
Ok(())
}
fn f64(&mut self, v: f64) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Float(v));
Ok(())
}
fn bool(&mut self, v: bool) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Bool(v));
Ok(())
}
fn char(&mut self, v: char) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Char(v));
Ok(())
}
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Str(v));
Ok(())
}
#[cfg(not(feature = "std"))]
fn str(&mut self, _: &str) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "std")]
fn str(&mut self, v: &str) -> Result<(), Error> {
self.0 = Cast::String(v.into());
Ok(())
}
fn none(&mut self) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::None);
Ok(())
}
#[cfg(feature = "kv_unstable_sval")]
fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
self.0 = super::sval::cast(v);
Ok(())
}
}
// Try downcast an erased value first
// It also lets us avoid the Visitor infrastructure for simple primitives
let primitive = match self {
Inner::Primitive(value) => Some(value),
Inner::Fill(value) => value.downcast_primitive(),
Inner::Debug(value) => value.downcast_primitive(),
Inner::Display(value) => value.downcast_primitive(),
#[cfg(feature = "sval")]
Inner::Sval(value) => value.downcast_primitive(),
};
primitive.map(Cast::Primitive).unwrap_or_else(|| {
// If the erased value isn't a primitive then we visit it
let mut cast = CastVisitor(Cast::Primitive(Primitive::None));
let _ = self.visit(&mut cast);
cast.0
})
}
}
pub(super) enum Cast<'v> {
Primitive(Primitive<'v>),
#[cfg(feature = "std")]
String(String),
}
impl<'v> Cast<'v> {
fn into_primitive(self) -> Primitive<'v> {
match self {
Cast::Primitive(value) => value,
#[cfg(feature = "std")]
_ => Primitive::None,
}
}
}
impl<'v> Primitive<'v> {
fn into_borrowed_str(self) -> Option<&'v str> {
if let Primitive::Str(value) = self {
Some(value)
} else {
None
}
}
fn into_u64(self) -> Option<u64> {
match self {
Primitive::Unsigned(value) => Some(value),
Primitive::Signed(value) => Some(value as u64),
Primitive::Float(value) => Some(value as u64),
_ => None,
}
}
fn into_i64(self) -> Option<i64> {
match self {
Primitive::Signed(value) => Some(value),
Primitive::Unsigned(value) => Some(value as i64),
Primitive::Float(value) => Some(value as i64),
_ => None,
}
}
fn into_f64(self) -> Option<f64> {
match self {
Primitive::Float(value) => Some(value),
Primitive::Unsigned(value) => Some(value as f64),
Primitive::Signed(value) => Some(value as f64),
_ => None,
}
}
fn into_char(self) -> Option<char> {
if let Primitive::Char(value) = self {
Some(value)
} else {
None
}
}
fn into_bool(self) -> Option<bool> {
if let Primitive::Bool(value) = self {
Some(value)
} else {
None
}
}
}
impl<'v, T: ?Sized + 'static> Erased<'v, T> {
// NOTE: This function is a perfect candidate for memoization
// The outcome could be stored in a `Cell<Primitive>`
fn downcast_primitive(self) -> Option<Primitive<'v>> {
macro_rules! type_ids {
($($value:ident : $ty:ty => $cast:expr,)*) => {{
struct TypeIds;
impl TypeIds {
fn downcast_primitive<'v, T: ?Sized>(&self, value: Erased<'v, T>) -> Option<Primitive<'v>> {
$(
if TypeId::of::<$ty>() == value.type_id {
let $value = unsafe { value.downcast_unchecked::<$ty>() };
return Some(Primitive::from($cast));
}
)*
None
}
}
TypeIds
}};
}
let type_ids = type_ids![
value: usize => *value as u64,
value: u8 => *value as u64,
value: u16 => *value as u64,
value: u32 => *value as u64,
value: u64 => *value,
value: isize => *value as i64,
value: i8 => *value as i64,
value: i16 => *value as i64,
value: i32 => *value as i64,
value: i64 => *value,
value: f32 => *value as f64,
value: f64 => *value,
value: char => *value,
value: bool => *value,
value: &str => *value,
];
type_ids.downcast_primitive(self)
}
}
#[cfg(feature = "std")]
mod std_support {
use super::*;
use std::borrow::Cow;
impl<'v> Value<'v> {
/// Try get a `usize` from this value.
///
/// This method is cheap for primitive types, but may call arbitrary
/// serialization implementations for complex ones. If the serialization
/// implementation produces a short lived string it will be allocated.
pub fn to_str(&self) -> Option<Cow<str>> {
self.inner.cast().into_str()
}
}
impl<'v> Cast<'v> {
pub(super) fn into_str(self) -> Option<Cow<'v, str>> {
match self {
Cast::Primitive(Primitive::Str(value)) => Some(value.into()),
Cast::String(value) => Some(value.into()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::kv::ToValue;
#[test]
fn primitive_cast() {
assert_eq!(
"a string",
"a string"
.to_owned()
.to_value()
.to_borrowed_str()
.expect("invalid value")
);
assert_eq!(
"a string",
&*"a string".to_value().to_str().expect("invalid value")
);
assert_eq!(
"a string",
&*"a string"
.to_owned()
.to_value()
.to_str()
.expect("invalid value")
);
}
}
}
#[cfg(test)]
mod tests {
use crate::kv::ToValue;
#[test]
fn primitive_cast() {
assert_eq!(
"a string",
"a string"
.to_value()
.to_borrowed_str()
.expect("invalid value")
);
assert_eq!(
"a string",
Some("a string")
.to_value()
.to_borrowed_str()
.expect("invalid value")
);
assert_eq!(1u8, 1u64.to_value().to_u8().expect("invalid value"));
assert_eq!(1u16, 1u64.to_value().to_u16().expect("invalid value"));
assert_eq!(1u32, 1u64.to_value().to_u32().expect("invalid value"));
assert_eq!(1u64, 1u64.to_value().to_u64().expect("invalid value"));
assert_eq!(1usize, 1u64.to_value().to_usize().expect("invalid value"));
assert_eq!(-1i8, -1i64.to_value().to_i8().expect("invalid value"));
assert_eq!(-1i16, -1i64.to_value().to_i16().expect("invalid value"));
assert_eq!(-1i32, -1i64.to_value().to_i32().expect("invalid value"));
assert_eq!(-1i64, -1i64.to_value().to_i64().expect("invalid value"));
assert_eq!(-1isize, -1i64.to_value().to_isize().expect("invalid value"));
assert!(1f32.to_value().to_f32().is_some(), "invalid value");
assert!(1f64.to_value().to_f64().is_some(), "invalid value");
assert_eq!(1u32, 1i64.to_value().to_u32().expect("invalid value"));
assert_eq!(1i32, 1u64.to_value().to_i32().expect("invalid value"));
assert!(1f32.to_value().to_i32().is_some(), "invalid value");
assert_eq!('a', 'a'.to_value().to_char().expect("invalid value"));
assert_eq!(true, true.to_value().to_bool().expect("invalid value"));
}
}