use std::any::Any;
use std::any::TypeId;
use std::cmp::Ordering;
use std::fmt::Debug;
use crate::attributes::linked_list::LinkedList;
mod linked_list;
trait AttributeTrait: Any + Send + Sync + Debug {
fn any_ref(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn AttributeTrait) -> bool;
fn dyn_cmp(&self, other: &dyn AttributeTrait) -> Ordering;
}
impl<T: Any + Send + Sync + Eq + Ord + Debug> AttributeTrait for T {
fn any_ref(&self) -> &dyn Any {
self
}
fn dyn_eq(&self, other: &dyn AttributeTrait) -> bool {
if let Some(other) = other.any_ref().downcast_ref::<T>() {
self == other
} else {
false
}
}
fn dyn_cmp(&self, other: &dyn AttributeTrait) -> Ordering {
if let Some(other) = other.any_ref().downcast_ref::<T>() {
self.cmp(other)
} else {
TypeId::of::<T>().cmp(&other.any_ref().type_id())
}
}
}
#[derive(Debug)]
struct AttributeValue {
inner: Box<dyn AttributeTrait>,
}
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
self.inner.dyn_eq(other.inner.as_ref())
}
}
impl Eq for AttributeValue {}
impl PartialOrd for AttributeValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AttributeValue {
fn cmp(&self, other: &Self) -> Ordering {
self.inner.dyn_cmp(other.inner.as_ref())
}
}
#[derive(Clone, Default, Debug)]
pub struct Attributes {
elements: LinkedList<TypeId, AttributeValue>,
}
impl Attributes {
pub fn new() -> Self {
Self::default()
}
pub fn add<T: Send + Sync + Eq + Ord + Debug + 'static>(&self, value: T) -> Self {
let id = TypeId::of::<T>();
Attributes {
elements: self.elements.add(
id,
AttributeValue {
inner: Box::new(value),
},
),
}
}
pub fn get<T: 'static>(&self) -> Option<&T> {
let id = TypeId::of::<T>();
self.elements
.get(&id)
.and_then(|v| v.inner.any_ref().downcast_ref())
}
}
impl PartialEq for Attributes {
fn eq(&self, other: &Self) -> bool {
let mut v1: Vec<_> = self.elements.iter().collect();
let mut v2: Vec<_> = other.elements.iter().collect();
if v1.len() != v2.len() {
return false;
}
v1.sort();
v2.sort();
v1 == v2
}
}
impl Eq for Attributes {}
impl PartialOrd for Attributes {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Attributes {
fn cmp(&self, other: &Self) -> Ordering {
let mut v1: Vec<_> = self.elements.iter().collect();
let mut v2: Vec<_> = other.elements.iter().collect();
v1.sort();
v2.sort();
v1.cmp(&v2)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eq() {
let a1 = Attributes::new().add(10i32);
let a2 = a1.clone();
let a3 = Attributes::new().add(10i32);
assert_eq!(a1, a2);
assert_eq!(a1, a3);
let a4 = Attributes::new().add(10i32).add("foo".to_string());
assert_ne!(a1, a4);
}
#[test]
fn test_attributes() {
let attrs = Attributes::new();
let attrs = attrs.add(42i32);
let attrs = attrs.add("hello".to_string());
assert_eq!(attrs.get::<i32>(), Some(&42));
assert_eq!(attrs.get::<String>(), Some(&"hello".to_string()));
assert_eq!(attrs.get::<bool>(), None);
}
#[test]
fn test_persistence() {
let a1 = Attributes::new().add(10i32);
let a2 = a1.add(20u32);
assert_eq!(a1.get::<i32>(), Some(&10));
assert_eq!(a1.get::<u32>(), None);
assert_eq!(a2.get::<i32>(), Some(&10));
assert_eq!(a2.get::<u32>(), Some(&20));
}
#[test]
fn test_overwrite() {
let a1 = Attributes::new().add(10i32);
let a2 = a1.add(20i32);
assert_eq!(a1.get::<i32>(), Some(&10));
assert_eq!(a2.get::<i32>(), Some(&20));
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Priority {
weight: u64,
name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Config {
retries: u32,
timeout_ms: u64,
}
#[test]
fn test_custom_structs() {
let p = Priority {
weight: 123,
name: "alice".into(),
};
let config = Config {
retries: 3,
timeout_ms: 1000,
};
let attrs = Attributes::new().add(p.clone()).add(config.clone());
assert_eq!(attrs.get::<Priority>(), Some(&p));
assert_eq!(attrs.get::<Config>(), Some(&config));
let p2 = Priority {
weight: 456,
name: "bob".into(),
};
let attrs2 = attrs.add(p2.clone());
assert_eq!(attrs2.get::<Priority>(), Some(&p2));
assert_eq!(attrs2.get::<Config>(), Some(&config));
assert_eq!(attrs.get::<Priority>(), Some(&p));
}
}