#![cfg(feature = "either")]
#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"either\"] }")]
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::{
exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPy, PyAny,
PyObject, PyResult, Python, ToPyObject,
};
use either::Either;
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<L, R> IntoPy<PyObject> for Either<L, R>
where
L: IntoPy<PyObject>,
R: IntoPy<PyObject>,
{
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
match self {
Either::Left(l) => l.into_py(py),
Either::Right(r) => r.into_py(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<L, R> ToPyObject for Either<L, R>
where
L: ToPyObject,
R: ToPyObject,
{
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
match self {
Either::Left(l) => l.to_object(py),
Either::Right(r) => r.to_object(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<'py, L, R> FromPyObject<'py> for Either<L, R>
where
L: FromPyObject<'py>,
R: FromPyObject<'py>,
{
#[inline]
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
if let Ok(l) = obj.extract::<L>() {
Ok(Either::Left(l))
} else if let Ok(r) = obj.extract::<R>() {
Ok(Either::Right(r))
} else {
let err_msg = format!(
"failed to convert the value to 'Union[{}, {}]'",
std::any::type_name::<L>(),
std::any::type_name::<R>()
);
Err(PyTypeError::new_err(err_msg))
}
}
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::union_of(&[L::type_input(), R::type_input()])
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::exceptions::PyTypeError;
use crate::{Python, ToPyObject};
use either::Either;
#[test]
fn test_either_conversion() {
type E = Either<i32, String>;
type E1 = Either<i32, f32>;
type E2 = Either<f32, i32>;
Python::with_gil(|py| {
let l = E::Left(42);
let obj_l = l.to_object(py);
assert_eq!(obj_l.extract::<i32>(py).unwrap(), 42);
assert_eq!(obj_l.extract::<E>(py).unwrap(), l);
let r = E::Right("foo".to_owned());
let obj_r = r.to_object(py);
assert_eq!(obj_r.extract::<Cow<'_, str>>(py).unwrap(), "foo");
assert_eq!(obj_r.extract::<E>(py).unwrap(), r);
let obj_s = "foo".to_object(py);
let err = obj_s.extract::<E1>(py).unwrap_err();
assert!(err.is_instance_of::<PyTypeError>(py));
assert_eq!(
err.to_string(),
"TypeError: failed to convert the value to 'Union[i32, f32]'"
);
let obj_i = 42.to_object(py);
assert_eq!(obj_i.extract::<E1>(py).unwrap(), E1::Left(42));
assert_eq!(obj_i.extract::<E2>(py).unwrap(), E2::Left(42.0));
let obj_f = 42.0.to_object(py);
assert_eq!(obj_f.extract::<E1>(py).unwrap(), E1::Right(42.0));
assert_eq!(obj_f.extract::<E2>(py).unwrap(), E2::Left(42.0));
});
}
}