flect 0.1.1

Rust reflection [WIP]
Documentation
use core::fmt::{Display, Debug};

use crate::ptr::{OpaqueConst, OpaqueMut};
use crate::types::{self, StructType, TypeKind};
use crate::{Model, Reflect};

pub struct Inspect<'ins, 'data, 'model> {
    ptr: OpaqueConst<'data>,
    model: &'ins Model<'model>,
}

impl<'ins, 'data, 'model> Display for Inspect<'ins, 'data, 'model> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.model.vtable.display {
            Some(display) => {
                unsafe { display(self.ptr, f) }
            }
            None => {
                write!(f, "Model ( {:?} )", self.model.ty)
            }
        }
    }
}

impl<'ins, 'data, 'model> Debug for Inspect<'ins, 'data, 'model> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.model.vtable.debug {
            Some(debug) => {
                unsafe { debug(self.ptr, f) }
            }
            None => {
                write!(f, "Model ( {:?} )", self.model.ty)
            }
        }
    }
}

impl<'ins, 'data, 'model> Inspect<'ins, 'data, 'model> {
    pub fn new<R: Reflect + ?Sized>(r: &'data R) -> Self {
        Self {
            ptr: OpaqueConst::new(r),
            model: R::MODEL,
        }
    }

    pub fn from_parts(model: &'model Model<'model>, ptr: OpaqueConst<'data>) -> Self {
        Self { ptr, model }
    }

    pub fn fields(&'ins self) -> Option<InspectFields<'data, 'model>> {
        let TypeKind::Struct(sty) = &self.model.ty.kind else { return None };
        Some(InspectFields::new(sty, self.ptr))
    }
}

pub struct InspectFields<'data, 'model> {
    ptr: OpaqueConst<'data>,
    sty: &'model StructType<'model>,
}

impl<'data, 'model> InspectFields<'data, 'model> {
    pub fn new(sty: &'model StructType<'model>, ptr: OpaqueConst<'data>) -> Self {
        Self { ptr, sty }
    }

    pub fn get_by_name<T: 'static>(&self, name: &str) -> Option<&T> {
        for field in self.sty.fields {
            if field.name == name {
                let exp_id = core::any::TypeId::of::<T>();
                if field.model.ty.id.get() != exp_id {
                    return None
                }
                unsafe {
                    let r = self.ptr
                                    .as_byte_ptr()
                                    .byte_add(field.offset)
                                    .cast::<T>()
                                    .as_ref()
                                    .unwrap();
                    return Some(r)
                }
            }
        }
        None
    }

    pub fn get_at<T: 'static>(&self, index: usize) -> Option<&T> {
        unsafe {
            self.ptr
                .as_byte_ptr()
                .add( self.sty.fields[index].offset )
                .cast::<T>()
                .as_ref()
        }
    }

    pub const fn len(&self) -> usize {
        self.sty.fields.len()
    }

    pub const fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl<'data, 'model> IntoIterator for InspectFields<'data, 'model> {
    type Item = Field<'data, 'model>;
    type IntoIter = FieldsIter<'data, 'model>;

    fn into_iter(self) -> Self::IntoIter {
        FieldsIter {
            fields: self.sty.fields,
            offset: 0,
            ptr: self.ptr,
        }
    }
}

pub struct Field<'data, 'model> {
    pub f: &'model types::Field<'model>,
    pub ptr: OpaqueConst<'data>,
}

pub struct FieldsIter<'data, 'model> {
    offset: usize,
    fields: &'model [types::Field<'model>],
    ptr: OpaqueConst<'data>
}

impl<'data, 'model> Iterator for FieldsIter<'data, 'model> {
    type Item = Field<'data, 'model>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.offset >= self.fields.len() {
            return None
        }
        let f = &self.fields[self.offset];
        let ptr = self.ptr.byte_add(f.offset);
        let f = Field { f, ptr };
        self.offset += 1;
        Some(f)
    }
}

