1use std::slice;
20
21use super::exc;
22use super::object::PyObject;
23use crate::conversion::{FromPyObject, ToPyObject};
24use crate::err::{self, PyErr, PyResult};
25use crate::ffi::{self, Py_ssize_t};
26use crate::python::{PyDrop, Python, PythonObject, ToPythonPointer};
27
28pub struct PyTuple(PyObject);
30
31pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);
32
33impl PyTuple {
34 pub fn new(py: Python, elements: &[PyObject]) -> PyTuple {
36 unsafe {
37 let len = elements.len();
38 let ptr = ffi::PyTuple_New(len as Py_ssize_t);
39 let t = err::result_cast_from_owned_ptr::<PyTuple>(py, ptr).unwrap();
40 for (i, e) in elements.iter().enumerate() {
41 ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
42 }
43 t
44 }
45 }
46
47 pub fn empty(py: Python) -> PyTuple {
49 unsafe { err::result_cast_from_owned_ptr::<PyTuple>(py, ffi::PyTuple_New(0)).unwrap() }
50 }
51
52 #[inline]
54 pub fn len(&self, _py: Python) -> usize {
55 unsafe {
56 ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
58 }
59 }
60
61 pub fn get_item(&self, py: Python, index: usize) -> PyObject {
65 assert!(index < self.len(py));
68 unsafe {
69 PyObject::from_borrowed_ptr(
70 py,
71 ffi::PyTuple_GET_ITEM(self.0.as_ptr(), index as Py_ssize_t),
72 )
73 }
74 }
75
76 #[inline]
77 pub fn as_slice<'a>(&'a self, py: Python) -> &'a [PyObject] {
78 unsafe {
82 let ptr = self.0.as_ptr() as *mut ffi::PyTupleObject;
83 PyObject::borrow_from_owned_ptr_slice(slice::from_raw_parts(
84 (*ptr).ob_item.as_ptr(),
85 self.len(py),
86 ))
87 }
88 }
89
90 #[inline]
91 pub fn iter(&self, py: Python) -> slice::Iter<PyObject> {
92 self.as_slice(py).iter()
93 }
94}
95
96fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr {
97 let msg = format!(
98 "Expected tuple of length {}, but got tuple of length {}.",
99 expected_length,
100 t.len(py)
101 );
102 PyErr::new_lazy_init(
103 py.get_type::<exc::ValueError>(),
104 Some(msg.to_py_object(py).into_object()),
105 )
106}
107
108macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
109 impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
111 type ObjectType = PyTuple;
112
113 fn to_py_object(&self, py: Python) -> PyTuple {
114 PyTuple::new(py, &[
115 $(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
116 ])
117 }
118
119 fn into_py_object(self, py: Python) -> PyTuple {
120 PyTuple::new(py, &[
121 $(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+
122 ])
123 }
124 }
125
126 impl <'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
131 fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
132 let t = obj.cast_as::<PyTuple>(py)?;
133 let slice = t.as_slice(py);
134 if slice.len() == $length {
135 Ok((
136 $( slice[$n].extract::<$T>(py)?, )+
137 ))
138 } else {
139 Err(wrong_tuple_length(py, t, $length))
140 }
141 }
142 }
143});
144
145tuple_conversion!(1, (ref0, 0, A));
146tuple_conversion!(2, (ref0, 0, A), (ref1, 1, B));
147tuple_conversion!(3, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C));
148tuple_conversion!(4, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D));
149tuple_conversion!(
150 5,
151 (ref0, 0, A),
152 (ref1, 1, B),
153 (ref2, 2, C),
154 (ref3, 3, D),
155 (ref4, 4, E)
156);
157tuple_conversion!(
158 6,
159 (ref0, 0, A),
160 (ref1, 1, B),
161 (ref2, 2, C),
162 (ref3, 3, D),
163 (ref4, 4, E),
164 (ref5, 5, F)
165);
166tuple_conversion!(
167 7,
168 (ref0, 0, A),
169 (ref1, 1, B),
170 (ref2, 2, C),
171 (ref3, 3, D),
172 (ref4, 4, E),
173 (ref5, 5, F),
174 (ref6, 6, G)
175);
176tuple_conversion!(
177 8,
178 (ref0, 0, A),
179 (ref1, 1, B),
180 (ref2, 2, C),
181 (ref3, 3, D),
182 (ref4, 4, E),
183 (ref5, 5, F),
184 (ref6, 6, G),
185 (ref7, 7, H)
186);
187tuple_conversion!(
188 9,
189 (ref0, 0, A),
190 (ref1, 1, B),
191 (ref2, 2, C),
192 (ref3, 3, D),
193 (ref4, 4, E),
194 (ref5, 5, F),
195 (ref6, 6, G),
196 (ref7, 7, H),
197 (ref8, 8, I)
198);
199
200#[derive(Copy, Clone, Debug)]
213pub struct NoArgs;
214
215impl ToPyObject for NoArgs {
217 type ObjectType = PyTuple;
218
219 fn to_py_object(&self, py: Python) -> PyTuple {
220 PyTuple::empty(py)
221 }
222}
223
224extract!(obj to NoArgs;
225 py => {
228 let t = obj.cast_as::<PyTuple>(py)?;
229 if t.len(py) == 0 {
230 Ok(NoArgs)
231 } else {
232 Err(wrong_tuple_length(py, t, 0))
233 }
234 }
235);
236
237#[cfg(test)]
238mod test {
239 use crate::conversion::ToPyObject;
240 use crate::python::{Python, PythonObject};
241
242 #[test]
243 fn test_len() {
244 let gil = Python::acquire_gil();
245 let py = gil.python();
246 let tuple = (1, 2, 3).to_py_object(py);
247 assert_eq!(3, tuple.len(py));
248 assert_eq!((1, 2, 3), tuple.into_object().extract(py).unwrap());
249 }
250}