pub(crate) mod cast;
use std::sync::Arc;
use derive_deftly::define_derive_deftly;
use downcast_rs::DowncastSync;
use serde::{Deserialize, Serialize};
use self::cast::CastTable;
pub trait Object: DowncastSync + Send + Sync + 'static {
fn expose_outside_of_session(&self) -> bool {
false
}
fn get_cast_table(&self) -> &CastTable {
&cast::EMPTY_CAST_TABLE
}
fn delegate(&self) -> Option<Arc<dyn Object>> {
None
}
}
downcast_rs::impl_downcast!(sync Object);
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ObjectId(
)
Box<str>,
);
impl AsRef<str> for ObjectId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl<T> From<T> for ObjectId
where
T: Into<Box<str>>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
pub trait ObjectArcExt {
fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T>;
fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>>;
}
impl dyn Object {
pub fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
let table = self.get_cast_table();
table.cast_object_to(self)
}
}
impl ObjectArcExt for Arc<dyn Object> {
fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
let obj: &dyn Object = self.as_ref();
obj.cast_to_trait()
}
fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>> {
let table = self.get_cast_table();
table.cast_object_to_arc(self.clone())
}
}
define_derive_deftly! {
export Object expect items:
impl<$tgens> $ttype where
$ttype: Send + Sync + 'static,
$twheres
{
#[doc(hidden)]
fn make_cast_table() -> $crate::CastTable {
${if tmeta(rpc(downcastable_to)) {
$crate::cast_table_deftness_helper!{
${tmeta(rpc(downcastable_to)) as token_stream}
}
} else {
$crate::CastTable::default()
}}
}
}
${if tmeta(rpc(delegate_type)) {
$crate::register_delegation_note!(
$ttype,
${tmeta(rpc(delegate_type )) as ty}
);
}}
${if tmeta(rpc(delegate_type)) {
#[doc = "Delegates to [`"]
#[doc = ${tmeta(rpc(delegate_type)) as str}]
#[doc = "`]"]
}}
impl<$tgens> $crate::Object for $ttype where
$ttype: Send + Sync + 'static,
$twheres
{
${if tmeta(rpc(expose_outside_of_session)) {
fn expose_outside_of_session(&self) -> bool {
true
}
}}
${if tmeta(rpc(delegate_with)) {
fn delegate(&self) -> Option<Arc<dyn $crate::Object>> {
let r: Option<Arc<${tmeta(rpc(delegate_type)) as ty}>> = (${tmeta(rpc(delegate_with)) as expr})(self);
r.map(|v| v as Arc<dyn $crate::Object>)
}
}}
fn get_cast_table(&self) -> &$crate::CastTable {
${if tgens {
use std::sync::LazyLock;
use std::sync::RwLock;
use std::collections::HashMap;
use std::any::TypeId;
static TABLES: LazyLock<RwLock<HashMap<TypeId, &'static $crate::CastTable>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
{
let tables_r = TABLES.read().expect("poisoned lock");
if let Some(table) = tables_r.get(&TypeId::of::<Self>()) {
table
} else {
drop(tables_r); TABLES
.write()
.expect("poisoned lock")
.entry(TypeId::of::<Self>())
.or_insert_with(|| Box::leak(Box::new(Self::make_cast_table())))
}
}
} else {
use std::sync::LazyLock;
static TABLE: LazyLock<$crate::CastTable> = LazyLock::new(|| $ttype::make_cast_table());
&TABLE
}}
}
}
}
pub use derive_deftly_template_Object;
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
use derive_deftly::Deftly;
#[derive(Deftly)]
#[derive_deftly(Object)]
#[deftly(rpc(downcastable_to = "HasWheels"))]
struct Bicycle {}
trait HasWheels {
fn num_wheels(&self) -> usize;
}
impl HasWheels for Bicycle {
fn num_wheels(&self) -> usize {
2
}
}
#[derive(Deftly, Default)]
#[derive_deftly(Object)]
struct Opossum {}
#[test]
fn standard_cast() {
let bike = Bicycle {};
let erased_bike: &dyn Object = &bike;
let has_wheels: &dyn HasWheels = erased_bike.cast_to_trait().unwrap();
assert_eq!(has_wheels.num_wheels(), 2);
let pogo = Opossum {};
let erased_pogo: &dyn Object = &pogo;
let has_wheels: Option<&dyn HasWheels> = erased_pogo.cast_to_trait();
assert!(has_wheels.is_none());
}
#[derive(Deftly)]
#[derive_deftly(Object)]
#[deftly(rpc(downcastable_to = "HasWheels"))]
struct Crowd<T: HasWheels + Send + Sync + 'static> {
members: Vec<T>,
}
impl<T: HasWheels + Send + Sync> HasWheels for Crowd<T> {
fn num_wheels(&self) -> usize {
self.members.iter().map(T::num_wheels).sum()
}
}
#[test]
fn generic_cast() {
let bikes = Crowd {
members: vec![Bicycle {}, Bicycle {}],
};
let erased_bikes: &dyn Object = &bikes;
let has_wheels: &dyn HasWheels = erased_bikes.cast_to_trait().unwrap();
assert_eq!(has_wheels.num_wheels(), 4);
let arc_bikes = Arc::new(bikes);
let erased_arc_bytes: Arc<dyn Object> = arc_bikes.clone();
let arc_has_wheels: Arc<dyn HasWheels> =
erased_arc_bytes.clone().cast_to_arc_trait().ok().unwrap();
assert_eq!(arc_has_wheels.num_wheels(), 4);
let ref_has_wheels: &dyn HasWheels = erased_arc_bytes.cast_to_trait().unwrap();
assert_eq!(ref_has_wheels.num_wheels(), 4);
trait SomethingElse {}
let arc_something_else: Result<Arc<dyn SomethingElse>, _> =
erased_arc_bytes.clone().cast_to_arc_trait();
let err_arc = arc_something_else.err().unwrap();
assert!(Arc::ptr_eq(&err_arc, &erased_arc_bytes));
}
#[derive(Deftly, Default)]
#[derive_deftly(Object)]
#[deftly(rpc(delegate_with = "|cage: &Self| Some(cage.possum.clone())"))]
#[deftly(rpc(delegate_type = "Opossum"))]
struct PossumCage {
possum: Arc<Opossum>,
}
const _: fn() = || {
let _: &dyn Object = &PossumCage::default();
};
}