rigetti_pyo3/
wrappers.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 wrapping different Rust types for use in Python.
16
17/// Creates a new exception type and implements converting from the given Rust error to the new
18/// exception.
19///
20/// The Rust error type must at least implement [`ToString`](std::string::ToString). All types
21/// that implement [`Error`](std::error::Error) implement this through
22/// [`Display`](std::fmt::Display).
23///
24///
25/// ```
26/// use rigetti_pyo3::py_wrap_error;
27/// use rigetti_pyo3::pyo3::exceptions::PyValueError;
28/// use std::fmt;
29///
30/// #[derive(Debug)]
31/// enum RustError {}
32///
33/// impl fmt::Display for RustError {
34///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35///         unimplemented!()
36///     }
37/// }
38///
39/// impl std::error::Error for RustError {}
40///
41/// py_wrap_error!(my_python_module, RustError, PythonError, PyValueError);
42/// ```
43#[macro_export]
44macro_rules! py_wrap_error {
45    ($module: ident, $rust: ty, $python: ident, $base: ty) => {
46        $crate::pyo3::create_exception!($module, $python, $base);
47
48        impl $crate::ToPythonError for $rust {
49            fn to_py_err(self) -> $crate::pyo3::PyErr {
50                <$python>::new_err(self.to_string())
51            }
52        }
53    };
54}
55
56/// Create a Python wrapper around a Rust type.
57///
58/// You probably do not want to call this directly, as other macros build on top of this.
59///
60/// Implements:
61/// - Conversion between wrapper and inner Rust type
62/// - `AsRef` to access the inner Rust type from [`pyo3`](crate::pyo3) code.
63/// - [`PyWrapper`](crate::PyWrapper) as non-generic aliases for the above
64/// - [`ToPyObject`](crate::pyo3::conversion::ToPyObject)
65///
66/// # Macro inputs:
67///
68/// - `$meta`: Any attributes to apply to the wrapper type. Supports `#[pyo3(...)]`
69///   for configuring the Python type.
70/// - `$name`: The Rust name for the wrapper type (usually `PySomething`).
71/// - `$from`: The Rust type to wrap.
72/// - `$py_alias` (optional): The type name to expose to Python (usually `$name` without a leading `Py`).
73///
74/// ```
75/// use std::collections::HashMap;
76/// use rigetti_pyo3::py_wrap_type;
77///
78/// py_wrap_type! {
79///     #[derive(Debug)]
80///     PyNumberLabels(HashMap<String, i32>) as "NumberLabels";
81/// }
82///
83/// let map = HashMap::new();
84/// let dict = PyNumberLabels::from(map);
85/// let map = HashMap::from(dict);
86/// let dict = PyNumberLabels::from(&map);
87/// assert_eq!(&map, dict.as_ref());
88/// ```
89#[macro_export]
90macro_rules! py_wrap_type {
91    (
92        $(#[$meta: meta])*
93        $name: ident($from: ty)$(as $py_alias: literal)?$(;)?
94    ) => {
95        #[repr(transparent)]
96        #[allow(clippy::use_self)]
97        #[$crate::pyo3::pyclass$((name = $py_alias))?]
98        #[derive(Clone)]
99        $(#[$meta])*
100        pub struct $name($from);
101
102        impl $crate::PyTryFrom<$name> for $from {
103            fn py_try_from(
104                py: $crate::pyo3::Python,
105                item: &$name,
106            ) -> $crate::pyo3::PyResult<Self> {
107                Ok(item.0.clone())
108            }
109        }
110
111        impl $crate::PyTryFrom<$crate::pyo3::PyAny> for $name {
112            fn py_try_from(
113                py: $crate::pyo3::Python,
114                item: &$crate::pyo3::PyAny,
115            ) -> $crate::pyo3::PyResult<Self> {
116                item.extract()
117            }
118        }
119
120        impl $crate::PyTryFrom<$name> for $name {
121            fn py_try_from(
122                py: $crate::pyo3::Python,
123                item: &$name,
124            ) -> $crate::pyo3::PyResult<Self> {
125                Ok(item.clone())
126            }
127        }
128
129        $crate::private_impl_to_python_with_reference!(&self, py, $from => $name {
130            Ok($name::from(self.clone()))
131        });
132
133        $crate::private_impl_to_python_with_reference!(&self, py, $name => $crate::pyo3::Py<$crate::pyo3::PyAny> {
134            Ok(<Self as $crate::pyo3::ToPyObject>::to_object(self, py))
135        });
136
137        impl From<$name> for $from {
138            fn from(wrapper: $name) -> Self {
139                wrapper.0
140            }
141        }
142
143        impl From<$from> for $name {
144            fn from(inner: $from) -> Self {
145                Self(inner)
146            }
147        }
148
149        impl From<&$from> for $name {
150            fn from(inner: &$from) -> Self {
151                Self(inner.clone())
152            }
153        }
154
155        impl AsRef<$from> for $name {
156            fn as_ref(&self) -> &$from {
157                &self.0
158            }
159        }
160
161        impl $crate::PyWrapper for $name {
162            type Inner = $from;
163        }
164
165        impl $crate::pyo3::conversion::ToPyObject for $name {
166            fn to_object(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyObject {
167                #[allow(clippy::use_self)]
168                const NAME: &'static str = stringify!($name);
169                let cell = $crate::pyo3::PyCell::new(py, self.clone())
170                    .unwrap_or_else(|err| {
171                        panic!(
172                            "failed to create {} on Python heap: {}",
173                            NAME,
174                            err
175                        )
176                    });
177                $crate::pyo3::conversion::ToPyObject::to_object(&cell, py)
178            }
179        }
180    };
181}
182
183/// Wrap an enum containing only unit variants.
184///
185/// Implements
186///
187/// - Conversion between Rust and Python types (also converting from references to each)
188///
189/// # Macro Inputs
190///
191/// - `$variant_name`: comma-separated list of variant names on the Rust enum. Required because
192///   there is no way to do reflection to programmatically find them.
193/// - `$variant_alias`: used in conjunction with `$variant_name` as the Python enum member name
194///   when it should be named differently, useful in cases when the enum member name is not a
195///   valid python identifier. If one variant uses an alias, they all must, even if the alias
196///   is the same as the name.
197/// - See also [`py_wrap_type`].
198///
199/// # Example
200///
201/// ```
202/// use rigetti_pyo3::py_wrap_simple_enum;
203///
204/// #[derive(Copy, Clone)]
205/// pub enum RustEnum {
206///     Foo,
207///     Bar,
208/// }
209///
210/// py_wrap_simple_enum! {
211///     PyEnum(RustEnum) {
212///         Foo,
213///         Bar
214///     }
215/// }
216///
217/// py_wrap_simple_enum! {
218///     PyEnumAliased(RustEnum) {
219///         Foo as FOO,
220///         Bar as Bar
221///     }
222/// }
223/// ```
224#[macro_export]
225macro_rules! py_wrap_simple_enum {
226    (
227        $(#[$meta: meta])*
228        $name: ident($rs_inner: ident) $(as $py_class: literal)? {
229            $($variant_name: ident),+
230        }
231    ) => {
232        $crate::py_wrap_simple_enum! {
233            $(#[$meta])*
234            $name($rs_inner) $(as $py_class)? {
235                $($variant_name as $variant_name),+
236            }
237        }
238    };
239    (
240        $(#[$meta: meta])*
241        $name: ident($rs_inner: ident) $(as $py_class: literal)? {
242            $($variant_name: ident as $variant_alias: ident),+
243        }
244    ) => {
245        #[derive(Copy, Clone)]
246        #[$crate::pyo3::pyclass$((name = $py_class))?]
247        $(#[$meta])*
248        pub enum $name {
249            $(
250            $variant_alias
251            ),+
252        }
253
254        impl From<$name> for $rs_inner {
255            fn from(item: $name) -> Self {
256                match item {
257                    $(
258                    $name::$variant_alias => Self::$variant_name,
259                    )+
260                }
261            }
262        }
263
264        impl From<&$name> for $rs_inner {
265            fn from(item: &$name) -> Self {
266                Self::from(*item)
267            }
268        }
269
270        impl From<$rs_inner> for $name {
271            fn from(item: $rs_inner) -> Self {
272                match item {
273                    $(
274                    $rs_inner::$variant_name => $name::$variant_alias,
275                    )+
276                }
277            }
278        }
279
280        impl From<&$rs_inner> for $name {
281            fn from(item: &$rs_inner) -> Self {
282                Self::from(*item)
283            }
284        }
285
286        impl $crate::PyWrapper for $name {
287            type Inner = $rs_inner;
288        }
289
290        impl AsRef<$rs_inner> for $name {
291            fn as_ref(&self) -> &$rs_inner {
292                match self {
293                    $(
294                    $name::$variant_alias => &$rs_inner::$variant_name,
295                    )+
296                }
297            }
298        }
299
300        impl $crate::pyo3::conversion::ToPyObject for $name {
301            fn to_object(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyObject {
302                let cell = $crate::pyo3::PyCell::new(py, self.clone())
303                    .unwrap_or_else(|err| panic!("failed to create {} on Python heap: {}", stringify!($name), err));
304                cell.to_object(py)
305            }
306        }
307
308        $crate::private_impl_to_python_with_reference!(&self, _py, $rs_inner => $name {
309            Ok($name::from(self))
310        });
311
312        $crate::private_impl_py_try_from!(&item, _py, $name => $rs_inner {
313            Ok(*item.as_ref())
314        });
315    }
316}
317
318/// Create a newtype wrapper for a Rust struct.
319///
320/// Implements the following:
321///
322/// - Conversion to/from the contained Rust type
323/// - Conversion to/from the related Python/Rust types
324/// - Constructor taking any type that can be converted from
325///
326/// # Limitations
327///
328/// This macro generates a `__new__` constructor for the Python type from the given
329/// `py -> rs` conversions. This constructor expects exactly one parameter, which cannot
330/// be omitted (i.e. has no default value).
331///
332/// To have more control over the constructor, use [`py_wrap_type`] with a manual
333/// implementation in a `pymethods` `impl` block.
334///
335/// # Example
336///
337/// ```
338/// use rigetti_pyo3::py_wrap_struct;
339/// use rigetti_pyo3::pyo3::{Py, PyErr, Python};
340/// use rigetti_pyo3::pyo3::conversion::{IntoPy, PyTryFrom, ToPyObject};
341/// use rigetti_pyo3::pyo3::types::{PyDict, PyTuple};
342///
343/// #[derive(Clone)]
344/// pub struct Foo {
345///     bar: String,
346///     baz: f32,
347/// }
348///
349/// impl From<(String, f32)> for Foo {
350///     fn from(tuple: (String, f32)) -> Self {
351///         Self { bar: tuple.0, baz: tuple.1 }
352///     }
353/// }
354///
355/// impl From<Foo> for (String, f32) {
356///     fn from(foo: Foo) -> Self {
357///         (foo.bar, foo.baz)
358///     }
359/// }
360///
361/// py_wrap_struct! {
362///     PyFoo(Foo) {
363///         // Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
364///         // Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
365///         py -> rs {
366///             py_dict: Py<PyDict> => Foo {
367///                 let bar = py_dict.as_ref(py).get_item("bar")?.unwrap().extract().unwrap();
368///                 let baz = py_dict.as_ref(py).get_item("baz")?.unwrap().extract().unwrap();
369///                 Ok::<_, PyErr>(Foo { bar, baz })
370///             },
371///             py_tuple: Py<PyTuple> => (String, f32) {
372///                 Ok::<_, PyErr>((
373///                     py_tuple.as_ref(py).get_item(0)?.extract().unwrap(),
374///                     py_tuple.as_ref(py).get_item(1)?.extract().unwrap(),
375///                 ))
376///             }
377///         },
378///         // Infallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`.
379///         // Used to implement `From<PyFoo> for P`.
380///         rs -> py {
381///             rs_tuple: (String, f32) => Py<PyTuple> {
382///                 Python::with_gil(|py| {
383///                     let obj = rs_tuple.to_object(py);
384///                     <PyTuple as PyTryFrom>::try_from(obj.as_ref(py))
385///                         .map(|tuple| tuple.into_py(py))
386///                         .map_err(PyErr::from)
387///                 })
388///             }
389///         }
390///     }
391/// }
392/// ```
393#[macro_export]
394macro_rules! py_wrap_struct {
395    (
396        $(#[$meta: meta])*
397        $name: ident($rs_from: ty) $(as $py_class: literal)? {
398            /// Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
399            /// Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
400            ///
401            /// $py_for_from should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
402            $($py_for_from: ident -> rs {
403                $($py_ident: ident: $py_src: ty => $rs_dest: ty $to_rs: block),+
404            },)?
405            /// Fallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`
406            /// Used to implement `TryFrom<PyFoo> for P`. Any errors returned must be `PyErr`.
407            ///
408            /// $py_for_to should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
409            $(rs -> $py_for_to: ident {
410                $($rs_ident: ident: $rs_src: ty => $py_dest: ty $to_py: block),+
411            })?
412        }
413    ) => {
414        $crate::py_wrap_type! {
415            $(
416            #[$meta]
417            )*
418            $name($rs_from) $(as $py_class)?;
419        }
420
421        $($(
422        impl TryFrom<$py_src> for $name {
423            #[allow(unused_qualifications)]
424            type Error = pyo3::PyErr;
425            fn try_from($py_ident: $py_src) -> Result<Self, Self::Error> {
426                $crate::pyo3::Python::with_gil(|$py_for_from| {
427                    let rust = {
428                        $to_rs
429                    }?;
430                    Ok(Self::from(<$rs_from>::from(rust)))
431                })
432            }
433        }
434        )+)?
435
436        $($(
437        impl TryFrom<$name> for $py_dest {
438            #[allow(unused_qualifications)]
439            type Error = pyo3::PyErr;
440            fn try_from(outer: $name) -> Result<Self, Self::Error> {
441                let $rs_ident = $crate::PyWrapper::into_inner(outer);
442                let $rs_ident: $rs_src = From::from($rs_ident);
443                $crate::pyo3::Python::with_gil(|$py_for_to| {
444                    $to_py
445                })
446            }
447        }
448        )+)?
449
450        $crate::impl_as_mut_for_wrapper!($name);
451
452        #[$crate::pyo3::pymethods]
453        impl $name {
454            #![allow(clippy::use_self)]
455
456            #[doc = concat!(
457                r"Create a new [`",
458                stringify!($name),
459                r"`] from Python arguments; corresponds to `",
460                $($py_class, r".",)?
461                r"__new__()` in Python"
462            )]
463            #[new]
464            pub fn new(py: $crate::pyo3::Python, input: $crate::pyo3::Py<$crate::pyo3::PyAny>) -> $crate::pyo3::PyResult<Self> {
465                use $crate::pyo3::FromPyObject;
466
467                $($(
468                if let Ok(item) = input.extract::<$py_src>(py) {
469                    return Self::try_from(item);
470                }
471                )+)?
472
473                Err($crate::pyo3::exceptions::PyValueError::new_err(
474                    concat!("expected one of:" $($(, " ", std::stringify!($py_src))+)?)
475                ))
476            }
477        }
478    }
479}
480
481/// (Internal) Helper macro to get the final type in a chain of conversions.
482///
483/// Necessary because the pattern `$(=> $foo: ty)* => $bar: ty` is ambiguous.
484#[macro_export]
485macro_rules! private_ultimate_type {
486    ($type: ty) => { $type };
487    ($type: ty, $($others: ty),+) => { $crate::private_ultimate_type!($($others),+) }
488}
489
490/// (Internal) Helper macro to implement chained conversion through intermediate types,
491/// where the type system cannot determine a path from the first to last item.
492#[macro_export]
493macro_rules! private_intermediate_to_python {
494    ($py: ident, &$item: ident $(=> $convert: ty)+) => {{
495        $(
496        let $item: $convert = $crate::ToPython::<$convert>::to_python(&$item, $py)?;
497        )+
498        Ok::<_, $crate::pyo3::PyErr>($item)
499    }}
500}
501
502/// (Internal) Helper macro to implement chained conversion through intermediate types,
503/// where the type system cannot determine a path from the last to first item.
504#[macro_export]
505macro_rules! private_intermediate_try_from_python {
506    ($py: ident, &$item: ident => $convert: ty $($(=> $delayed: ty)+)?) => {{
507        $(let $item: $convert = $crate::private_intermediate_try_from_python!($py, &$item $(=> $delayed)+)?;
508        let $item = &$item;)?
509        <_ as $crate::PyTryFrom<$convert>>::py_try_from($py, $item)
510    }};
511}
512
513/// Create a newtype wrapper for a Rust enum with unique 1-tuple variants.
514///
515/// # Implements
516///
517/// - Conversion between the wrapper and the inner enum
518/// - A Python constructor that creates a new instance from one of the Python variants.
519/// - A Python function `inner()` that directly returns the Python version of the variant
520///   discriminant (i.e. `Discriminant` in `Enum::Variant(Discriminant)`).
521/// - Python conversion functions:
522///     - `from_x`: Like the constructor, but for a specific variant `x`.
523///     - `is_x`: Returns `True` if the enum is variant `x`.
524///     - `as_x`: Returns the discriminant if the enum is variant `x`, otherwise `None`.
525///     - `to_x`: Returns the discriminant if the enum is variant `x`, otherwise raises
526///       `ValueError`.
527///
528/// # Example
529///
530/// ```
531/// use rigetti_pyo3::py_wrap_union_enum;
532/// use rigetti_pyo3::pyo3::prelude::*;
533/// use rigetti_pyo3::pyo3::types::*;
534/// use std::collections::HashSet;
535///
536/// #[derive(Clone)]
537/// pub enum TestEnum {
538///     Unit,
539///     String(String),
540///     Integer(i32),
541///     UInteger(u32),
542///     List(Vec<HashSet<String>>),
543///     Mapping(std::collections::HashMap<String, String>),
544/// }
545///
546/// py_wrap_union_enum! {
547///     PyTestEnum(TestEnum) as "TestEnum" {
548///         // Syntax is (1): (2) [=> (3)] [=> (4)] [...], where:
549///         // 1: The name used in generated methods
550///         // 2: The name of the Rust enum variant
551///         // 3: The (Python) type the inner item must convert to (if it has associated data)
552///         // 4: The (Python) type the type from (3) must convert to, etc.
553///         unit: Unit,
554///         // Read as "give the name `string` to variant `String`, which must convert (from
555///         // a `String`) to a `String` and then to a `Py<PyString>`."
556///         //
557///         // That is, `string` is used to generate methods `is_string`, `from_string`, etc.;
558///         // the first `String` is the name of the variant, not the type (which is elided);
559///         // the second `String` is the type to convert the elided type into, and `Py<String>` is
560///         // the final type to convert into.
561///         //
562///         // This specifies an unnecessary conversion from String => String to illustrate
563///         // conversion chaining.
564///         string: String => String => Py<PyString>,
565///         int: Integer => Py<PyInt>,
566///         uint: UInteger => Py<PyInt>,
567///         list: List => Py<PyList>,
568///         // Alternatively, in the case of `Vec<T>` where `T` does not have conversion to `PyAny`.
569///         // list: List => Vec<Py<PySet>> => Py<PyList>,
570///         // Generates `from_dict`, `is_dict`, `as_dict`, `to_dict`
571///         dict: Mapping => Py<PyDict>
572///     }
573/// }
574/// ```
575#[macro_export]
576macro_rules! py_wrap_union_enum {
577    // @from creates its own impl block to avoid an error of "cannot find attribute `staticmethod`
578    // in this scope".
579    //
580    // There may be a performance hit to this, but I (@Shadow53) cannot figure out how to do this
581    // otherwise without rewriting everything as procedural macros.
582    //
583    // Note: the cause of the error was the use of `paste!` *within* a `pymethods` impl block.
584    // Having the impl block within the `paste!` macro is what makes the error go away.
585    (@from $name: ident, $rs_enum: ident, $variant_name: ident, $variant: ident $(=> $convert: ty)+) => {
586        $crate::paste::paste! {
587            #[$crate::pyo3::pymethods]
588            impl $name {
589                #[doc = concat!(
590                    r"The Python wrapper for [`",
591                    stringify!($rs_enum),
592                    r"::",
593                    stringify!($variant),
594                    r"`], creating a [`",
595                    stringify!($name),
596                    r"`] and taking a Python argument."
597                )]
598                #[staticmethod]
599                pub fn [< from_ $variant_name >](py: $crate::pyo3::Python, inner: $crate::private_ultimate_type!($($convert),+)) -> $crate::pyo3::PyResult<Self> {
600                    let inner = &inner;
601                    $crate::private_intermediate_try_from_python!(py, &inner $(=> $convert)+)
602                        .map($rs_enum::$variant)
603                        .map(Self)
604                }
605            }
606        }
607    };
608    (@from $name: ident, $rs_enum: ident, $variant_name: ident, $variant: ident) => {
609        $crate::paste::paste! {
610            #[$crate::pyo3::pymethods]
611            impl $name {
612                #[doc = concat!(
613                    r"Create a new [`", stringify!($name), r"`] wrapping a ",
614                    r"[`", stringify!($rs_enum), r"::", stringify!($variant), "`]."
615                )]
616                #[staticmethod]
617                pub fn [< new_ $variant_name >]() -> Self {
618                    Self::from($rs_enum::$variant)
619                }
620            }
621        }
622    };
623    (@is_variant $self: ident, $rs_enum: ident, $variant: ident ($(=> $_convert: ty)+)) => {
624        match &$self.0 {
625            $rs_enum::$variant(_) => true,
626            _ => false,
627        }
628    };
629    (@is_variant $self: ident, $rs_enum: ident, $variant: ident) => {
630        match &$self.0 {
631            $rs_enum::$variant => true,
632            _ => false,
633        }
634    };
635    (
636        $(#[$meta: meta])*
637        $name: ident($rs_inner: ident) $(as $py_class: literal)? {
638            $($variant_name: ident: $variant: ident $($(=> $convert: ty)+)?),+
639        }
640    ) => {
641        $crate::py_wrap_type! {
642            $(#[$meta])*
643            $name($rs_inner) $(as $py_class)?;
644        }
645
646        $crate::impl_as_mut_for_wrapper!($name);
647
648        $(
649        $crate::py_wrap_union_enum!(@from $name, $rs_inner, $variant_name, $variant $($(=> $convert)+)?);
650        )+
651
652        $crate::paste::paste! {
653        #[$crate::pyo3::pymethods]
654        impl $name {
655            #[doc = concat!(
656                r"Create a new [`",
657                stringify!($name),
658                r"`] from a Python argument; corresponds to `",
659                $($py_class, r".",)?
660                r"__new__()` in Python"
661            )]
662            #[new]
663            pub fn new(py: $crate::pyo3::Python, input: &$crate::pyo3::PyAny) -> $crate::pyo3::PyResult<Self> {
664                $(
665                    $(
666                        if let Ok(inner) = <_ as $crate::PyTryFrom<$crate::pyo3::PyAny>>::py_try_from(py, input) {
667                            let inner = &inner;
668                            let converted = $crate::private_intermediate_try_from_python!(py, &inner $(=> $convert)+);
669                            if let Ok(item) = converted {
670                                return Ok(Self::from($rs_inner::$variant(item)));
671                            }
672                        }
673                    )?
674                )+
675
676                Err($crate::pyo3::exceptions::PyValueError::new_err(
677                    format!(
678                        "could not create {} from {}",
679                        stringify!($name),
680                        input.repr()?
681                    )
682                ))
683            }
684
685            #[doc = concat!(
686                r"Directly return the Python version of the variant discriminant wrapped by this ",
687                r"value; ",
688                r"i.e., performs the match `",
689                stringify!($rs_inner),
690                r"::Variant(x) => x` for every variant constructor in [`",
691                stringify!($rs_inner),
692                r"`]"
693            )]
694            #[allow(unreachable_code, unreachable_pattern)]
695            pub fn inner(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyResult<$crate::pyo3::Py<$crate::pyo3::PyAny>> {
696                match &self.0 {
697                    $(
698                        $($rs_inner::$variant(inner) => {
699                            Ok($crate::pyo3::conversion::IntoPy::<$crate::pyo3::Py<$crate::pyo3::PyAny>>::into_py(
700                                $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)?,
701                                py,
702                            ))
703                        },)?
704                    )+
705                    _ => {
706                        use $crate::pyo3::exceptions::PyRuntimeError;
707                        Err(PyRuntimeError::new_err("Enum variant has no inner data or is unimplemented"))
708                    }
709                }
710            }
711
712            $(
713            #[doc = concat!(
714                r"Tests if this [`", stringify!($name), r"`] ",
715                r"wraps a [`", stringify!($rs_inner), r"::", stringify!($variant_name), "`] value"
716            )]
717            const fn [< is_ $variant_name >](&self) -> bool {
718                $crate::py_wrap_union_enum!(@is_variant self, $rs_inner, $variant $(($(=> $convert)+))?)
719            }
720
721                $(
722                #[doc = concat!(
723                    r"Returns `x` if this [`", stringify!($name), r"`] ",
724                    r"wraps a `", stringify!($rs_inner), r"::", stringify!($variant_name), "`(x); ",
725                    r"otherwise returns (Python) `None`.  On the Rust side, this corresponds to ",
726                    r"either `Some(x)` or [`None`]."
727                )]
728                fn [< as_ $variant_name >](&self, py: $crate::pyo3::Python) -> Option<$crate::private_ultimate_type!($($convert),+)> {
729                    self.[< to_ $variant_name >](py).ok()
730                }
731
732                #[doc = concat!(
733                    r"Returns `x` if this [`", stringify!($name), r"`] ",
734                    r"wraps a `", stringify!($rs_inner), r"::", stringify!($variant_name), "`(x); ",
735                    r"otherwise raises a `ValueError`.  On the Rust side, this corresponds to ",
736                    r"either `Ok(x)` or `Err(...)`."
737                )]
738                fn [< to_ $variant_name >](&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyResult<$crate::private_ultimate_type!($($convert),+)> {
739                    if let $rs_inner::$variant(inner) = &self.0 {
740                        $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)
741                    } else {
742                        Err($crate::pyo3::exceptions::PyValueError::new_err(
743                            concat!("expected self to be a ", stringify!($variant_name))
744                        ))
745                    }
746                }
747                )?
748            )+
749        }
750        }
751    }
752}
753
754/// Wraps an external error type in a newtype `struct` so it can be used with [`py_wrap_error`].
755///
756/// # Implements
757///
758/// - [`From`] impls between the newtype and the inner type.
759/// - [`Display`](std::fmt::Display) delegating to the inner type
760/// - [`Error`](std::error::Error)
761///
762/// # Example
763///
764/// ```
765/// use rigetti_pyo3::{wrap_error, py_wrap_error};
766/// use rigetti_pyo3::pyo3::exceptions::PyRuntimeError;
767///
768/// wrap_error!{
769///     RustIOError(std::io::Error);
770/// }
771///
772/// py_wrap_error!(errors, RustIOError, IOError, PyRuntimeError);
773/// ```
774#[macro_export]
775macro_rules! wrap_error {
776    ($(#[$meta: meta])* $name: ident ($inner: ty)$(;)?) => {
777        $(#[$meta])*
778        #[derive(Debug)]
779        #[repr(transparent)]
780        pub struct $name($inner);
781
782        impl From<$inner> for $name {
783            fn from(inner: $inner) -> Self {
784                Self(inner)
785            }
786        }
787
788        impl From<$name> for $inner {
789            fn from(outer: $name) -> Self {
790                outer.0
791            }
792        }
793
794        impl ::std::fmt::Display for $name {
795            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
796                write!(f, "{}", self.0)
797            }
798        }
799
800        impl ::std::error::Error for $name {}
801    };
802}
803
804/// Wraps a data struct and makes (some of) its fields available to Python.
805///
806/// # Implements
807///
808/// - Everything implemented by [`py_wrap_type`].
809/// - [`PyWrapperMut`](crate::PyWrapperMut).
810/// - `get_foo` and `set_foo` methods for field `foo`, which translate to `@property` and
811///   `@foo.setter` in Python, i.e. allowing access to the field as a property.
812///
813/// # Warning!
814///
815/// The mutability of exposed fields may not work as you expect.
816///
817/// Since objects are converted back and forth along the FFI boundary using `Clone`s,
818/// pointers are not shared like in native Python. In native Python, this code runs
819/// without issue:
820///
821/// ```python
822/// class Test:
823///   def __init__(self):
824///       self.inner = {}
825///
826///   @property
827///   def foo(self):
828///     return self.inner
829///
830///   @foo.setter
831///   def foo(self, value):
832///     self.inner = value
833///
834/// c = Test()
835/// d = c.inner
836///
837/// d["a"] = ["a"]
838/// assert "a" in c.inner
839///
840/// d = c.foo
841/// d["b"] = "b"
842/// assert "b" in c.inner
843/// ```
844///
845/// Using these bindings, assuming that this macro was used to create `Test`, the
846/// equivalent would be:
847///
848/// ```python
849/// c = Test()
850/// d = c.foo
851///
852/// d["a"] = ["a"]
853/// assert "a" not in c.foo
854/// c.foo = d
855/// assert "a" in c.foo
856/// ```
857///
858/// # Example
859///
860/// ```
861/// use rigetti_pyo3::pyo3::{Py, types::{PyInt, PyString}};
862/// use rigetti_pyo3::py_wrap_data_struct;
863///
864/// #[derive(Clone)]
865/// pub struct Person {
866///     pub name: String,
867///     pub age: u8,
868/// }
869///
870/// py_wrap_data_struct! {
871///     PyPerson(Person) as "Person" {
872///         name: String => Py<PyString>,
873///         age: u8 => Py<PyInt>
874///     }
875/// }
876/// ```
877#[macro_export]
878macro_rules! py_wrap_data_struct {
879    (
880        $(#[$meta: meta])*
881        $name: ident($rs_inner: ty) $(as $class_name: literal)? {
882            $(
883            $field_name: ident: $field_rs_type: ty $(=> $convert: ty)+
884            ),+
885        }
886    ) => {
887        $crate::py_wrap_type! {
888            $(
889            #[$meta]
890            )*
891            $name($rs_inner) $(as $class_name)?;
892        }
893
894        $crate::impl_as_mut_for_wrapper!($name);
895
896        $crate::paste::paste! {
897            #[rigetti_pyo3::pyo3::pymethods]
898            impl $name {
899                $(
900                #[doc = concat!(
901                    r"Get the ", stringify!($field_name), r" field from Python.  ",
902                    r"Annotated with `@property`."
903                )]
904                #[getter]
905                fn [< get_ $field_name >](&self, py: $crate::pyo3::Python<'_>) -> $crate::pyo3::PyResult<$crate::private_ultimate_type!($($convert),+)> {
906                    use $crate::{PyWrapper, ToPython};
907                    let inner = &self.as_inner().$field_name;
908                    $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)
909                }
910
911                #[doc = concat!(
912                    r"Set the ", stringify!($field_name), r" field from Python.  ",
913                    r"Annotated with `@", stringify!($field_name), r".setter`."
914                )]
915                #[setter]
916                fn [< set_ $field_name >](&mut self, py: $crate::pyo3::Python<'_>, from: $crate::private_ultimate_type!($($convert),+)) -> $crate::pyo3::PyResult<()> {
917                    use $crate::{PyTryFrom, PyWrapperMut};
918                    let from = &from;
919                    let new_val: $field_rs_type = $crate::private_intermediate_try_from_python!(py, &from $(=> $convert)+)?;
920                    self.as_inner_mut().$field_name = new_val;
921                    Ok(())
922                }
923                )+
924            }
925        }
926    };
927}