use alloc::boxed::Box;
use core::{
any::{Any, TypeId},
ptr::NonNull,
};
use crate::{
attachment::data::AttachmentData,
handlers::{AttachmentFormattingStyle, AttachmentHandler, FormattingFunction},
util::Erased,
};
#[repr(transparent)]
pub struct RawAttachment {
ptr: NonNull<AttachmentData<Erased>>,
}
impl RawAttachment {
#[inline]
pub fn new<A, H>(attachment: A) -> Self
where
A: 'static,
H: AttachmentHandler<A>,
{
let ptr = Box::new(AttachmentData::new::<H>(attachment));
let ptr: *mut AttachmentData<A> = Box::into_raw(ptr);
let ptr: *mut AttachmentData<Erased> = ptr.cast::<AttachmentData<Erased>>();
let ptr: NonNull<AttachmentData<Erased>> = unsafe {
NonNull::new_unchecked(ptr)
};
Self {
ptr,
}
}
#[inline]
pub fn as_ref(&self) -> RawAttachmentRef<'_> {
RawAttachmentRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn as_mut(&mut self) -> RawAttachmentMut<'_> {
RawAttachmentMut {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
}
impl core::ops::Drop for RawAttachment {
#[inline]
fn drop(&mut self) {
let vtable = self.as_ref().vtable();
unsafe {
vtable.drop(self.ptr);
}
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RawAttachmentRef<'a> {
ptr: NonNull<AttachmentData<Erased>>,
_marker: core::marker::PhantomData<&'a AttachmentData<Erased>>,
}
impl<'a> RawAttachmentRef<'a> {
#[inline]
pub(super) unsafe fn cast_inner<A>(self) -> &'a AttachmentData<A> {
debug_assert_eq!(self.vtable().type_id(), TypeId::of::<A>());
let this = self.ptr.cast::<AttachmentData<A>>();
unsafe { this.as_ref() }
}
#[inline]
pub(super) fn as_ptr(self) -> *const AttachmentData<Erased> {
self.ptr.as_ptr()
}
#[inline]
pub fn attachment_type_id(self) -> TypeId {
self.vtable().type_id()
}
#[inline]
pub fn attachment_type_name(self) -> &'static str {
self.vtable().type_name()
}
#[inline]
pub fn attachment_handler_type_id(self) -> TypeId {
self.vtable().handler_type_id()
}
#[inline]
pub fn attachment_display(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let vtable = self.vtable();
unsafe {
vtable.display(self, formatter)
}
}
#[inline]
pub fn attachment_debug(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let vtable = self.vtable();
unsafe {
vtable.debug(self, formatter)
}
}
#[inline]
pub fn attachment_as_any(self) -> &'a (dyn Any + 'static) {
let vtable = self.vtable();
unsafe {
vtable.attachment_as_any(self)
}
}
#[inline]
pub fn preferred_formatting_style(
self,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
let vtable = self.vtable();
unsafe {
vtable.preferred_formatting_style(self, report_formatting_function)
}
}
}
#[repr(transparent)]
pub struct RawAttachmentMut<'a> {
ptr: NonNull<AttachmentData<Erased>>,
_marker: core::marker::PhantomData<&'a mut AttachmentData<Erased>>,
}
impl<'a> RawAttachmentMut<'a> {
#[inline]
pub(super) unsafe fn cast_inner<A>(self) -> &'a mut AttachmentData<A> {
debug_assert_eq!(self.as_ref().vtable().type_id(), TypeId::of::<A>());
let mut this = self.ptr.cast::<AttachmentData<A>>();
unsafe { this.as_mut() }
}
#[inline]
pub fn reborrow<'b>(&'b mut self) -> RawAttachmentMut<'b> {
RawAttachmentMut {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn as_ref<'b: 'a>(&'b self) -> RawAttachmentRef<'b> {
RawAttachmentRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn into_attachment_as_any_mut(self) -> &'a mut (dyn Any + 'static) {
let vtable = self.as_ref().vtable();
unsafe {
vtable.attachment_as_any_mut(self)
}
}
#[inline]
pub fn into_ref(self) -> RawAttachmentRef<'a> {
RawAttachmentRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::String;
use super::*;
use crate::handlers::AttachmentHandler;
struct HandlerI32;
impl AttachmentHandler<i32> for HandlerI32 {
fn display(value: &i32, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(value, formatter)
}
fn debug(value: &i32, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(value, formatter)
}
}
struct HandlerString;
impl AttachmentHandler<String> for HandlerString {
fn display(value: &String, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(value, formatter)
}
fn debug(value: &String, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(value, formatter)
}
}
#[test]
fn test_raw_attachment_size() {
assert_eq!(
core::mem::size_of::<RawAttachment>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawAttachment>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawAttachment>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawAttachment>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawAttachment>>>(),
core::mem::size_of::<Option<usize>>()
);
assert_eq!(
core::mem::size_of::<RawAttachmentRef<'_>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawAttachmentRef<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawAttachmentRef<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawAttachmentRef<'_>>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawAttachmentRef<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);
assert_eq!(
core::mem::size_of::<RawAttachmentMut<'_>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawAttachmentMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawAttachmentMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawAttachmentMut<'_>>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawAttachmentMut<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);
}
#[test]
fn test_raw_attachment_get_refs() {
let attachment = RawAttachment::new::<i32, HandlerI32>(100);
let attachment_ref = attachment.as_ref();
let ptr1 = attachment_ref.as_ptr();
let ptr2 = attachment_ref.as_ptr();
assert_eq!(ptr1, ptr2);
}
#[test]
fn test_raw_attachment_downcast() {
let int_attachment = RawAttachment::new::<i32, HandlerI32>(42);
let string_attachment = RawAttachment::new::<String, HandlerString>(String::from("test"));
let int_ref = int_attachment.as_ref();
let string_ref = string_attachment.as_ref();
assert_eq!(int_ref.attachment_type_id(), TypeId::of::<i32>());
assert_eq!(string_ref.attachment_type_id(), TypeId::of::<String>());
assert!(!core::ptr::eq(int_ref.vtable(), string_ref.vtable()));
}
#[test]
fn test_raw_attachment_display_debug() {
use alloc::format;
let int_attachment = RawAttachment::new::<i32, HandlerI32>(42);
let string_attachment = RawAttachment::new::<String, HandlerString>(String::from("test"));
let int_ref = int_attachment.as_ref();
let string_ref = string_attachment.as_ref();
let display_int = format!(
"{}",
TestDisplayFormatter::new(|f| int_ref.attachment_display(f))
);
let display_string = format!(
"{}",
TestDisplayFormatter::new(|f| string_ref.attachment_display(f))
);
assert_eq!(display_int, "42");
assert_eq!(display_string, "test");
let debug_int = format!(
"{}",
TestDisplayFormatter::new(|f| int_ref.attachment_debug(f))
);
let debug_string = format!(
"{}",
TestDisplayFormatter::new(|f| string_ref.attachment_debug(f))
);
assert_eq!(debug_int, "42");
assert_eq!(debug_string, "\"test\"");
}
struct TestDisplayFormatter<F> {
formatter_fn: F,
}
impl<F> TestDisplayFormatter<F>
where
F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
{
fn new(formatter_fn: F) -> Self {
Self { formatter_fn }
}
}
impl<F> core::fmt::Display for TestDisplayFormatter<F>
where
F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.formatter_fn)(f)
}
}
#[test]
fn test_send_sync() {
static_assertions::assert_not_impl_any!(RawAttachment: Send, Sync);
static_assertions::assert_not_impl_any!(RawAttachmentRef<'_>: Send, Sync);
static_assertions::assert_not_impl_any!(RawAttachmentMut<'_>: Send, Sync);
}
#[test]
fn test_raw_attachment_as_any() {
let attachment = RawAttachment::new::<i32, HandlerI32>(42);
let any = attachment.as_ref().attachment_as_any();
assert_eq!(any.downcast_ref::<i32>(), Some(&42));
assert!(any.downcast_ref::<String>().is_none());
}
#[test]
fn test_raw_attachment_as_any_mut() {
let mut attachment = RawAttachment::new::<i32, HandlerI32>(41);
let any = attachment.as_mut().into_attachment_as_any_mut();
*any.downcast_mut::<i32>().unwrap() += 1;
assert_eq!(
unsafe { *attachment.as_ref().attachment_downcast_unchecked::<i32>() },
42
);
}
}