oors 0.9.2

Adding cross-crate inheritance features to Rust structs
Documentation
/*
 * Copyright (c) 2025 eligamii.
 * Licenced under the MIT licence. See the LICENCE file in the project for full licence information
 */

//! All traits and structs required to make inheritance work
//! # Manually implementing any trait of this crate
//! [`IsA`], [`Object`], any generated traits (starting with `__`), and `__oors_recursive_impl_*!()` generated macros
//! expect an exact layout from the implementing struct `S`:
//! - `IsA<X>`, `__XAccessors` and `__XBuilder`: `S` must have `X` at offset 0
//! - `Object<S>`: [`Object::uninit_selective_drop()`] must actually only drop individual fields of
//! `S` then call [`Object::uninit_selective_drop()`] of the immediate parent of `S` if one, and [`Object::type_ids()`]
//!  must only "declare" types which are at offset 0 of S
//! - `__oors_recursive_impl_X!(S)`: The same as implementing `IsA<X>`, with the additional requirement
//!  of `S` having to have **every** parent of X at offset 0 (`See #[object]`'s documentation)
//!
//! NOTE: You will also have to `impl IsA<S> for S`

use std::any::TypeId;
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};

/// Marks an "is a" relationship between types implementing this trait
/// and [`T`].
pub unsafe trait IsA<T> {}

unsafe impl<T> IsA<()> for T where T: Object {}
unsafe impl Object for () {}


/// A simple struct containing data of type [`T`] along with information about the actual type of [`T`]
/// in case this struct was cast to another compatible type using
/// [`Typed::try_cast_ref()`], [`Typed::try_cast_mut()`], [`Typed::upcast_ref()`] and [`Typed::upcast_mut`].
/// This type [`Deref`] and [`AsRef`] (+ mut variants) into [`T`]
#[repr(C)]
#[derive(Clone)] // Every other impls are impl for T
pub struct Typed<T> {
    type_ids: HashSet<TypeId>, // THe TypeIds of every parent types T, of T itself, and of ()
    value: T
}


impl<T> Debug for Typed<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Typed")
            .field("type_ids", &self.type_ids)
            .finish_non_exhaustive()
    }
}

impl<T> Deref for Typed<T> {
    type Target = T;
    fn deref(&self) -> &T{
        &self.value
    }
}


impl<T> DerefMut for Typed<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.value
    }
}


impl<T: 'static> Typed<T>  {
    /// Create a new instance of [`Self`]
    pub fn new(value: T) -> Self where T: Object {
        Typed {
            type_ids: T::type_ids(),
            value
        }
    }

    // SAFETY: upcast_* ensure safety at compile time using IsA: An struct C implementing
    // IsA<P> always contains P at offset 0

    /// Upcast [`Self`] to a parent type [`Target`]
    #[must_use]
    pub fn upcast_ref<Target: 'static>(&self) -> &Typed<Target> where T: IsA<Target> {
        // SAFETY: Target is contained at offset 0 of T
        unsafe { std::mem::transmute(self) }
    }


    /// Upcast [`Self`] to a parent type [`Target`]
    #[must_use]
    pub fn upcast_mut<Target: 'static>(&mut self) -> &mut Typed<Target> where T: IsA<Target> {
        // SAFETY: Target is contained at offset 0 of T
        unsafe { std::mem::transmute(self) }
    }

    // SAFETY: try_cast_* ensure safety at runtime using Typed.type_ids: self.type_ids contains
    // every struct allowed to be cast to using these methods (all allowed structs are contained
    // in offset 0 + are founds using the Object::type_ids function generated using #[object])

    /// Try to cast [`Self`] to another, parent or child type [`Target`]
    pub fn try_cast_ref<Target: Object + 'static>(&self) -> Result<&Typed<Target>, ()> {
        unsafe {
            if self.type_ids.contains(&TypeId::of::<Target>()) {
                // SAFETY: Typed is a #[repr(C)] and self.type_ids only contains the TypeIds of
                // structs located at offset 0 of the original type of Typed
                Ok(std::mem::transmute(self))
            } else {
                Err(())
            }
        }
    }

    /// Try to cast [`Self`] to another, parent or child type [`Target`]
    pub fn try_cast_mut<Target: 'static>(&mut self) -> Result<&mut Typed<Target>, ()> {
        unsafe {
            if self.type_ids.contains(&TypeId::of::<Target>()) {
                // SAFETY: Typed is a #[repr(C)] and self.type_ids only contains the TypeIds of
                // structs located at offset 0 of the original type of Typed
                Ok(std::mem::transmute(self))
            } else {
                Err(())
            }
        }
    }
    
    /// Extracts the value of [`T`]
    pub fn extract_value(self) -> T {
        // SAFETY: We can only cast a Typed into a reference to another Typed,
        // so, here, self.value is actually of type T
        self.value
    }
}


