use std::any::{Any, TypeId};
pub trait ExpressionLike: Send + Sync {
fn clone_box(&self) -> Box<dyn ExpressionLike>;
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId;
fn type_name(&self) -> &'static str;
}
impl<T> ExpressionLike for T
where
T: Clone + Send + Sync + 'static,
{
fn clone_box(&self) -> Box<dyn ExpressionLike> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
}
pub struct AnyExpression {
inner: Box<dyn ExpressionLike>,
type_id: TypeId,
type_name: &'static str,
}
impl AnyExpression {
pub fn new<T: Clone + Send + Sync + 'static>(expr: T) -> Self {
Self {
inner: Box::new(expr),
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
}
}
pub fn downcast<T: Clone + 'static>(self) -> Result<T, Self> {
if self.type_id != TypeId::of::<T>() {
return Err(self);
}
let any = self.inner.as_any();
match any.downcast_ref::<T>() {
Some(expr) => Ok(expr.clone()),
None => Err(self),
}
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
if self.type_id != TypeId::of::<T>() {
return None;
}
self.inner.as_any().downcast_ref::<T>()
}
pub fn is_type<T: 'static>(&self) -> bool {
self.type_id == TypeId::of::<T>()
}
pub fn type_name(&self) -> &str {
self.type_name
}
pub fn type_id(&self) -> TypeId {
self.type_id
}
pub fn into_any(self) -> Box<dyn Any> {
Box::new(self)
}
}
impl Clone for AnyExpression {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone_box(),
type_id: self.type_id,
type_name: self.type_name,
}
}
}
impl std::fmt::Debug for AnyExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnyExpression")
.field("type_name", &self.type_name())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct TestExpr {
value: i32,
}
#[derive(Debug, Clone, PartialEq)]
struct OtherExpr {
text: String,
}
#[test]
fn test_any_expression_creation_and_downcast() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr.clone());
assert_eq!(any.type_name(), std::any::type_name::<TestExpr>());
assert!(any.is_type::<TestExpr>());
let recovered = any.downcast::<TestExpr>().unwrap();
assert_eq!(recovered, expr);
}
#[test]
fn test_any_expression_downcast_ref() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr.clone());
let expr_ref = any.downcast_ref::<TestExpr>().unwrap();
assert_eq!(expr_ref, &expr);
assert!(any.is_type::<TestExpr>());
}
#[test]
fn test_any_expression_downcast_wrong_type() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr);
let result = any.downcast::<OtherExpr>();
assert!(result.is_err());
}
#[test]
fn test_any_expression_is_type() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr);
assert!(any.is_type::<TestExpr>());
assert!(!any.is_type::<OtherExpr>());
}
#[test]
fn test_any_expression_clone() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr.clone());
let cloned = any.clone();
assert_eq!(cloned.type_name(), any.type_name());
assert_eq!(cloned.type_id(), any.type_id());
let recovered1 = any.downcast::<TestExpr>().unwrap();
let recovered2 = cloned.downcast::<TestExpr>().unwrap();
assert_eq!(recovered1, recovered2);
}
#[test]
fn test_any_expression_debug() {
let expr = TestExpr { value: 42 };
let any = AnyExpression::new(expr);
let debug_str = format!("{:?}", any);
assert!(debug_str.contains("AnyExpression"));
assert!(debug_str.contains("type_name"));
}
}