macro_rules! gen_unop {
($type : ty, $opname : ident, $expr: expr, $docstring: expr) => {
impl $opname for $type {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [<$opname:lower>](self) -> Self::Output {
if let Some(lhs) = self.into() {
let f = $expr;
if let Some(res) = f(lhs) {
return $type::from(res);
}
}
$type::na()
}
}
}
impl $opname for &$type {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [< $opname:lower >](self) -> Self::Output {
if let Some(lhs) = (*self).into() {
let f = $expr;
if let Some(res) = f(lhs) {
return $type::from(res);
}
}
$type::na()
}
}
}
};
}
macro_rules! gen_binop {
($type : tt, $type_prim : tt, $opname : ident, $expr: expr, $docstring: expr) => {
impl $opname<$type> for $type {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [< $opname:lower >](self, rhs: $type) -> Self::Output {
if let Some(lhs) = self.clone().into() {
if let Some(rhs) = rhs.into() {
let f = $expr;
if let Some(res) = f(lhs, rhs) {
return $type::from(res);
}
}
}
$type::na()
}
}
}
impl $opname<$type> for &$type {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [< $opname:lower >](self, rhs: $type) -> Self::Output {
if let Some(lhs) = self.clone().into() {
if let Some(rhs) = rhs.into() {
let f = $expr;
if let Some(res) = f(lhs, rhs) {
return $type::from(res);
}
}
}
$type::na()
}
}
}
impl $opname<$type_prim> for $type {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [< $opname:lower >](self, rhs: $type_prim) -> Self::Output {
if let Some(lhs) = self.clone().into() {
let f = $expr;
if let Some(res) = f(lhs, rhs) {
return $type::from(res);
}
}
$type::na()
}
}
}
impl $opname<$type> for $type_prim {
type Output = $type;
paste::paste! {
#[doc = $docstring]
fn [< $opname:lower >](self, rhs: $type) -> Self::Output {
if let Some(rhs) = rhs.clone().into() {
let f = $expr;
if let Some(res) = f(self, rhs) {
return $type::from(res);
}
}
$type::na()
}
}
}
};
}
macro_rules! gen_binopassign {
($type : ty, $type_prim : ty, $opname : ident, $expr: expr, $docstring: expr) => {
impl $opname<$type> for $type {
paste::paste! {
#[doc = $docstring]
fn [< $opname:snake >](&mut self, other: $type) {
match (self.clone().into(), other.into()) {
(Some(lhs), Some(rhs)) => {
let f = $expr;
match f(lhs, rhs) {
Some(res) => *self = $type::from(res),
None => *self = $type::na(),
}
},
_ => *self = $type::na(),
}
}
}
}
impl $opname<$type> for &mut $type {
paste::paste! {
#[doc = $docstring]
fn [< $opname:snake >](&mut self, other: $type) {
match (self.clone().into(), other.into()) {
(Some(lhs), Some(rhs)) => {
let f = $expr;
match f(lhs, rhs) {
Some(res) => **self = $type::from(res),
None => **self = $type::na(),
}
},
_ => **self = $type::na(),
}
}
}
}
impl $opname<$type_prim> for $type {
paste::paste! {
#[doc = $docstring]
fn [< $opname:snake >](&mut self, other: $type_prim) {
match self.clone().into() {
Some(lhs) => {
let f = $expr;
match f(lhs, other) {
Some(res) => *self = $type::from(res),
None => *self = $type::na(),
}
}
None => *self = $type::na(),
}
}
}
}
impl $opname<$type_prim> for &mut $type {
paste::paste! {
#[doc = $docstring]
fn [< $opname:snake >](&mut self, other: $type_prim) {
match self.clone().into() {
Some(lhs) => {
let f = $expr;
match f(lhs, other) {
Some(res) => **self = $type::from(res),
None => **self = $type::na(),
}
}
None => **self = $type::na(),
}
}
}
}
impl $opname<$type> for Option<$type_prim> {
paste::paste! {
#[doc = $docstring]
fn [< $opname:snake >](&mut self, other: $type) {
match (*self, other.into()) {
(Some(lhs), Some(rhs)) => {
let f = $expr;
let _ = (); *self = f(lhs, rhs);
},
_ => *self = None,
}
}
}
}
};
}
macro_rules! gen_from_primitive {
($type : tt, $type_prim : tt) => {
impl From<$type_prim> for $type {
fn from(v: $type_prim) -> Self {
Self(v)
}
}
impl From<&$type_prim> for $type {
fn from(v: &$type_prim) -> Self {
Self(*v)
}
}
impl From<Option<$type_prim>> for $type {
fn from(v: Option<$type_prim>) -> Self {
if let Some(v) = v {
v.into()
} else {
$type::na()
}
}
}
impl From<Option<&$type_prim>> for $type {
fn from(v: Option<&$type_prim>) -> Self {
if let Some(v) = v {
v.into()
} else {
$type::na()
}
}
}
};
}
macro_rules! gen_trait_impl {
($type : ident, $type_prim : ty, $na_check : expr, $na_val : expr) => {
impl Clone for $type {
fn clone(&self) -> Self {
*self
}
}
impl Copy for $type {}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert!((<" $type ">::na()).is_na());"]
#[doc = "}"]
#[doc = "```"]
impl CanBeNA for $type {
fn is_na(&self) -> bool {
$na_check(self)
}
fn na() -> Self {
$type($na_val)
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert!(<" $type ">::default().eq(&<" $type ">::default()));"]
#[doc = " assert!(!<" $type ">::na().eq(&<" $type ">::na()));"]
#[doc = "}"]
#[doc = "```"]
impl PartialEq<$type> for $type {
fn eq(&self, other: &$type) -> bool {
!(self.is_na() || other.is_na()) && self.0.eq(&other.0)
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert!(<" $type ">::default().eq(&<" $type_prim ">::default()));"]
#[doc = "}"]
#[doc = "```"]
impl PartialEq<$type_prim> for $type {
fn eq(&self, other: &$type_prim) -> bool {
<Option<$type_prim>>::try_from(self.clone()) == Ok(Some(*other))
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert!(<" $type_prim ">::default().eq(&<" $type ">::default()));"]
#[doc = "}"]
#[doc = "```"]
impl PartialEq<$type> for $type_prim {
fn eq(&self, other: &$type) -> bool {
<Option<$type_prim>>::try_from(*other) == Ok(Some(*self))
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert_eq!(<" $type ">::default(), <" $type_prim ">::default());"]
#[doc = "}"]
#[doc = "```"]
impl std::default::Default for $type {
fn default() -> Self {
$type::from(<$type_prim>::default())
}
}
}
};
}
macro_rules! gen_partial_ord {
($type : ident, $type_prim : ty) => {
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert_eq!(<" $type ">::default() < <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type ">::default() <= <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type ">::default() > <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type ">::default() >= <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type ">::default() < <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type ">::default() <= <" $type ">::default(), true);"]
#[doc = " assert_eq!(<" $type ">::default() > <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type ">::default() >= <" $type ">::default(), true);"]
#[doc = "}"]
#[doc = "```"]
impl std::cmp::PartialOrd<$type> for $type {
fn partial_cmp(&self, other: &$type) -> Option<std::cmp::Ordering> {
if self.is_na() || other.is_na() {
None
} else {
self.0.partial_cmp(&other.0)
}
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert_eq!(<" $type_prim ">::default() < <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() <= <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() > <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() >= <" $type ">::na(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() < <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() <= <" $type ">::default(), true);"]
#[doc = " assert_eq!(<" $type_prim ">::default() > <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type_prim ">::default() >= <" $type ">::default(), true);"]
#[doc = "}"]
#[doc = "```"]
impl std::cmp::PartialOrd<$type_prim> for $type {
fn partial_cmp(&self, other: &$type_prim) -> Option<std::cmp::Ordering> {
let other: $type = (*other).try_into().unwrap_or($type::na());
self.partial_cmp(&other)
}
}
}
paste::paste! {
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "test! {"]
#[doc = " assert_eq!(<" $type ">::default() < <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type ">::default() <= <" $type ">::default(), true);"]
#[doc = " assert_eq!(<" $type ">::default() > <" $type ">::default(), false);"]
#[doc = " assert_eq!(<" $type ">::default() >= <" $type ">::default(), true);"]
#[doc = "}"]
#[doc = "```"]
impl std::cmp::PartialOrd<$type> for $type_prim {
fn partial_cmp(&self, other: &$type) -> Option<std::cmp::Ordering> {
let slf: $type = (*self).try_into().unwrap_or($type::na());
slf.partial_cmp(other)
}
}
}
};
}
macro_rules! gen_sum_iter {
($type : ty) => {
impl std::iter::Sum for $type {
paste::paste! {
#[doc = "Yields NA on overflow if NAs present."]
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "use std::iter::Sum;"]
#[doc = "test! {"]
#[doc = " let x = (0..100).map(|x| " $type "::default());"]
#[doc = " assert_eq!(<" $type " as Sum>::sum(x), <" $type ">::default());"]
#[doc = "}"]
#[doc = "```"]
fn sum<I: Iterator<Item = $type>>(iter: I) -> $type {
iter.fold($type::default(), |a, b| a + b)
}
}
}
impl<'a> std::iter::Sum<&'a $type> for $type {
paste::paste! {
#[doc = "Yields NA on overflow if NAs present."]
#[doc = "```"]
#[doc = "use extendr_api::prelude::*;"]
#[doc = "use std::iter::Sum;"]
#[doc = "test! {"]
#[doc = " let z =" $type "::default();"]
#[doc = " let x = (0..100).map(|_| &z);"]
#[doc = " assert_eq!(<" $type " as Sum<& " $type ">>::sum(x), <" $type ">::default());"]
#[doc = "}"]
#[doc = "```"]
fn sum<I: Iterator<Item = &'a $type>>(iter: I) -> $type {
iter.fold($type::default(), |a, b| a + *b)
}
}
}
};
}
pub(in crate::scalar) use gen_binop;
pub(in crate::scalar) use gen_binopassign;
pub(in crate::scalar) use gen_from_primitive;
pub(in crate::scalar) use gen_partial_ord;
pub(in crate::scalar) use gen_sum_iter;
pub(in crate::scalar) use gen_trait_impl;
pub(in crate::scalar) use gen_unop;