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