use alloc::boxed::Box;
use core::{
any::{self, Any, TypeId},
ptr::NonNull,
};
use crate::{
attachment::{
data::AttachmentData,
raw::{RawAttachmentMut, RawAttachmentRef},
},
handlers::{AttachmentFormattingStyle, AttachmentHandler, FormattingFunction},
util::Erased,
};
pub(crate) struct AttachmentVtable {
type_id: fn() -> TypeId,
type_name: fn() -> &'static str,
handler_type_id: fn() -> TypeId,
drop: unsafe fn(NonNull<AttachmentData<Erased>>),
display: unsafe fn(RawAttachmentRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
debug: unsafe fn(RawAttachmentRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
preferred_formatting_style:
unsafe fn(RawAttachmentRef<'_>, FormattingFunction) -> AttachmentFormattingStyle,
attachment_as_any: unsafe fn(RawAttachmentRef<'_>) -> &(dyn Any + 'static),
attachment_as_any_mut: unsafe fn(RawAttachmentMut<'_>) -> &mut (dyn Any + 'static),
}
impl AttachmentVtable {
pub(super) const fn new<A: 'static, H: AttachmentHandler<A>>() -> &'static Self {
const {
&Self {
type_id: TypeId::of::<A>,
type_name: any::type_name::<A>,
handler_type_id: TypeId::of::<H>,
drop: drop::<A>,
display: display::<A, H>,
debug: debug::<A, H>,
preferred_formatting_style: preferred_formatting_style::<A, H>,
attachment_as_any: attachment_as_any::<A>,
attachment_as_any_mut: attachment_as_any_mut::<A>,
}
}
}
#[inline]
pub(super) fn type_id(&self) -> TypeId {
(self.type_id)()
}
#[inline]
pub(super) fn type_name(&self) -> &'static str {
(self.type_name)()
}
#[inline]
pub(super) fn handler_type_id(&self) -> TypeId {
(self.handler_type_id)()
}
#[inline]
pub(super) unsafe fn drop(&self, ptr: NonNull<AttachmentData<Erased>>) {
unsafe {
(self.drop)(ptr);
}
}
#[inline]
pub(super) unsafe fn display(
&self,
ptr: RawAttachmentRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
unsafe {
(self.display)(ptr, formatter)
}
}
#[inline]
pub(super) unsafe fn debug(
&self,
ptr: RawAttachmentRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
unsafe {
(self.debug)(ptr, formatter)
}
}
#[inline]
pub(super) unsafe fn preferred_formatting_style(
&self,
ptr: RawAttachmentRef<'_>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
unsafe {
(self.preferred_formatting_style)(ptr, report_formatting_function)
}
}
#[inline]
pub(super) unsafe fn attachment_as_any<'a>(
&self,
ptr: RawAttachmentRef<'a>,
) -> &'a (dyn Any + 'static) {
unsafe {
(self.attachment_as_any)(ptr)
}
}
#[inline]
pub(super) unsafe fn attachment_as_any_mut<'a>(
&self,
ptr: RawAttachmentMut<'a>,
) -> &'a mut (dyn Any + 'static) {
unsafe {
(self.attachment_as_any_mut)(ptr)
}
}
}
unsafe fn drop<A: 'static>(ptr: NonNull<AttachmentData<Erased>>) {
let ptr: NonNull<AttachmentData<A>> = ptr.cast();
let ptr = ptr.as_ptr();
let boxed = unsafe {
Box::from_raw(ptr)
};
core::mem::drop(boxed);
}
unsafe fn display<A: 'static, H: AttachmentHandler<A>>(
ptr: RawAttachmentRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let attachment: &A = unsafe { ptr.attachment_downcast_unchecked::<A>() };
H::display(attachment, formatter)
}
unsafe fn debug<A: 'static, H: AttachmentHandler<A>>(
ptr: RawAttachmentRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let attachment: &A = unsafe { ptr.attachment_downcast_unchecked::<A>() };
H::debug(attachment, formatter)
}
unsafe fn preferred_formatting_style<A: 'static, H: AttachmentHandler<A>>(
ptr: RawAttachmentRef<'_>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
let attachment: &A = unsafe { ptr.attachment_downcast_unchecked::<A>() };
H::preferred_formatting_style(attachment, report_formatting_function)
}
unsafe fn attachment_as_any<'a, A: 'static>(ptr: RawAttachmentRef<'a>) -> &'a (dyn Any + 'static) {
let attachment: &A = unsafe { ptr.attachment_downcast_unchecked::<A>() };
attachment as &(dyn Any + 'static)
}
unsafe fn attachment_as_any_mut<'a, A: 'static>(
ptr: RawAttachmentMut<'a>,
) -> &'a mut (dyn Any + 'static) {
let attachment: &mut A = unsafe { ptr.into_attachment_downcast_unchecked::<A>() };
attachment as &mut (dyn Any + 'static)
}
#[cfg(test)]
mod tests {
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)
}
}
#[test]
fn test_attachment_vtable_eq() {
let vtable1 = AttachmentVtable::new::<i32, HandlerI32>();
let vtable2 = AttachmentVtable::new::<i32, HandlerI32>();
assert!(core::ptr::eq(vtable1, vtable2));
}
#[test]
fn test_attachment_type_id() {
let vtable = AttachmentVtable::new::<i32, HandlerI32>();
assert_eq!(vtable.type_id(), TypeId::of::<i32>());
}
#[test]
fn test_attachment_type_name() {
let vtable = AttachmentVtable::new::<i32, HandlerI32>();
assert_eq!(vtable.type_name(), core::any::type_name::<i32>());
}
}