2015-01-07 00:40:48 +00:00
|
|
|
use std;
|
2015-01-11 17:56:59 +00:00
|
|
|
use python::{Python, PythonObject, ToPythonPointer};
|
|
|
|
use err::{self, PyResult, PyErr};
|
2015-01-07 00:40:48 +00:00
|
|
|
use super::object::PyObject;
|
2015-01-11 17:56:59 +00:00
|
|
|
use super::exc;
|
2015-01-07 00:40:48 +00:00
|
|
|
use ffi::{self, Py_ssize_t};
|
2015-01-11 17:56:59 +00:00
|
|
|
use conversion::{ToPyObject, FromPyObject};
|
2015-01-07 00:40:48 +00:00
|
|
|
|
|
|
|
pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);
|
|
|
|
|
|
|
|
impl <'p> PyTuple<'p> {
|
2015-01-11 17:56:59 +00:00
|
|
|
pub fn new(py: Python<'p>, elements: &[PyObject<'p>]) -> PyTuple<'p> {
|
2015-01-07 00:40:48 +00:00
|
|
|
unsafe {
|
|
|
|
let len = elements.len();
|
|
|
|
let ptr = ffi::PyTuple_New(len as Py_ssize_t);
|
2015-01-11 17:56:59 +00:00
|
|
|
let t = err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PyTuple>();
|
2015-01-07 00:40:48 +00:00
|
|
|
for (i, e) in elements.iter().enumerate() {
|
|
|
|
ffi::PyTuple_SET_ITEM(ptr, i as Py_ssize_t, e.clone().steal_ptr());
|
|
|
|
}
|
2015-01-11 17:56:59 +00:00
|
|
|
t
|
2015-01-07 00:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-11 17:56:59 +00:00
|
|
|
|
|
|
|
/// Retrieves the empty tuple.
|
|
|
|
pub fn empty(py: Python<'p>) -> PyTuple<'p> {
|
|
|
|
unsafe {
|
|
|
|
err::result_from_owned_ptr(py, ffi::PyTuple_New(0)).unwrap().unchecked_cast_into::<PyTuple>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
#[inline]
|
2015-01-11 03:21:05 +00:00
|
|
|
pub fn len(&self) -> usize {
|
2015-01-07 00:40:48 +00:00
|
|
|
// non-negative Py_ssize_t should always fit into Rust uint
|
|
|
|
unsafe {
|
2015-01-11 03:21:05 +00:00
|
|
|
ffi::PyTuple_GET_SIZE(self.as_ptr()) as usize
|
2015-01-07 00:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn as_slice<'a>(&'a self) -> &'a [PyObject<'p>] {
|
|
|
|
// This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
|
|
|
|
// and because tuples are immutable.
|
|
|
|
unsafe {
|
|
|
|
let ptr = self.as_ptr() as *mut ffi::PyTupleObject;
|
|
|
|
std::mem::transmute(std::raw::Slice {
|
|
|
|
data: (*ptr).ob_item.as_ptr(),
|
|
|
|
len: self.len()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2015-01-08 08:19:35 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, PyObject<'p>> {
|
|
|
|
self.as_slice().iter()
|
|
|
|
}
|
2015-01-07 00:40:48 +00:00
|
|
|
}
|
|
|
|
|
2015-01-11 03:21:05 +00:00
|
|
|
impl<'p> std::ops::Index<usize> for PyTuple<'p> {
|
2015-01-07 00:40:48 +00:00
|
|
|
type Output = PyObject<'p>;
|
|
|
|
|
|
|
|
#[inline]
|
2015-01-11 03:21:05 +00:00
|
|
|
fn index<'a>(&'a self, index: &usize) -> &'a PyObject<'p> {
|
2015-01-07 00:40:48 +00:00
|
|
|
// use as_slice() to use the normal Rust bounds checking when indexing
|
|
|
|
&self.as_slice()[*index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-11 17:56:59 +00:00
|
|
|
|
|
|
|
fn wrong_tuple_length<'p>(t: &PyTuple<'p>, expected_length: usize) -> PyErr<'p> {
|
|
|
|
let py = t.python();
|
|
|
|
let msg = format!("Expected tuple of length {}, but got tuple of length {}.", expected_length, t.len());
|
|
|
|
PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), msg.to_py_object(py).ok())
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => (
|
|
|
|
impl <'p, $($T: ToPyObject<'p>),+> ToPyObject<'p> for ($($T,)+) {
|
|
|
|
type ObjectType = PyTuple<'p>;
|
|
|
|
|
|
|
|
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyTuple<'p>> {
|
|
|
|
Ok(PyTuple::new(py, &[
|
|
|
|
$(try!(self.$n.to_py_object(py)).into_object(),)+
|
|
|
|
]))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn into_py_object(self, py: Python<'p>) -> PyResult<'p, PyTuple<'p>> {
|
|
|
|
Ok(PyTuple::new(py, &[
|
|
|
|
$(try!(self.$n.into_py_object(py)).into_object(),)+
|
|
|
|
]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'p, 's, $($T: FromPyObject<'p, 's>),+> FromPyObject<'p, 's> for ($($T,)+) {
|
|
|
|
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, ($($T,)+)> {
|
|
|
|
let t = try!(s.cast_as::<PyTuple>());
|
|
|
|
match t.as_slice() {
|
|
|
|
[$(ref $refN,)+] => Ok((
|
|
|
|
$(try!($refN.extract::<$T>()),)+
|
|
|
|
)),
|
|
|
|
_ => Err(wrong_tuple_length(t, 2))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
));
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
// Empty tuple:
|
|
|
|
|
|
|
|
pub struct NoArgs;
|
|
|
|
|
|
|
|
impl <'p> ToPyObject<'p> for NoArgs {
|
|
|
|
type ObjectType = PyTuple<'p>;
|
|
|
|
|
|
|
|
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyTuple<'p>> {
|
|
|
|
Ok(PyTuple::empty(py))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'p, 's> FromPyObject<'p, 's> for NoArgs {
|
|
|
|
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, NoArgs> {
|
|
|
|
let t = try!(s.cast_as::<PyTuple>());
|
|
|
|
if t.len() == 0 {
|
|
|
|
Ok(NoArgs)
|
|
|
|
} else {
|
|
|
|
Err(wrong_tuple_length(t, 0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|