use derive_more::*;
use downcast_rs::{impl_downcast, DowncastSync};
use dyn_clone::{clone_trait_object, DynClone};
pub trait DataObject: DowncastSync + Send + Sync + DynClone + 'static {
fn try_send(&mut self, value: Box<dyn DataObject>) -> Result<(), Box<dyn DataObject>> {
Err(value)
}
}
clone_trait_object!(DataObject);
impl_downcast!(sync DataObject);
pub trait DataObjectExt {
fn send(&mut self, value: Box<dyn DataObject>);
}
impl DataObjectExt for Box<dyn DataObject> {
fn send(&mut self, value: Box<dyn DataObject>) {
if let Err(value) = self.try_send(value) {
*self = value;
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Empty;
impl DataObject for Empty {}
#[derive(Clone, PartialEq)]
pub struct Container<T: Send + Sync + Clone + 'static>(pub T);
impl<T: Send + Sync + Clone + 'static> DataObject for Container<T> {}
#[derive(Deref, DerefMut, Clone)]
#[deref(forward)]
#[deref_mut(forward)]
pub struct Collection(pub Vec<Box<dyn DataObject>>);
impl DataObject for Collection {
fn try_send(&mut self, value: Box<dyn DataObject>) -> Result<(), Box<dyn DataObject>> {
self.0.push(value);
Ok(())
}
}
impl Collection {
pub fn new() -> Self {
Self(vec![])
}
}
impl Default for Collection {
fn default() -> Self {
Collection::new()
}
}
impl DataObject for serde_json::Value {}
#[cfg(test)]
mod tests {
use super::*;
use bpxe_internal_macros as bpxe_im;
#[bpxe_im::test]
fn send_to_empty_replaces() {
let mut data_object: Box<dyn DataObject> = Box::new(Empty);
data_object.send(Box::new(Container(1u8)));
assert!(matches!(
data_object.downcast::<Container<u8>>().map(|v| *v),
Ok(Container(1u8))
));
}
#[bpxe_im::test]
fn send_to_container_replaces() {
let mut data_object: Box<dyn DataObject> = Box::new(Container(true));
data_object.send(Box::new(Empty));
assert!(matches!(
data_object.downcast::<Empty>().map(|v| *v),
Ok(Empty)
));
}
#[bpxe_im::test]
fn send_to_collection_appends() {
let mut data_object: Box<dyn DataObject> = Box::new(Collection::new());
data_object.send(Box::new(Empty));
assert!(matches!(
data_object.downcast_ref::<Collection>(),
Some(Collection(v)) if v[0].downcast_ref::<Empty>().is_some()
));
data_object.send(Box::new(Container(1u8)));
assert!(matches!(
data_object.downcast_ref::<Collection>(),
Some(Collection(v)) if matches!(v[1].downcast_ref::<Container<u8>>(), Some(Container(1)))
));
}
}