use std::slice;
use super::exc;
use super::object::PyObject;
use crate::conversion::{FromPyObject, ToPyObject};
use crate::err::{self, PyErr, PyResult};
use crate::ffi::{self, Py_ssize_t};
use crate::python::{PyDrop, Python, PythonObject, ToPythonPointer};
pub struct PyTuple(PyObject);
pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);
impl PyTuple {
pub fn new(py: Python, elements: &[PyObject]) -> PyTuple {
unsafe {
let len = elements.len();
let ptr = ffi::PyTuple_New(len as Py_ssize_t);
let t = err::result_cast_from_owned_ptr::<PyTuple>(py, ptr).unwrap();
for (i, e) in elements.iter().enumerate() {
ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
}
t
}
}
pub fn empty(py: Python) -> PyTuple {
unsafe { err::result_cast_from_owned_ptr::<PyTuple>(py, ffi::PyTuple_New(0)).unwrap() }
}
#[inline]
pub fn len(&self, _py: Python) -> usize {
unsafe {
ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
}
}
pub fn get_item(&self, py: Python, index: usize) -> PyObject {
assert!(index < self.len(py));
unsafe {
PyObject::from_borrowed_ptr(
py,
ffi::PyTuple_GET_ITEM(self.0.as_ptr(), index as Py_ssize_t),
)
}
}
#[inline]
pub fn as_slice<'a>(&'a self, py: Python) -> &'a [PyObject] {
unsafe {
let ptr = self.0.as_ptr() as *mut ffi::PyTupleObject;
PyObject::borrow_from_owned_ptr_slice(slice::from_raw_parts(
(*ptr).ob_item.as_ptr(),
self.len(py),
))
}
}
#[inline]
pub fn iter(&self, py: Python) -> slice::Iter<PyObject> {
self.as_slice(py).iter()
}
}
fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr {
let msg = format!(
"Expected tuple of length {}, but got tuple of length {}.",
expected_length,
t.len(py)
);
PyErr::new_lazy_init(
py.get_type::<exc::ValueError>(),
Some(msg.to_py_object(py).into_object()),
)
}
macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
type ObjectType = PyTuple;
fn to_py_object(&self, py: Python) -> PyTuple {
PyTuple::new(py, &[
$(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
])
}
fn into_py_object(self, py: Python) -> PyTuple {
PyTuple::new(py, &[
$(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+
])
}
}
impl <'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
let t = obj.cast_as::<PyTuple>(py)?;
let slice = t.as_slice(py);
if slice.len() == $length {
Ok((
$( slice[$n].extract::<$T>(py)?, )+
))
} else {
Err(wrong_tuple_length(py, t, $length))
}
}
}
});
tuple_conversion!(1, (ref0, 0, A));
tuple_conversion!(2, (ref0, 0, A), (ref1, 1, B));
tuple_conversion!(3, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C));
tuple_conversion!(4, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D));
tuple_conversion!(
5,
(ref0, 0, A),
(ref1, 1, B),
(ref2, 2, C),
(ref3, 3, D),
(ref4, 4, E)
);
tuple_conversion!(
6,
(ref0, 0, A),
(ref1, 1, B),
(ref2, 2, C),
(ref3, 3, D),
(ref4, 4, E),
(ref5, 5, F)
);
tuple_conversion!(
7,
(ref0, 0, A),
(ref1, 1, B),
(ref2, 2, C),
(ref3, 3, D),
(ref4, 4, E),
(ref5, 5, F),
(ref6, 6, G)
);
tuple_conversion!(
8,
(ref0, 0, A),
(ref1, 1, B),
(ref2, 2, C),
(ref3, 3, D),
(ref4, 4, E),
(ref5, 5, F),
(ref6, 6, G),
(ref7, 7, H)
);
tuple_conversion!(
9,
(ref0, 0, A),
(ref1, 1, B),
(ref2, 2, C),
(ref3, 3, D),
(ref4, 4, E),
(ref5, 5, F),
(ref6, 6, G),
(ref7, 7, H),
(ref8, 8, I)
);
#[derive(Copy, Clone, Debug)]
pub struct NoArgs;
impl ToPyObject for NoArgs {
type ObjectType = PyTuple;
fn to_py_object(&self, py: Python) -> PyTuple {
PyTuple::empty(py)
}
}
extract!(obj to NoArgs;
py => {
let t = obj.cast_as::<PyTuple>(py)?;
if t.len(py) == 0 {
Ok(NoArgs)
} else {
Err(wrong_tuple_length(py, t, 0))
}
}
);
#[cfg(test)]
mod test {
use crate::conversion::ToPyObject;
use crate::python::{Python, PythonObject};
#[test]
fn test_len() {
let gil = Python::acquire_gil();
let py = gil.python();
let tuple = (1, 2, 3).to_py_object(py);
assert_eq!(3, tuple.len(py));
assert_eq!((1, 2, 3), tuple.into_object().extract(py).unwrap());
}
}