use crate::std::{any::TypeId, fmt};
#[cfg(feature = "std")]
use crate::std::{borrow::ToOwned, string::String};
use super::{Inner, Primitive, Visitor};
use crate::{Error, ValueBag};
mod primitive;
pub(super) fn type_id<T: 'static>() -> TypeId {
TypeId::of::<T>()
}
pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option<ValueBag<'v>> {
primitive::from_any(value).map(|primitive| ValueBag {
inner: Inner::Primitive { value: primitive },
})
}
impl<'v> ValueBag<'v> {
pub fn to_usize(&self) -> Option<usize> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as usize)
}
pub fn to_u8(&self) -> Option<u8> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u8)
}
pub fn to_u16(&self) -> Option<u16> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u16)
}
pub fn to_u32(&self) -> Option<u32> {
self.inner
.cast()
.into_primitive()
.into_u64()
.map(|v| v as u32)
}
pub fn to_u64(&self) -> Option<u64> {
self.inner.cast().into_primitive().into_u64()
}
pub fn to_isize(&self) -> Option<isize> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as isize)
}
pub fn to_i8(&self) -> Option<i8> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i8)
}
pub fn to_i16(&self) -> Option<i16> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i16)
}
pub fn to_i32(&self) -> Option<i32> {
self.inner
.cast()
.into_primitive()
.into_i64()
.map(|v| v as i32)
}
pub fn to_i64(&self) -> Option<i64> {
self.inner.cast().into_primitive().into_i64()
}
pub fn to_f32(&self) -> Option<f32> {
self.inner
.cast()
.into_primitive()
.into_f64()
.map(|v| v as f32)
}
pub fn to_f64(&self) -> Option<f64> {
self.inner.cast().into_primitive().into_f64()
}
pub fn to_bool(&self) -> Option<bool> {
self.inner.cast().into_primitive().into_bool()
}
pub fn to_char(&self) -> Option<char> {
self.inner.cast().into_primitive().into_char()
}
pub fn to_borrowed_str(&self) -> Option<&str> {
self.inner.cast().into_primitive().into_borrowed_str()
}
pub fn is<T: 'static>(&self) -> bool {
self.downcast_ref::<T>().is_some()
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
let target = TypeId::of::<T>();
match self.inner {
Inner::Debug {
type_id: Some(type_id),
value,
} if type_id == target => Some(unsafe { &*(value as *const _ as *const T) }),
Inner::Display {
type_id: Some(type_id),
value,
} if type_id == target => Some(unsafe { &*(value as *const _ as *const T) }),
#[cfg(feature = "std")]
Inner::Error {
type_id: Some(type_id),
value,
} if type_id == target => Some(unsafe { &*(value as *const _ as *const T) }),
#[cfg(feature = "sval1")]
Inner::Sval1 {
type_id: Some(type_id),
value,
} if type_id == target => Some(unsafe { &*(value as *const _ as *const T) }),
#[cfg(feature = "serde1")]
Inner::Serde1 {
type_id: Some(type_id),
value,
} if type_id == target => Some(unsafe { &*(value as *const _ as *const T) }),
_ => None,
}
}
}
impl<'v> Inner<'v> {
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(())
}
#[cfg(feature = "std")]
fn str(&mut self, s: &str) -> Result<(), Error> {
self.0 = Cast::String(s.to_owned());
Ok(())
}
#[cfg(not(feature = "std"))]
fn str(&mut self, _: &str) -> Result<(), Error> {
Ok(())
}
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::Str(v));
Ok(())
}
fn none(&mut self) -> Result<(), Error> {
self.0 = Cast::Primitive(Primitive::None);
Ok(())
}
#[cfg(feature = "std")]
fn error(&mut self, _: &dyn super::error::Error) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "sval1")]
fn sval1(&mut self, v: &dyn super::sval::v1::Value) -> Result<(), Error> {
self.0 = super::sval::v1::cast(v);
Ok(())
}
#[cfg(feature = "serde1")]
fn serde1(&mut self, v: &dyn super::serde::v1::Serialize) -> Result<(), Error> {
self.0 = super::serde::v1::cast(v);
Ok(())
}
}
if let Inner::Primitive { value } = self {
Cast::Primitive(value)
} else {
let mut cast = CastVisitor(Cast::Primitive(Primitive::None));
let _ = self.visit(&mut cast);
cast.0
}
}
}
pub(in crate::internal) 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
}
}
}
#[cfg(feature = "std")]
mod std_support {
use super::*;
use crate::std::borrow::Cow;
impl<'v> ValueBag<'v> {
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 {
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);
use crate::{std::borrow::ToOwned, test::IntoValueBag};
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn primitive_cast() {
let short_lived = "a string".to_owned();
assert_eq!(
"a string",
(&*short_lived)
.into_value_bag()
.to_borrowed_str()
.expect("invalid value")
);
assert_eq!(
"a string",
&*"a string".into_value_bag().to_str().expect("invalid value")
);
assert_eq!(
"a string",
(&*short_lived)
.into_value_bag()
.to_str()
.expect("invalid value")
);
}
}
}
#[cfg(test)]
mod tests {
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);
use crate::test::IntoValueBag;
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn primitive_cast() {
assert_eq!(
"a string",
"a string"
.into_value_bag()
.to_borrowed_str()
.expect("invalid value")
);
assert_eq!(1u8, 1u64.into_value_bag().to_u8().expect("invalid value"));
assert_eq!(1u16, 1u64.into_value_bag().to_u16().expect("invalid value"));
assert_eq!(1u32, 1u64.into_value_bag().to_u32().expect("invalid value"));
assert_eq!(1u64, 1u64.into_value_bag().to_u64().expect("invalid value"));
assert_eq!(
1usize,
1u64.into_value_bag().to_usize().expect("invalid value")
);
assert_eq!(-1i8, -1i64.into_value_bag().to_i8().expect("invalid value"));
assert_eq!(
-1i16,
-1i64.into_value_bag().to_i16().expect("invalid value")
);
assert_eq!(
-1i32,
-1i64.into_value_bag().to_i32().expect("invalid value")
);
assert_eq!(
-1i64,
-1i64.into_value_bag().to_i64().expect("invalid value")
);
assert_eq!(
-1isize,
-1i64.into_value_bag().to_isize().expect("invalid value")
);
assert!(1f32.into_value_bag().to_f32().is_some(), "invalid value");
assert!(1f64.into_value_bag().to_f64().is_some(), "invalid value");
assert_eq!(1u32, 1i64.into_value_bag().to_u32().expect("invalid value"));
assert_eq!(1i32, 1u64.into_value_bag().to_i32().expect("invalid value"));
assert!(1f32.into_value_bag().to_i32().is_some(), "invalid value");
assert_eq!('a', 'a'.into_value_bag().to_char().expect("invalid value"));
assert_eq!(
true,
true.into_value_bag().to_bool().expect("invalid value")
);
}
}