pub struct InspectMut<'ins, 'data, 'model> {
    ptr: OpaqueMut<'data>,
    model: &'ins Model<'model>,
}

impl<'ins, 'data, 'model> Display for InspectMut<'ins, 'data, 'model> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.model.vtable.display {
            Some(display) => {
                unsafe { display(self.ptr.as_const(), f) }
            }
            None => {
                write!(f, "Model ( {:?} )", self.model.ty)
            }
        }
    }
}

impl<'ins, 'data, 'model> Debug for InspectMut<'ins, 'data, 'model> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.model.vtable.debug {
            Some(debug) => {
                unsafe { debug(self.ptr.as_const(), f) }
            }
            None => {
                write!(f, "Model ( {:?} )", self.model.ty)
            }
        }
    }
}

impl<'ins, 'data, 'model> InspectMut<'ins, 'data, 'model> {
    pub fn new<R: Reflect + ?Sized>(r: &'data mut R) -> Self {
        Self {
            ptr: OpaqueMut::new(r),
            model: R::MODEL,
        }
    }

    pub fn fields<'d>(&'d self) -> Option<InspectFields<'d, 'model>> {
        let TypeKind::Struct(sty) = &self.model.ty.kind else { return None };
        Some(InspectFields::new(sty, self.ptr.as_const()))
    }

    pub fn fields_mut(&mut self) -> Option<InspectFieldsMut<'data, 'model>> {
        let TypeKind::Struct(sty) = &self.model.ty.kind else { return None };
        Some(InspectFieldsMut::new(sty, self.ptr))
    }
}

pub struct InspectFieldsMut<'data, 'model> {
    ptr: OpaqueMut<'data>,
    sty: &'model StructType<'model>,
}

impl<'data, 'model> InspectFieldsMut<'data, 'model> {
    pub fn new(sty: &'model StructType<'model>, ptr: OpaqueMut<'data>) -> Self {
        Self { ptr, sty }
    }

    fn _get_raw_by_name<T: 'static>(&self, name: &str, ptr: *mut u8) -> Option<*mut T> {
        for field in self.sty.fields {
            if field.name == name {
                let exp_id = core::any::TypeId::of::<T>();
                if field.model.ty.id.get() != exp_id {
                    return None
                }
                unsafe {
                    let r = ptr.byte_add(field.offset)
                               .cast::<T>();
                    return Some(r)
                }
            }
        }
        None
    }

    pub fn get_by_name<T: 'static>(&self, name: &str) -> Option<&T> {
        self._get_raw_by_name(name, self.ptr.as_byte_ptr() as *mut _)
            .and_then(|p: *mut T| unsafe { p.as_ref() })
    }

    pub fn get_by_name_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
        let p = self.ptr.as_mut_byte_ptr();
        self._get_raw_by_name(name, p)
            .and_then(|p: *mut T| unsafe { p.as_mut() })
    }

    pub fn set_by_name<T: 'static>(&mut self, name: &str, val: T) -> Result<(), T> {
        match self.get_by_name_mut::<T>(name) {
            Some(e) => {
                *e = val;
                Ok(())
            }
            None => Err(val)
        }
    }

    pub fn get_at<T: 'static>(&self, index: usize) -> Option<&T> {
        unsafe {
            self.ptr
                .as_byte_ptr()
                .add( self.sty.fields[index].offset )
                .cast::<T>()
                .as_ref()
        }
    }

    pub fn get_at_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
        unsafe {
            self.ptr
                .as_mut_byte_ptr()
                .add( self.sty.fields[index].offset )
                .cast::<T>()
                .as_mut()
        }
    }

    pub fn set_at<T: 'static>(&mut self, index: usize, val: T) -> Result<(), T> {
        match self.get_at_mut::<T>(index) {
            Some(r) => {
                *r = val;
                Ok(())
            }
            None => Err(val)
        }
    }
}