2017-05-16 05:24:06 +00:00
|
|
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
|
|
|
|
|
|
|
use syn;
|
2017-05-23 06:19:14 +00:00
|
|
|
use quote::Tokens;
|
2017-05-16 05:24:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
2017-05-25 03:31:51 +00:00
|
|
|
let base = syn::Ident::from("_pyo3::PyObject");
|
2017-05-28 05:45:48 +00:00
|
|
|
let mut token: Option<syn::Ident> = None;
|
2017-05-17 06:43:39 +00:00
|
|
|
|
|
|
|
match ast.body {
|
2017-05-28 05:45:48 +00:00
|
|
|
syn::Body::Struct(syn::VariantData::Struct(ref mut fields)) => {
|
2017-06-01 16:45:00 +00:00
|
|
|
for field in fields.iter() {
|
|
|
|
if is_python_token(field) {
|
|
|
|
token = field.ident.clone();
|
|
|
|
break
|
2017-05-28 05:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-27 17:49:38 +00:00
|
|
|
},
|
2017-05-23 06:19:14 +00:00
|
|
|
_ => panic!("#[class] can only be used with notmal structs"),
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-16 05:24:06 +00:00
|
|
|
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
2017-05-28 05:45:48 +00:00
|
|
|
let tokens = impl_class(&ast.ident, &base, token);
|
2017-05-23 06:19:14 +00:00
|
|
|
|
2017-05-16 05:24:06 +00:00
|
|
|
quote! {
|
|
|
|
#[feature(specialization)]
|
2017-05-18 21:46:29 +00:00
|
|
|
#[allow(non_upper_case_globals, unused_attributes,
|
|
|
|
unused_qualifications, unused_variables, non_camel_case_types)]
|
2017-05-16 05:24:06 +00:00
|
|
|
const #dummy_const: () = {
|
2017-06-01 22:06:48 +00:00
|
|
|
use std;
|
2017-05-18 18:15:06 +00:00
|
|
|
extern crate pyo3 as _pyo3;
|
2017-05-16 05:24:06 +00:00
|
|
|
|
|
|
|
#tokens
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-28 05:45:48 +00:00
|
|
|
fn impl_class(cls: &syn::Ident, base: &syn::Ident, token: Option<syn::Ident>) -> Tokens {
|
2017-05-18 23:57:39 +00:00
|
|
|
let cls_name = quote! { #cls }.as_str().to_string();
|
|
|
|
|
2017-05-28 05:45:48 +00:00
|
|
|
let extra = if let Some(token) = token {
|
|
|
|
Some(quote! {
|
2017-06-01 22:06:48 +00:00
|
|
|
impl _pyo3::PyObjectWithToken for #cls {
|
2017-05-29 20:30:38 +00:00
|
|
|
fn token<'p>(&'p self) -> _pyo3::python::Python<'p> {
|
|
|
|
self.#token.token()
|
2017-05-28 05:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-28 15:57:34 +00:00
|
|
|
impl std::fmt::Debug for #cls {
|
2017-05-28 05:45:48 +00:00
|
|
|
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
2017-06-06 03:25:00 +00:00
|
|
|
let py = _pyo3::PyObjectWithToken::token(self);
|
2017-06-01 22:06:48 +00:00
|
|
|
let ptr = <#cls as _pyo3::python::ToPyPointer>::as_ptr(self);
|
2017-05-28 15:57:34 +00:00
|
|
|
let repr = unsafe {
|
2017-06-03 01:58:16 +00:00
|
|
|
_pyo3::PyString::downcast_from_ptr(
|
2017-06-06 03:25:00 +00:00
|
|
|
py, _pyo3::ffi::PyObject_Repr(ptr))
|
2017-05-29 04:19:29 +00:00
|
|
|
.map_err(|_| std::fmt::Error)?
|
|
|
|
};
|
2017-06-06 03:25:00 +00:00
|
|
|
f.write_str(&repr.to_string_lossy(py))
|
2017-05-28 05:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for #cls {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
2017-06-06 03:25:00 +00:00
|
|
|
let py = _pyo3::PyObjectWithToken::token(self);
|
2017-06-01 22:06:48 +00:00
|
|
|
let ptr = <#cls as _pyo3::python::ToPyPointer>::as_ptr(self);
|
2017-05-29 04:19:29 +00:00
|
|
|
let str_obj = unsafe {
|
2017-06-03 01:58:16 +00:00
|
|
|
_pyo3::PyString::downcast_from_ptr(
|
2017-06-06 03:25:00 +00:00
|
|
|
py, _pyo3::ffi::PyObject_Str(ptr))
|
2017-05-29 04:19:29 +00:00
|
|
|
.map_err(|_| std::fmt::Error)?
|
|
|
|
};
|
2017-06-06 03:25:00 +00:00
|
|
|
f.write_str(&str_obj.to_string_lossy(py))
|
2017-05-28 05:45:48 +00:00
|
|
|
}
|
2017-05-28 15:57:34 +00:00
|
|
|
}
|
2017-05-28 05:45:48 +00:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2017-05-17 06:43:39 +00:00
|
|
|
quote! {
|
2017-05-25 05:43:07 +00:00
|
|
|
impl _pyo3::typeob::PyTypeInfo for #cls {
|
2017-05-23 06:19:14 +00:00
|
|
|
type Type = #cls;
|
|
|
|
|
2017-05-22 05:22:45 +00:00
|
|
|
#[inline]
|
|
|
|
fn size() -> usize {
|
2017-05-23 06:45:28 +00:00
|
|
|
Self::offset() as usize + std::mem::size_of::<#cls>()
|
2017-05-22 05:22:45 +00:00
|
|
|
}
|
2017-05-17 06:43:39 +00:00
|
|
|
|
2017-05-22 05:22:45 +00:00
|
|
|
#[inline]
|
2017-05-23 06:45:28 +00:00
|
|
|
fn offset() -> isize {
|
2017-05-22 05:22:45 +00:00
|
|
|
let align = std::mem::align_of::<#cls>();
|
2017-05-25 05:43:07 +00:00
|
|
|
let bs = <#base as _pyo3::typeob::PyTypeInfo>::size();
|
2017-05-17 06:43:39 +00:00
|
|
|
|
2017-05-22 05:22:45 +00:00
|
|
|
// round base_size up to next multiple of align
|
2017-05-23 06:45:28 +00:00
|
|
|
((bs + align - 1) / align * align) as isize
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-18 23:57:39 +00:00
|
|
|
#[inline]
|
|
|
|
fn type_name() -> &'static str { #cls_name }
|
|
|
|
|
2017-05-17 06:43:39 +00:00
|
|
|
#[inline]
|
2017-05-19 04:35:08 +00:00
|
|
|
fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject {
|
2017-05-18 18:15:06 +00:00
|
|
|
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT;
|
2017-05-19 04:35:08 +00:00
|
|
|
unsafe { &mut TYPE_OBJECT }
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-25 05:43:07 +00:00
|
|
|
|
2017-06-03 01:58:16 +00:00
|
|
|
impl _pyo3::python::PyDowncastFrom for #cls
|
2017-05-29 09:47:27 +00:00
|
|
|
{
|
2017-06-03 01:58:16 +00:00
|
|
|
fn downcast_from<'a, 'p>(py: Python<'p>, ob: &'a _pyo3::PyObject)
|
|
|
|
-> Result<&'a #cls, _pyo3::PyDowncastError<'p>>
|
2017-05-29 09:47:27 +00:00
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
let checked = ffi::PyObject_TypeCheck(
|
2017-06-03 01:58:16 +00:00
|
|
|
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
2017-05-29 09:47:27 +00:00
|
|
|
|
|
|
|
if checked {
|
|
|
|
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
2017-06-03 01:58:16 +00:00
|
|
|
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
|
2017-05-29 09:47:27 +00:00
|
|
|
Ok(ptr.as_ref().unwrap())
|
|
|
|
} else {
|
2017-06-03 01:58:16 +00:00
|
|
|
Err(_pyo3::PyDowncastError(py, None))
|
2017-05-29 09:47:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-06 03:25:00 +00:00
|
|
|
impl _pyo3::python::PyMutDowncastFrom for #cls
|
|
|
|
{
|
|
|
|
fn downcast_mut_from<'a, 'p>(py: Python<'p>, ob: &'a mut _pyo3::PyObject)
|
|
|
|
-> Result<&'a mut #cls, _pyo3::PyDowncastError<'p>>
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
let checked = ffi::PyObject_TypeCheck(
|
|
|
|
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
|
|
|
|
|
|
|
if checked {
|
|
|
|
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
|
|
|
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
|
|
|
|
Ok(ptr.as_mut().unwrap())
|
|
|
|
} else {
|
|
|
|
Err(_pyo3::PyDowncastError(py, None))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl _pyo3::ToPyObject for #cls
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn to_object<'p>(&self, py: _pyo3::Python<'p>) -> _pyo3::PyObject {
|
|
|
|
_pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr())
|
|
|
|
}
|
2017-05-29 09:47:27 +00:00
|
|
|
|
2017-06-06 03:25:00 +00:00
|
|
|
#[inline]
|
|
|
|
fn with_borrowed_ptr<F, R>(&self, _py: _pyo3::Python, f: F) -> R
|
|
|
|
where F: FnOnce(*mut ffi::PyObject) -> R
|
|
|
|
{
|
|
|
|
f(self.as_ptr())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl std::convert::AsRef<PyObject> for #cls {
|
|
|
|
fn as_ref(&self) -> &_pyo3::PyObject {
|
|
|
|
unsafe{std::mem::transmute(self.as_ptr())}
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 22:06:48 +00:00
|
|
|
impl _pyo3::python::ToPyPointer for #cls {
|
2017-05-30 05:59:03 +00:00
|
|
|
#[inline]
|
|
|
|
fn as_ptr(&self) -> *mut ffi::PyObject {
|
|
|
|
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
|
|
|
unsafe {
|
|
|
|
{self as *const _ as *mut u8}.offset(-offset) as *mut ffi::PyObject
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-28 05:45:48 +00:00
|
|
|
#extra
|
2017-05-16 05:24:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-01 16:45:00 +00:00
|
|
|
|
|
|
|
fn is_python_token(field: &syn::Field) -> bool {
|
|
|
|
match field.ty {
|
|
|
|
syn::Ty::Path(_, ref path) => {
|
|
|
|
if let Some(segment) = path.segments.last() {
|
2017-06-01 22:06:48 +00:00
|
|
|
return segment.ident.as_ref() == "PyToken"
|
2017-06-01 16:45:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|