| 1 | //! `PyClass` and related traits. |
| 2 | use crate::{ffi, impl_::pyclass::PyClassImpl, PyTypeInfo}; |
| 3 | use std::{cmp::Ordering, os::raw::c_int}; |
| 4 | |
| 5 | mod create_type_object; |
| 6 | mod gc; |
| 7 | |
| 8 | pub(crate) use self::create_type_object::{create_type_object, PyClassTypeObject}; |
| 9 | |
| 10 | pub use self::gc::{PyTraverseError, PyVisit}; |
| 11 | |
| 12 | /// Types that can be used as Python classes. |
| 13 | /// |
| 14 | /// The `#[pyclass]` attribute implements this trait for your Rust struct - |
| 15 | /// you shouldn't implement this trait directly. |
| 16 | pub trait PyClass: PyTypeInfo + PyClassImpl { |
| 17 | /// Whether the pyclass is frozen. |
| 18 | /// |
| 19 | /// This can be enabled via `#[pyclass(frozen)]`. |
| 20 | type Frozen: Frozen; |
| 21 | } |
| 22 | |
| 23 | /// Operators for the `__richcmp__` method |
| 24 | #[derive (Debug, Clone, Copy)] |
| 25 | pub enum CompareOp { |
| 26 | /// The *less than* operator. |
| 27 | Lt = ffi::Py_LT as isize, |
| 28 | /// The *less than or equal to* operator. |
| 29 | Le = ffi::Py_LE as isize, |
| 30 | /// The equality operator. |
| 31 | Eq = ffi::Py_EQ as isize, |
| 32 | /// The *not equal to* operator. |
| 33 | Ne = ffi::Py_NE as isize, |
| 34 | /// The *greater than* operator. |
| 35 | Gt = ffi::Py_GT as isize, |
| 36 | /// The *greater than or equal to* operator. |
| 37 | Ge = ffi::Py_GE as isize, |
| 38 | } |
| 39 | |
| 40 | impl CompareOp { |
| 41 | /// Conversion from the C enum. |
| 42 | pub fn from_raw(op: c_int) -> Option<Self> { |
| 43 | match op { |
| 44 | ffi::Py_LT => Some(CompareOp::Lt), |
| 45 | ffi::Py_LE => Some(CompareOp::Le), |
| 46 | ffi::Py_EQ => Some(CompareOp::Eq), |
| 47 | ffi::Py_NE => Some(CompareOp::Ne), |
| 48 | ffi::Py_GT => Some(CompareOp::Gt), |
| 49 | ffi::Py_GE => Some(CompareOp::Ge), |
| 50 | _ => None, |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /// Returns if a Rust [`std::cmp::Ordering`] matches this ordering query. |
| 55 | /// |
| 56 | /// Usage example: |
| 57 | /// |
| 58 | /// ```rust |
| 59 | /// # use pyo3::prelude::*; |
| 60 | /// # use pyo3::class::basic::CompareOp; |
| 61 | /// |
| 62 | /// #[pyclass] |
| 63 | /// struct Size { |
| 64 | /// size: usize, |
| 65 | /// } |
| 66 | /// |
| 67 | /// #[pymethods] |
| 68 | /// impl Size { |
| 69 | /// fn __richcmp__(&self, other: &Size, op: CompareOp) -> bool { |
| 70 | /// op.matches(self.size.cmp(&other.size)) |
| 71 | /// } |
| 72 | /// } |
| 73 | /// ``` |
| 74 | pub fn matches(&self, result: Ordering) -> bool { |
| 75 | match self { |
| 76 | CompareOp::Eq => result == Ordering::Equal, |
| 77 | CompareOp::Ne => result != Ordering::Equal, |
| 78 | CompareOp::Lt => result == Ordering::Less, |
| 79 | CompareOp::Le => result != Ordering::Greater, |
| 80 | CompareOp::Gt => result == Ordering::Greater, |
| 81 | CompareOp::Ge => result != Ordering::Less, |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /// A workaround for [associated const equality](https://github.com/rust-lang/rust/issues/92827). |
| 87 | /// |
| 88 | /// This serves to have True / False values in the [`PyClass`] trait's `Frozen` type. |
| 89 | #[doc (hidden)] |
| 90 | pub mod boolean_struct { |
| 91 | pub(crate) mod private { |
| 92 | use super::*; |
| 93 | |
| 94 | /// A way to "seal" the boolean traits. |
| 95 | pub trait Boolean { |
| 96 | const VALUE: bool; |
| 97 | } |
| 98 | |
| 99 | impl Boolean for True { |
| 100 | const VALUE: bool = true; |
| 101 | } |
| 102 | impl Boolean for False { |
| 103 | const VALUE: bool = false; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | pub struct True(()); |
| 108 | pub struct False(()); |
| 109 | } |
| 110 | |
| 111 | /// A trait which is used to describe whether a `#[pyclass]` is frozen. |
| 112 | #[doc (hidden)] |
| 113 | pub trait Frozen: boolean_struct::private::Boolean {} |
| 114 | |
| 115 | impl Frozen for boolean_struct::True {} |
| 116 | impl Frozen for boolean_struct::False {} |
| 117 | |
| 118 | mod tests { |
| 119 | #[test ] |
| 120 | fn test_compare_op_matches() { |
| 121 | use super::CompareOp; |
| 122 | use std::cmp::Ordering; |
| 123 | |
| 124 | assert!(CompareOp::Eq.matches(Ordering::Equal)); |
| 125 | assert!(CompareOp::Ne.matches(Ordering::Less)); |
| 126 | assert!(CompareOp::Ge.matches(Ordering::Greater)); |
| 127 | assert!(CompareOp::Gt.matches(Ordering::Greater)); |
| 128 | assert!(CompareOp::Le.matches(Ordering::Equal)); |
| 129 | assert!(CompareOp::Lt.matches(Ordering::Less)); |
| 130 | } |
| 131 | } |
| 132 | |