/// Utilities for a Python callable object that invokes a Rust function. use std::os::raw::c_int; use std::{any, mem, ptr, isize, io, panic}; use libc; use pyptr::Py; use python::{Python, IntoPythonPointer}; use objects::exc; use conversion::ToPyObject; use ffi::{self, Py_hash_t}; use err::{PyErr, PyResult}; pub trait CallbackConverter { type R; fn convert(S, Python) -> Self::R; fn error_value() -> Self::R; } pub struct PyObjectCallbackConverter; impl CallbackConverter for PyObjectCallbackConverter where S: ToPyObject { type R = *mut ffi::PyObject; fn convert(val: S, py: Python) -> *mut ffi::PyObject { val.into_object(py).into_ptr() } #[inline] fn error_value() -> *mut ffi::PyObject { ptr::null_mut() } } pub struct BoolCallbackConverter; impl CallbackConverter for BoolCallbackConverter { type R = c_int; #[inline] fn convert(val: bool, _py: Python) -> c_int { val as c_int } #[inline] fn error_value() -> c_int { -1 } } pub struct LenResultConverter; impl CallbackConverter for LenResultConverter { type R = isize; fn convert(val: usize, py: Python) -> isize { if val <= (isize::MAX as usize) { val as isize } else { PyErr::new_lazy_init(py.get_ptype::(), None).restore(py); -1 } } #[inline] fn error_value() -> isize { -1 } } pub struct UnitCallbackConverter; impl CallbackConverter<()> for UnitCallbackConverter { type R = c_int; #[inline] fn convert(_: (), _: Python) -> c_int { 0 } #[inline] fn error_value() -> c_int { -1 } } pub struct IterNextResultConverter; impl CallbackConverter> for IterNextResultConverter where T: ToPyObject { type R = *mut ffi::PyObject; fn convert(val: Option, py: Python) -> *mut ffi::PyObject { match val { Some(val) => val.into_object(py).into_ptr(), None => unsafe { ffi::PyErr_SetNone(ffi::PyExc_StopIteration); ptr::null_mut() } } } #[inline] fn error_value() -> *mut ffi::PyObject { ptr::null_mut() } } pub trait WrappingCastTo { fn wrapping_cast(self) -> T; } macro_rules! wrapping_cast { ($from:ty, $to:ty) => { impl WrappingCastTo<$to> for $from { #[inline] fn wrapping_cast(self) -> $to { self as $to } } } } wrapping_cast!(u8, Py_hash_t); wrapping_cast!(u16, Py_hash_t); wrapping_cast!(u32, Py_hash_t); wrapping_cast!(usize, Py_hash_t); wrapping_cast!(u64, Py_hash_t); wrapping_cast!(i8, Py_hash_t); wrapping_cast!(i16, Py_hash_t); wrapping_cast!(i32, Py_hash_t); wrapping_cast!(isize, Py_hash_t); wrapping_cast!(i64, Py_hash_t); pub struct HashConverter; impl CallbackConverter for HashConverter where T: WrappingCastTo { type R = Py_hash_t; #[inline] fn convert(val: T, _py: Python) -> Py_hash_t { let hash = val.wrapping_cast(); if hash == -1 { -2 } else { hash } } #[inline] fn error_value() -> Py_hash_t { -1 } } // very unsafe util for callbacks, maybe bug un rust compiler? pub unsafe fn unref<'p, T>(p: Py<'p, T>) -> &Py { {&p as *const _}.as_ref().unwrap() } pub unsafe fn handle<'p, F, T, C>(location: &str, _c: C, f: F) -> C::R where F: FnOnce(Python<'p>) -> PyResult, F: panic::UnwindSafe, C: CallbackConverter { let guard = AbortOnDrop(location); let ret = panic::catch_unwind(|| { let py = Python::assume_gil_acquired(); match f(py) { Ok(val) => { C::convert(val, py) } Err(e) => { e.restore(py); C::error_value() } } }); let ret = match ret { Ok(r) => r, Err(ref err) => { handle_panic(Python::assume_gil_acquired(), err); C::error_value() } }; mem::forget(guard); ret } fn handle_panic(_py: Python, _panic: &any::Any) { unsafe { ffi::PyErr_SetString(ffi::PyExc_SystemError, "Rust panic\0".as_ptr() as *const i8); } } pub struct AbortOnDrop<'a>(pub &'a str); impl <'a> Drop for AbortOnDrop<'a> { fn drop(&mut self) { use std::io::Write; let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0); unsafe { libc::abort() } } }