rigetti_pyo3/traits.rs
1// Copyright 2022 Rigetti Computing
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Macros for implementing trait-related behavior for wrapped types.
16//!
17//! - Implement Rust traits based on inner types.
18//! - Implement "dunder" methods based on wrapper type trait implementations.
19//! - Note that you can pass `#[derive(Trait)]` as part of the `py_wrap_*` macro input
20
21/// Implement Python comparison for a given type. That type must implement
22/// [`PartialOrd`](std::cmp::PartialOrd).
23#[macro_export]
24macro_rules! impl_compare {
25 ($name: ident) => {
26 #[$crate::pyo3::pymethods]
27 impl $name {
28 /// Implements all the Python comparison operators in terms of the
29 /// Rust [`PartialOrd`](std::cmp::PartialOrd) instance.
30 #![allow(clippy::use_self)]
31 pub fn __richcmp__(&self, object: &Self, cmp: $crate::pyo3::basic::CompareOp) -> bool {
32 let result = ::std::cmp::PartialOrd::partial_cmp(self, object);
33 match cmp {
34 $crate::pyo3::basic::CompareOp::Lt => {
35 matches!(result, Some(::std::cmp::Ordering::Less))
36 }
37 $crate::pyo3::basic::CompareOp::Le => {
38 !matches!(result, Some(::std::cmp::Ordering::Greater))
39 }
40 $crate::pyo3::basic::CompareOp::Eq => {
41 matches!(result, Some(::std::cmp::Ordering::Equal))
42 }
43 $crate::pyo3::basic::CompareOp::Ne => {
44 !matches!(result, Some(::std::cmp::Ordering::Equal))
45 }
46 $crate::pyo3::basic::CompareOp::Gt => {
47 matches!(result, Some(::std::cmp::Ordering::Greater))
48 }
49 $crate::pyo3::basic::CompareOp::Ge => {
50 !matches!(result, Some(::std::cmp::Ordering::Less))
51 }
52 }
53 }
54 }
55 };
56}
57
58/// Implement `__hash__` for types that implement [`Hash`](std::hash::Hash).
59#[macro_export]
60macro_rules! impl_hash {
61 ($name: ident) => {
62 #[$crate::pyo3::pymethods]
63 impl $name {
64 /// Implements `__hash__` for Python in terms of the Rust
65 /// [`Hash`](std::hash::Hash) instance.
66 pub fn __hash__(&self) -> i64 {
67 let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
68 ::std::hash::Hash::hash($crate::PyWrapper::as_inner(self), &mut hasher);
69 let bytes = ::std::hash::Hasher::finish(&hasher).to_ne_bytes();
70 i64::from_ne_bytes(bytes)
71 }
72 }
73 };
74}
75
76/// Implement `__repr__` for wrapper types whose inner type implements [`Debug`](std::fmt::Debug).
77#[macro_export]
78macro_rules! impl_repr {
79 ($name: ident) => {
80 /// Implements `__repr__` for Python in terms of the Rust
81 /// [`Debug`](std::fmt::Debug) instance.
82 #[$crate::pyo3::pymethods]
83 impl $name {
84 pub fn __repr__(&self) -> String {
85 format!("{:?}", $crate::PyWrapper::as_inner(self))
86 }
87 }
88 };
89}
90
91/// Implement `__str__` for wrapper types whose inner type implements [`Display`](std::fmt::Display).
92#[macro_export]
93macro_rules! impl_str {
94 ($name: ident) => {
95 /// Implements `__str__` for Python in terms of the Rust
96 /// [`Display`](std::fmt::Display) instance.
97 #[$crate::pyo3::pymethods]
98 impl $name {
99 pub fn __str__(&self) -> String {
100 format!("{}", $crate::PyWrapper::as_inner(self))
101 }
102 }
103 };
104}
105
106/// Implement [`FromStr`](std::str::FromStr) for wrapper types whose inner type implements [`FromStr`](std::str::FromStr).
107///
108/// The second argument must be a Python error wrapper that implements [`From<E>`], where `E = <$name::Inner as FromStr>::Err`.
109#[macro_export]
110macro_rules! impl_from_str {
111 ($name: ident, $error: ty) => {
112 impl ::std::str::FromStr for $name {
113 type Err = $error;
114 fn from_str(input: &str) -> Result<Self, Self::Err> {
115 <<Self as $crate::PyWrapper>::Inner as ::std::str::FromStr>::from_str(input)
116 .map(Self::from)
117 .map_err(
118 <$error as From<
119 <<Self as $crate::PyWrapper>::Inner as ::std::str::FromStr>::Err,
120 >>::from,
121 )
122 }
123 }
124 };
125}
126
127/// Implement a method `parse` for wrapper types that implement [`FromStr`](std::str::FromStr).
128///
129/// See also: [`impl_from_str`].
130#[macro_export]
131macro_rules! impl_parse {
132 ($name: ident) => {
133 #[$crate::pyo3::pymethods]
134 impl $name {
135 /// Implements a static `parse` method for Python in terms of the
136 /// Rust [`FromStr`](std::str::FromStr) instance.
137 #[staticmethod]
138 pub fn parse(input: &str) -> $crate::pyo3::PyResult<Self> {
139 <Self as std::str::FromStr>::from_str(input)
140 .map(Self::from)
141 .map_err($crate::ToPythonError::to_py_err)
142 }
143 }
144 };
145}
146
147/// Implement `AsMut<T>` for a Python wrapper around `T`.
148///
149/// This macro is automatically invoked by [`py_wrap_struct`](crate::py_wrap_struct)
150/// and [`py_wrap_union_enum`](crate::py_wrap_union_enum), and should otherwise only be used if
151/// you need more flexibility and are using [`py_wrap_type`](crate::py_wrap_type) directly.
152#[macro_export]
153macro_rules! impl_as_mut_for_wrapper {
154 ($py_type: ident) => {
155 impl AsMut<<$py_type as $crate::PyWrapper>::Inner> for $py_type {
156 fn as_mut(&mut self) -> &mut <$py_type as $crate::PyWrapper>::Inner {
157 &mut self.0
158 }
159 }
160 };
161}