1use crate::err::{PyErr, PyResult};
2use crate::ffi;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::types::any::PyAnyMethods;
5use crate::types::{PyRange, PyRangeMethods};
6use crate::{Bound, IntoPyObject, PyAny, Python};
7use std::convert::Infallible;
8
9#[repr(transparent)]
19pub struct PySlice(PyAny);
20
21pyobject_native_type!(
22 PySlice,
23 ffi::PySliceObject,
24 pyobject_native_static_type_object!(ffi::PySlice_Type),
25 #checkfunction=ffi::PySlice_Check
26);
27
28#[derive(Debug, Eq, PartialEq)]
30pub struct PySliceIndices {
31 pub start: isize,
35 pub stop: isize,
39 pub step: isize,
41 pub slicelength: usize,
43}
44
45impl PySliceIndices {
46 pub fn new(start: isize, stop: isize, step: isize) -> PySliceIndices {
48 PySliceIndices {
49 start,
50 stop,
51 step,
52 slicelength: 0,
53 }
54 }
55}
56
57impl PySlice {
58 pub fn new(py: Python<'_>, start: isize, stop: isize, step: isize) -> Bound<'_, PySlice> {
60 unsafe {
61 ffi::PySlice_New(
62 ffi::PyLong_FromSsize_t(start),
63 ffi::PyLong_FromSsize_t(stop),
64 ffi::PyLong_FromSsize_t(step),
65 )
66 .assume_owned(py)
67 .downcast_into_unchecked()
68 }
69 }
70
71 pub fn full(py: Python<'_>) -> Bound<'_, PySlice> {
73 unsafe {
74 ffi::PySlice_New(ffi::Py_None(), ffi::Py_None(), ffi::Py_None())
75 .assume_owned(py)
76 .downcast_into_unchecked()
77 }
78 }
79}
80
81#[doc(alias = "PySlice")]
87pub trait PySliceMethods<'py>: crate::sealed::Sealed {
88 fn indices(&self, length: isize) -> PyResult<PySliceIndices>;
92}
93
94impl<'py> PySliceMethods<'py> for Bound<'py, PySlice> {
95 fn indices(&self, length: isize) -> PyResult<PySliceIndices> {
96 unsafe {
97 let mut slicelength: isize = 0;
98 let mut start: isize = 0;
99 let mut stop: isize = 0;
100 let mut step: isize = 0;
101 let r = ffi::PySlice_GetIndicesEx(
102 self.as_ptr(),
103 length,
104 &mut start,
105 &mut stop,
106 &mut step,
107 &mut slicelength,
108 );
109 if r == 0 {
110 Ok(PySliceIndices {
111 start,
112 stop,
113 step,
114 slicelength: slicelength as _,
116 })
117 } else {
118 Err(PyErr::fetch(self.py()))
119 }
120 }
121 }
122}
123
124impl<'py> IntoPyObject<'py> for PySliceIndices {
125 type Target = PySlice;
126 type Output = Bound<'py, Self::Target>;
127 type Error = Infallible;
128
129 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
130 Ok(PySlice::new(py, self.start, self.stop, self.step))
131 }
132}
133
134impl<'py> IntoPyObject<'py> for &PySliceIndices {
135 type Target = PySlice;
136 type Output = Bound<'py, Self::Target>;
137 type Error = Infallible;
138
139 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
140 Ok(PySlice::new(py, self.start, self.stop, self.step))
141 }
142}
143
144impl<'py> TryFrom<Bound<'py, PyRange>> for Bound<'py, PySlice> {
145 type Error = PyErr;
146
147 fn try_from(range: Bound<'py, PyRange>) -> Result<Self, Self::Error> {
148 Ok(PySlice::new(
149 range.py(),
150 range.start()?,
151 range.stop()?,
152 range.step()?,
153 ))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
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}