/// Allows a type [`T`] to indicate all of its parent types.
/// Used by [`Typed`] to ensure the data its holding can be safely cast to
/// its parent types.
/// # Safety
/// This trait is safe to implement as long as it does only declare "compatible" types
/// as parents
///
/// In this context, a compatible parent type is a type with is contained in the beginning
/// of the parent type
///```
/// struct A {
///    a: String,
///    b: u8
/// }
///
/// // B is child of A
/// struct B {
///    a: A,
///    b: i32
/// }
///
/// // C is child of A
/// struct C {
///    a: String,
///    b: u8,
///    c: [u8; 4]
/// }
///
/// // D is NOT child of A
/// #[repr(C)]
/// struct D {
///    a: String,
///    b: u8,
///    c: C
/// }
///
/// // E is NOT child od A
/// struct E {
///    a: i32,
///    b: A
/// }
///```
pub unsafe trait Object where Self: Sized + 'static {
    /// Gets the [`TypeId`] of [`Self`] and all of its parent structs
    /// # Safety
    /// Implementing this method is actually unsafe and will lead to
    /// undefined behavior if any of [`TypeIds`](`TypeId`) of the [`HashSet`] are [`TypeIds`](`TypeId`) from
    /// types not contained at offset 0 of `T`.
    fn type_ids() -> HashSet<TypeId> {
        let mut set = HashSet::new();
        set.insert(TypeId::of::<Self>());
        set.insert(TypeId::of::<()>());
        set
    }

    /// Makes an [`ObjectBuilder`] for [`T`] allowing for initialization, using the builder pattern.
    fn builder() -> ObjectBuilder<Self> {
        ObjectBuilder::new()
    }

    /// Only drop initialized fields of uninit_self.
    /// # Safety
    /// This method inherit the unsafety of [`std::ptr::drop_in_place()`] and should never be used.
    /// Only used by [`ObjectBuilder::insert_value()`] and [`ObjectBuilder::insert_typed()`] to avoid memory leaks,
    /// and implemented by the `#[object]` macro.
    #[doc(hidden)]
    unsafe fn uninit_selective_drop(uninit_self: *mut Self, _init_offsets: &Vec<usize>, _size_of_0: Option<usize>) {
        // TODO: Remove this default impl (this is called only on extern parents which I should remove too)
        unsafe {
            uninit_self.drop_in_place()
        }
    }
}

/// A safe wrapper into [`MaybeUninit<T>`] which checks that all fields are actually implemented before
/// allowing to get its value.
#[derive(Debug)]
pub struct ObjectBuilder<T: Object + 'static> {
    // We represent this as a HashMap of sizes to allow for initializing
    // a field multiple times
    /// _HashMap<offset of field, size of field with align>_
    /// Represents the number of bytes of `_value` that were initialized
    /// # Used internally by `#[oors::object]`
    #[doc(hidden)]
    pub _initialized_fields_size: HashMap<usize, usize>,
    /// # Used internally by `#[oors::object]`
    #[doc(hidden)]
    pub _value: MaybeUninit<T>,
}


/// An error thrown if [`ObjectBuilder::try_build()`] failed
#[derive(Debug, Copy, Clone)]
pub struct ObjectInitError(usize);

impl Display for ObjectInitError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "called `ObjectBuilder::build(...)` before fully initializing the object. ({} fields were initialized)",
            self.0
        )
    }
}

mod private {
    
}

impl Error for ObjectInitError {}

impl<T: Object> ObjectBuilder<T> {
    /// Make a new instance of [`Self`] for the object [`T`].
    /// Equivalent of [`T`]`::builder()`
    pub fn new() -> Self {
        Self {
            _value: MaybeUninit::uninit(),
            _initialized_fields_size: HashMap::new(),
        }
    }


