use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
use crate::{PyCell, PyClass, PyErr, PyResult, Python};
use std::convert::TryFrom;
use std::marker::PhantomData;
pub trait PyObjectInit<T: PyTypeInfo>: Sized {
fn init_class<L: PyLayout<T>>(self, layout: &mut L);
private_decl! {}
}
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
fn init_class<L: PyLayout<T>>(self, _layout: &mut L) {}
private_impl! {}
}
pub struct PyClassInitializer<T: PyClass> {
init: T,
super_init: <T::BaseType as PyTypeInfo>::Initializer,
}
impl<T: PyClass> PyClassInitializer<T> {
pub fn new(init: T, super_init: <T::BaseType as PyTypeInfo>::Initializer) -> Self {
Self { init, super_init }
}
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = T>,
S::BaseLayout: PySizedLayout<T>,
S::BaseType: PyTypeInfo<Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
}
#[doc(hidden)]
pub fn create_cell(self, py: Python) -> PyResult<*mut PyCell<T>>
where
T: PyClass,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py)) }
}
#[doc(hidden)]
pub unsafe fn create_cell_from_subtype(
self,
py: Python,
subtype: *mut crate::ffi::PyTypeObject,
) -> PyResult<*mut PyCell<T>>
where
T: PyClass,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let cell = PyCell::internal_new(py, subtype)?;
self.init_class(&mut *cell);
Ok(cell)
}
}
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
fn init_class<L: PyLayout<T>>(self, layout: &mut L) {
let Self { init, super_init } = self;
unsafe {
layout.py_init(init);
}
if let Some(super_obj) = layout.get_super() {
super_init.init_class(super_obj);
}
}
private_impl! {}
}
impl<T> From<T> for PyClassInitializer<T>
where
T: PyClass,
T::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<T::BaseType>>,
{
fn from(value: T) -> PyClassInitializer<T> {
Self::new(value, PyNativeTypeInitializer(PhantomData))
}
}
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = B>,
S::BaseLayout: PySizedLayout<B>,
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
let (sub, base) = sub_and_base;
PyClassInitializer::from(base).add_subclass(sub)
}
}
impl<T, U> TryFrom<PyResult<U>> for PyClassInitializer<T>
where
T: PyClass,
U: Into<PyClassInitializer<T>>,
{
type Error = PyErr;
fn try_from(result: PyResult<U>) -> PyResult<Self> {
result.map(Into::into)
}
}