use dyn_clone::DynClone;
use std::any::Any;
#[derive(Clone, Debug)]
pub struct Val {
pub(crate) val: Box<dyn Message>,
pub type_name: String,
}
impl PartialEq for Val {
fn eq(&self, other: &Self) -> bool {
self.val.msg_equals(other)
}
}
impl Val {
pub fn new<T: Message + 'static>(val: T) -> Self {
Val {
val: Box::new(val),
type_name: std::any::type_name::<T>().to_string(),
}
}
pub(crate) fn set_pending(&mut self) {
*self = Self::default();
}
pub(crate) fn is_pending(&self) -> bool {
self.val.msg_as_any_ref().is::<Unit>()
}
pub fn as_any(&self) -> Box<dyn Any> {
self.val.clone().msg_as_any()
}
pub fn as_any_ref(&self) -> &dyn Any {
self.val.msg_as_any_ref()
}
}
impl Default for Val {
fn default() -> Self {
Val::new(Unit)
}
}
#[derive(Clone, PartialEq, Debug)]
struct Unit;
impl std::fmt::Display for Unit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "()")
}
}
macro_rules! sign_msg_core {
() => {
fn msg_as_any(self: Box<Self>) -> Box<dyn Any>;
fn msg_as_any_ref(&self) -> &dyn Any;
fn msg_equals(&self, other: &crate::Val) -> bool;
};
}
#[cfg(not(any(feature = "print_vals", feature = "print_vals_custom")))]
pub trait Message: Send + DynClone {
sign_msg_core!();
}
#[cfg(feature = "print_vals")]
pub trait Message: Send + DynClone + std::fmt::Debug {
sign_msg_core!();
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
}
#[cfg(feature = "print_vals_custom")]
pub trait Message: Send + DynClone + std::fmt::Display {
sign_msg_core!();
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
}
macro_rules! impl_msg_core {
() => {
fn msg_as_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn msg_as_any_ref(&self) -> &dyn Any {
self
}
fn msg_equals(&self, other: &crate::Val) -> bool {
match other.as_any_ref().downcast_ref::<T>() {
Some(a) => self == a,
None => false,
}
}
};
}
#[cfg(not(any(feature = "print_vals", feature = "print_vals_custom")))]
impl<T: Send + PartialEq + DynClone + std::fmt::Debug + 'static> Message for T {
impl_msg_core!();
}
#[cfg(feature = "print_vals")]
impl<T: Send + PartialEq + DynClone + std::fmt::Debug + 'static> Message for T {
impl_msg_core!();
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{:?}",
self.msg_as_any_ref().downcast_ref::<T>().unwrap()
)
}
}
#[cfg(feature = "print_vals_custom")]
impl<T: Send + PartialEq + DynClone + std::fmt::Display + 'static> Message for T {
impl_msg_core!();
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.msg_as_any_ref().downcast_ref::<T>().unwrap())
}
}
#[cfg(all(feature = "print_vals", feature = "print_vals_custom"))]
compile_error!("features `must/print_vals` and `must/print_vals_custom` are mutually exclusive");
dyn_clone::clone_trait_object!(Message);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unit_display() {
let unit = Unit;
assert_eq!(format!("{}", unit), "()");
}
#[test]
fn test_val_partial_eq() {
let val1 = Val::new(42);
let val2 = Val::new(42);
let val3 = Val::new(43);
assert!(val1 == val2);
assert!(!(val1 == val3));
}
#[test]
fn test_val_partial_eq_different_types() {
let val1 = Val::new(42);
let val2 = Val::new("hello");
assert!(!(val1 == val2));
}
#[test]
fn test_val_partial_eq_null() {
let val1: Val = Val::default();
let val2: Val = Val::default();
assert!(val1 == val2);
}
}