extendr_api/optional/
either.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*!
Enables support for the [`either`](https://docs.rs/either/latest/either/) crate,
to allow accepting and returning `Either<L, R>` values if both `L` and `R` are convertible to/from `Robj`.

`either` crate support is currently available in the dev version of `extendr-api`
and requires enabling `either` feature:

```toml
[dependencies]
extendr-api = { git = "https://github.com/extendr/extendr" , features = ["either"] }
```

```rust
use extendr_api::prelude::*;

#[extendr]
fn accept_numeric(input : Either<Integers, Doubles>) {}
```

Here is an example of `either` usage -- a type-aware sum:
```rust
use extendr_api::prelude::*;

#[extendr]
fn type_aware_sum(input : Either<Integers, Doubles>) -> Either<Rint, Rfloat> {
    match input {
        Left(ints) => Left(ints.iter().sum::<Rint>()),
        Right(dbls) => Right(dbls.iter().sum::<Rfloat>()),
    }
}
```
*/
use crate::prelude::*;
use crate::{Error, Robj};

impl<'a, L, R> TryFrom<&'a Robj> for Either<L, R>
where
    L: TryFrom<&'a Robj, Error = Error>,
    R: TryFrom<&'a Robj, Error = Error>,
{
    type Error = Error;

    /// Returns the first type that matches the provided `Robj`, starting from
    /// `L`-type, and if that fails, then the `R`-type is converted.
    fn try_from(value: &'a Robj) -> Result<Self> {
        match L::try_from(value) {
            Ok(left) => Ok(Left(left)),
            Err(left_err) => match R::try_from(value) {
                Ok(right) => Ok(Right(right)),
                Err(right_err) => Err(Error::EitherError(Box::new(left_err), Box::new(right_err))),
            },
        }
    }
}

impl<L, R> TryFrom<Robj> for Either<L, R>
where
    for<'a> Either<L, R>: TryFrom<&'a Robj, Error = Error>,
{
    type Error = Error;

    /// Returns the first type that matches the provided `Robj`, starting from
    /// `L`-type, and if that fails, then the `R`-type is converted.
    fn try_from(value: Robj) -> Result<Self> {
        (&value).try_into()
    }
}

impl<L, R> From<Either<L, R>> for Robj
where
    Robj: From<L> + From<R>,
{
    fn from(value: Either<L, R>) -> Self {
        match value {
            Left(left) => Robj::from(left),
            Right(right) => Robj::from(right),
        }
    }
}