use std::{alloc::Layout, any::TypeId, ptr::NonNull};
mod pretty_print;
mod struct_shape;
pub use struct_shape::*;
mod enum_shape;
pub use enum_shape::*;
mod scalar_shape;
pub use scalar_shape::*;
mod list_shape;
pub use list_shape::*;
mod map_shape;
pub use map_shape::*;
#[derive(Clone, Copy)]
pub struct Shape {
pub name: NameFn,
pub typeid: TypeId,
pub layout: Layout,
pub innards: Innards,
pub set_to_default: Option<SetToDefaultFn>,
pub drop_in_place: Option<DropFn>,
}
#[non_exhaustive]
#[derive(Clone, Copy)]
pub struct NameOpts {
pub recurse_ttl: isize,
}
impl Default for NameOpts {
fn default() -> Self {
Self { recurse_ttl: -1 }
}
}
impl NameOpts {
pub fn none() -> Self {
Self { recurse_ttl: 0 }
}
pub fn one() -> Self {
Self { recurse_ttl: 1 }
}
pub fn for_children(&self) -> Option<Self> {
if self.recurse_ttl > 0 {
Some(Self {
recurse_ttl: self.recurse_ttl - 1,
})
} else {
None
}
}
}
pub type NameFn = fn(f: &mut std::fmt::Formatter, opts: NameOpts) -> std::fmt::Result;
impl std::fmt::Display for Shape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(self.name)(f, NameOpts::default())
}
}
pub type SetToDefaultFn = unsafe fn(*mut u8);
pub type DropFn = unsafe fn(*mut u8);
impl PartialEq for Shape {
fn eq(&self, other: &Self) -> bool {
self.typeid == other.typeid
}
}
impl Eq for Shape {}
impl std::hash::Hash for Shape {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.typeid.hash(state);
}
}
impl Shape {
const INDENT: usize = 2;
pub unsafe fn get_scalar_contents<'a>(&self, ptr: *const u8) -> crate::ScalarContents<'a> {
match self.innards {
Innards::Scalar(scalar) => unsafe { scalar.get_contents(ptr) },
_ => panic!("Expected a scalar shape"),
}
}
pub fn known_fields(&self) -> &'static [Field] {
match self.innards {
Innards::Struct { fields } => fields,
_ => &[],
}
}
pub fn field_by_name(&self, name: &str) -> Option<&Field> {
self.known_fields().iter().find(|field| field.name == name)
}
pub fn field_by_index(&self, index: usize) -> Result<&Field, FieldError> {
match self.innards {
Innards::Struct { fields } => fields.get(index).ok_or(FieldError::IndexOutOfBounds),
_ => Err(FieldError::NotAStruct),
}
}
pub fn dangling(&self) -> NonNull<u8> {
let dang = NonNull::dangling();
let offset = dang.align_offset(self.layout.align());
unsafe { dang.byte_add(offset) }
}
pub fn variants(&self) -> &'static [Variant] {
match self.innards {
Innards::Enum { variants, repr: _ } => variants,
_ => &[],
}
}
pub fn variant_by_name(&self, name: &str) -> Option<&Variant> {
self.variants().iter().find(|variant| variant.name == name)
}
pub fn variant_by_index(&self, index: usize) -> Result<&Variant, VariantError> {
match self.innards {
Innards::Enum { variants, repr: _ } => {
variants.get(index).ok_or(VariantError::IndexOutOfBounds)
}
_ => Err(VariantError::NotAnEnum),
}
}
pub fn enum_repr(&self) -> Option<EnumRepr> {
match self.innards {
Innards::Enum { variants: _, repr } => Some(repr),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FieldError {
NoStaticFields,
NoSuchStaticField,
IndexOutOfBounds,
NotAStruct,
}
impl std::error::Error for FieldError {}
impl std::fmt::Display for FieldError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FieldError::NoStaticFields => write!(f, "No static fields available"),
FieldError::NoSuchStaticField => write!(f, "No such static field"),
FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
FieldError::NotAStruct => write!(f, "Not a struct"),
}
}
}
impl std::fmt::Debug for Shape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.pretty_print_recursive(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Innards {
Struct {
fields: &'static [Field],
},
TupleStruct {
fields: &'static [Field],
},
Tuple {
fields: &'static [Field],
},
Map {
vtable: MapVTable,
value_shape: ShapeDesc,
},
List {
vtable: ListVTable,
item_shape: ShapeDesc,
},
Transparent(ShapeDesc),
Scalar(Scalar),
Enum {
variants: &'static [Variant],
repr: EnumRepr,
},
}
#[derive(Clone, Copy)]
pub struct ShapeDesc(pub fn() -> Shape);
impl From<fn() -> Shape> for ShapeDesc {
fn from(f: fn() -> Shape) -> Self {
Self(f)
}
}
impl ShapeDesc {
#[inline(always)]
pub fn get(&self) -> Shape {
(self.0)()
}
}
impl PartialEq for ShapeDesc {
fn eq(&self, other: &Self) -> bool {
if std::ptr::eq(self.0 as *const (), other.0 as *const ()) {
true
} else {
let self_shape = self.0();
let other_shape = other.0();
self_shape == other_shape
}
}
}
impl Eq for ShapeDesc {}
impl std::hash::Hash for ShapeDesc {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.0 as *const ()).hash(state);
}
}
impl std::fmt::Debug for ShapeDesc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.get().fmt(f)
}
}