use std::any::{Any, TypeId, type_name};
use std::fmt;
trait ErasedValue: Any + Send {
fn clone_box(&self) -> Box<dyn ErasedValue>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any + Send>;
fn concrete_type_name(&self) -> &'static str;
}
impl<T> ErasedValue for T
where
T: Any + Clone + Send,
{
fn clone_box(&self) -> Box<dyn ErasedValue> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any + Send> {
self
}
fn concrete_type_name(&self) -> &'static str {
type_name::<T>()
}
}
pub(crate) struct StateValue {
inner: Box<dyn ErasedValue>,
}
impl StateValue {
pub(crate) fn new<T>(value: T) -> Self
where
T: Any + Clone + Send,
{
Self {
inner: Box::new(value),
}
}
pub(crate) fn type_id(&self) -> TypeId {
self.inner.as_any().type_id()
}
pub(crate) fn type_name(&self) -> &'static str {
self.inner.concrete_type_name()
}
pub(crate) fn is<T>(&self) -> bool
where
T: Any,
{
self.type_id() == TypeId::of::<T>()
}
pub(crate) fn downcast_ref<T>(&self) -> Option<&T>
where
T: Any,
{
self.inner.as_any().downcast_ref::<T>()
}
pub(crate) fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: Any,
{
self.inner.as_any_mut().downcast_mut::<T>()
}
pub(crate) fn downcast<T>(self) -> Result<T, Self>
where
T: Any + Send,
{
if !self.is::<T>() {
return Err(self);
}
match self.inner.into_any().downcast::<T>() {
Ok(value) => Ok(*value),
Err(_) => unreachable!("matching TypeId failed its Any downcast"),
}
}
}
impl Clone for StateValue {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone_box(),
}
}
}
impl fmt::Debug for StateValue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("StateValue")
.field("type_name", &self.type_name())
.finish_non_exhaustive()
}
}