pyo3/conversions/
either.rs1#![cfg(feature = "either")]
2
3#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"either\"] }")]
20#[cfg(feature = "experimental-inspect")]
48use crate::inspect::types::TypeInfo;
49use crate::{
50 exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPyObject,
51 IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python,
52};
53#[allow(deprecated)]
54use crate::{IntoPy, ToPyObject};
55use either::Either;
56
57#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
58#[allow(deprecated)]
59impl<L, R> IntoPy<PyObject> for Either<L, R>
60where
61 L: IntoPy<PyObject>,
62 R: IntoPy<PyObject>,
63{
64 #[inline]
65 fn into_py(self, py: Python<'_>) -> PyObject {
66 match self {
67 Either::Left(l) => l.into_py(py),
68 Either::Right(r) => r.into_py(py),
69 }
70 }
71}
72
73#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
74impl<'py, L, R> IntoPyObject<'py> for Either<L, R>
75where
76 L: IntoPyObject<'py>,
77 R: IntoPyObject<'py>,
78{
79 type Target = PyAny;
80 type Output = Bound<'py, Self::Target>;
81 type Error = PyErr;
82
83 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
84 match self {
85 Either::Left(l) => l.into_bound_py_any(py),
86 Either::Right(r) => r.into_bound_py_any(py),
87 }
88 }
89}
90
91#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
92impl<'a, 'py, L, R> IntoPyObject<'py> for &'a Either<L, R>
93where
94 &'a L: IntoPyObject<'py>,
95 &'a R: IntoPyObject<'py>,
96{
97 type Target = PyAny;
98 type Output = Bound<'py, Self::Target>;
99 type Error = PyErr;
100
101 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
102 match self {
103 Either::Left(l) => l.into_bound_py_any(py),
104 Either::Right(r) => r.into_bound_py_any(py),
105 }
106 }
107}
108
109#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
110#[allow(deprecated)]
111impl<L, R> ToPyObject for Either<L, R>
112where
113 L: ToPyObject,
114 R: ToPyObject,
115{
116 #[inline]
117 fn to_object(&self, py: Python<'_>) -> PyObject {
118 match self {
119 Either::Left(l) => l.to_object(py),
120 Either::Right(r) => r.to_object(py),
121 }
122 }
123}
124
125#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
126impl<'py, L, R> FromPyObject<'py> for Either<L, R>
127where
128 L: FromPyObject<'py>,
129 R: FromPyObject<'py>,
130{
131 #[inline]
132 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
133 if let Ok(l) = obj.extract::<L>() {
134 Ok(Either::Left(l))
135 } else if let Ok(r) = obj.extract::<R>() {
136 Ok(Either::Right(r))
137 } else {
138 let err_msg = format!(
141 "failed to convert the value to 'Union[{}, {}]'",
142 std::any::type_name::<L>(),
143 std::any::type_name::<R>()
144 );
145 Err(PyTypeError::new_err(err_msg))
146 }
147 }
148
149 #[cfg(feature = "experimental-inspect")]
150 fn type_input() -> TypeInfo {
151 TypeInfo::union_of(&[L::type_input(), R::type_input()])
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use std::borrow::Cow;
158
159 use crate::exceptions::PyTypeError;
160 use crate::{IntoPyObject, Python};
161
162 use crate::types::PyAnyMethods;
163 use either::Either;
164
165 #[test]
166 fn test_either_conversion() {
167 type E = Either<i32, String>;
168 type E1 = Either<i32, f32>;
169 type E2 = Either<f32, i32>;
170
171 Python::with_gil(|py| {
172 let l = E::Left(42);
173 let obj_l = (&l).into_pyobject(py).unwrap();
174 assert_eq!(obj_l.extract::<i32>().unwrap(), 42);
175 assert_eq!(obj_l.extract::<E>().unwrap(), l);
176
177 let r = E::Right("foo".to_owned());
178 let obj_r = (&r).into_pyobject(py).unwrap();
179 assert_eq!(obj_r.extract::<Cow<'_, str>>().unwrap(), "foo");
180 assert_eq!(obj_r.extract::<E>().unwrap(), r);
181
182 let obj_s = "foo".into_pyobject(py).unwrap();
183 let err = obj_s.extract::<E1>().unwrap_err();
184 assert!(err.is_instance_of::<PyTypeError>(py));
185 assert_eq!(
186 err.to_string(),
187 "TypeError: failed to convert the value to 'Union[i32, f32]'"
188 );
189
190 let obj_i = 42i32.into_pyobject(py).unwrap();
191 assert_eq!(obj_i.extract::<E1>().unwrap(), E1::Left(42));
192 assert_eq!(obj_i.extract::<E2>().unwrap(), E2::Left(42.0));
193
194 let obj_f = 42.0f64.into_pyobject(py).unwrap();
195 assert_eq!(obj_f.extract::<E1>().unwrap(), E1::Right(42.0));
196 assert_eq!(obj_f.extract::<E2>().unwrap(), E2::Left(42.0));
197 });
198 }
199}