1use crate::err::{PyErr, PyResult};
2use crate::ffi;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::types::{PyRange, PyRangeMethods};
5use crate::{Bound, IntoPyObject, PyAny, Python};
6use std::convert::Infallible;
7
8#[repr(transparent)]
18pub struct PySlice(PyAny);
19
20pyobject_native_type!(
21 PySlice,
22 ffi::PySliceObject,
23 pyobject_native_static_type_object!(ffi::PySlice_Type),
24 #checkfunction=ffi::PySlice_Check
25);
26
27#[derive(Debug, Eq, PartialEq)]
29pub struct PySliceIndices {
30 pub start: isize,
34 pub stop: isize,
38 pub step: isize,
40 pub slicelength: usize,
42}
43
44impl PySliceIndices {
45 pub fn new(start: isize, stop: isize, step: isize) -> PySliceIndices {
47 PySliceIndices {
48 start,
49 stop,
50 step,
51 slicelength: 0,
52 }
53 }
54}
55
56impl PySlice {
57 pub fn new(py: Python<'_>, start: isize, stop: isize, step: isize) -> Bound<'_, PySlice> {
59 unsafe {
60 ffi::PySlice_New(
61 ffi::PyLong_FromSsize_t(start),
62 ffi::PyLong_FromSsize_t(stop),
63 ffi::PyLong_FromSsize_t(step),
64 )
65 .assume_owned(py)
66 .cast_into_unchecked()
67 }
68 }
69
70 pub fn full(py: Python<'_>) -> Bound<'_, PySlice> {
72 unsafe {
73 ffi::PySlice_New(ffi::Py_None(), ffi::Py_None(), ffi::Py_None())
74 .assume_owned(py)
75 .cast_into_unchecked()
76 }
77 }
78}
79
80#[doc(alias = "PySlice")]
86pub trait PySliceMethods<'py>: crate::sealed::Sealed {
87 fn indices(&self, length: isize) -> PyResult<PySliceIndices>;
91}
92
93impl<'py> PySliceMethods<'py> for Bound<'py, PySlice> {
94 fn indices(&self, length: isize) -> PyResult<PySliceIndices> {
95 unsafe {
96 let mut slicelength: isize = 0;
97 let mut start: isize = 0;
98 let mut stop: isize = 0;
99 let mut step: isize = 0;
100 let r = ffi::PySlice_GetIndicesEx(
101 self.as_ptr(),
102 length,
103 &mut start,
104 &mut stop,
105 &mut step,
106 &mut slicelength,
107 );
108 if r == 0 {
109 Ok(PySliceIndices {
110 start,
111 stop,
112 step,
113 slicelength: slicelength as _,
115 })
116 } else {
117 Err(PyErr::fetch(self.py()))
118 }
119 }
120 }
121}
122
123impl<'py> IntoPyObject<'py> for PySliceIndices {
124 type Target = PySlice;
125 type Output = Bound<'py, Self::Target>;
126 type Error = Infallible;
127
128 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
129 Ok(PySlice::new(py, self.start, self.stop, self.step))
130 }
131}
132
133impl<'py> IntoPyObject<'py> for &PySliceIndices {
134 type Target = PySlice;
135 type Output = Bound<'py, Self::Target>;
136 type Error = Infallible;
137
138 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
139 Ok(PySlice::new(py, self.start, self.stop, self.step))
140 }
141}
142
143impl<'py> TryFrom<Bound<'py, PyRange>> for Bound<'py, PySlice> {
144 type Error = PyErr;
145
146 fn try_from(range: Bound<'py, PyRange>) -> Result<Self, Self::Error> {
147 Ok(PySlice::new(
148 range.py(),
149 range.start()?,
150 range.stop()?,
151 range.step()?,
152 ))
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use crate::types::PyAnyMethods as _;
160
161 #[test]
162 fn test_py_slice_new() {
163 Python::attach(|py| {
164 let slice = PySlice::new(py, isize::MIN, isize::MAX, 1);
165 assert_eq!(
166 slice.getattr("start").unwrap().extract::<isize>().unwrap(),
167 isize::MIN
168 );
169 assert_eq!(
170 slice.getattr("stop").unwrap().extract::<isize>().unwrap(),
171 isize::MAX
172 );
173 assert_eq!(
174 slice.getattr("step").unwrap().extract::<isize>().unwrap(),
175 1
176 );
177 });
178 }
179
180 #[test]
181 fn test_py_slice_full() {
182 Python::attach(|py| {
183 let slice = PySlice::full(py);
184 assert!(slice.getattr("start").unwrap().is_none(),);
185 assert!(slice.getattr("stop").unwrap().is_none(),);
186 assert!(slice.getattr("step").unwrap().is_none(),);
187 assert_eq!(
188 slice.indices(0).unwrap(),
189 PySliceIndices {
190 start: 0,
191 stop: 0,
192 step: 1,
193 slicelength: 0,
194 },
195 );
196 assert_eq!(
197 slice.indices(42).unwrap(),
198 PySliceIndices {
199 start: 0,
200 stop: 42,
201 step: 1,
202 slicelength: 42,
203 },
204 );
205 });
206 }
207
208 #[test]
209 fn test_py_slice_indices_new() {
210 let start = 0;
211 let stop = 0;
212 let step = 0;
213 assert_eq!(
214 PySliceIndices::new(start, stop, step),
215 PySliceIndices {
216 start,
217 stop,
218 step,
219 slicelength: 0
220 }
221 );
222
223 let start = 0;
224 let stop = 100;
225 let step = 10;
226 assert_eq!(
227 PySliceIndices::new(start, stop, step),
228 PySliceIndices {
229 start,
230 stop,
231 step,
232 slicelength: 0
233 }
234 );
235
236 let start = 0;
237 let stop = -10;
238 let step = -1;
239 assert_eq!(
240 PySliceIndices::new(start, stop, step),
241 PySliceIndices {
242 start,
243 stop,
244 step,
245 slicelength: 0
246 }
247 );
248
249 let start = 0;
250 let stop = -10;
251 let step = 20;
252 assert_eq!(
253 PySliceIndices::new(start, stop, step),
254 PySliceIndices {
255 start,
256 stop,
257 step,
258 slicelength: 0
259 }
260 );
261 }
262}