    /// Safely insert an instance of [`Typed<P>`], parent of [`T`], into [`T`].
    /// This would be the equivalent of doing `{ ..obj }` in a struct init expression
    pub fn insert_typed<P: 'static>(self, obj: Typed<P>) -> Self where T: IsA<P>, P: Object {
        self.insert_value(obj.value)
    }

    /// Safely insert an instance of [`P`], parent of [`T`], into [`T`]
    /// This would be the equivalent of doing `{ ..obj }` in a struct init expression
    pub fn insert_value<P: 'static>(mut self, obj: P) -> Self where T: IsA<P>, P: Object {
        unsafe {
            let ptr = self._value.as_mut_ptr() as *mut P;
            // Drop any individually initialized fields of the P in T
            // SAFETY: See implementations of this method, and of with_*() methods on oors_macros::object
            P::uninit_selective_drop(
                ptr,
                &self._initialized_fields_size.iter().map(|p| *p.0).collect(),
                self._initialized_fields_size.get(&0).map(|p| *p)
            );

            // SAFETY: P is always at offset 0 of T
            ptr.write(obj);
        }

        self._initialized_fields_size.insert(0, _align(align_of::<P>(), size_of::<P>()));

        self
    }

    /// Check if [`Self`] is fully initialized
    #[must_use]
    pub fn is_init(&self) -> bool {
        let potential_parent_field_size = self._initialized_fields_size.get(&0).unwrap_or(&0);
        let struct_init_bytes = self._initialized_fields_size.iter()
            .filter(|(offset, _)| **offset != 0) // Skip the potential parent
            // Skips anything overlapping with the parent
            // if a field of the parent was re-init via a .with_*()
            .filter(|(offset, _size)| *offset >= potential_parent_field_size)
            .map(|(_, size)| size)
            .sum::<usize>() + potential_parent_field_size;

        struct_init_bytes == size_of::<T>()
    }

    /// Builds [`Self`] into a [`Typed<T>`]
    /// # Panic
    /// If the value was not fully initialized. Use [`self.try_build`] instead if you want a
    /// non-panicking version
    #[must_use]
    pub fn build(self) -> Typed<T> {
        match self.try_build() {
            Ok(val) => val,
            Err(err) => panic!("{err}"),
        }
    }
    

    /// Try to build [`Self`] into a [`Typed<T>`]
    pub fn try_build(self) -> Result<Typed<T>, ObjectInitError> {
        self.try_build_as_value().map(|val| Typed::new(val))
    }

    /// Builds the [`Self`] into a [`T`]
    /// # Panic
    /// If the value was not fully initialized. Use [`self.try_build_as_value`] instead if you want a
    /// non-panicking version
    #[must_use]
    pub fn build_as_value(self) -> T {
        match self.try_build_as_value() {
            Ok(val) => val,
            Err(err) => panic!("{err}"),
        }
    }


    /// Try to build [`Self`] into a [`T`]
    #[must_use]
    pub fn try_build_as_value(self) -> Result<T, ObjectInitError> {
        if self.is_init() {
            // SAFETY: We just checked with self.is_init()
            Ok(unsafe { self._value.assume_init() })
        } else {
            Err(ObjectInitError(self._initialized_fields_size.len()))
        }
    }
}


impl<P: 'static, C: 'static> From<Typed<P>> for ObjectBuilder<C> where C: IsA<P>, P: Object, C: Object {
    fn from(val: Typed<P>) -> Self {
        ObjectBuilder::<C>::new().insert_typed(val)
    }
}

impl<P: 'static, C: 'static> From<P> for ObjectBuilder<C> where C: IsA<P>, P: Object, C: Object {
    fn from(val: P) -> Self {
        ObjectBuilder::<C>::new().insert_value(val)
    }
}


// From hOS: Almost obtained through bruteforce without any research on existing
// algorithms. TODO: Maybe better ways of doing this exists
/// Helper function: "Align" a `number` to the nearest multiple of `to` greater than or equal to `number`
pub fn _align(to: usize, number: usize) -> usize {
    number + ( to - ( (number - 1) % to) ) - 1
}