use std::cmp::Ordering;
use std::fmt::{self, Debug, Display};
use std::hash::{Hash, Hasher};
use std::ops::{Add, Div, Mul, Sub};
#[derive(Clone, Copy)]
pub enum NA<T> {
Value(T),
NA,
}
impl<T> NA<T> {
pub fn is_na(&self) -> bool {
match self {
NA::Value(_) => false,
NA::NA => true,
}
}
pub fn is_value(&self) -> bool {
!self.is_na()
}
pub fn value(&self) -> Option<&T> {
match self {
NA::Value(v) => Some(v),
NA::NA => None,
}
}
pub fn value_or<'a>(&'a self, default: &'a T) -> &'a T {
match self {
NA::Value(v) => v,
NA::NA => default,
}
}
pub fn map<U, F>(&self, f: F) -> NA<U>
where
F: FnOnce(&T) -> U,
{
match self {
NA::Value(v) => NA::Value(f(v)),
NA::NA => NA::NA,
}
}
}
impl<T> From<T> for NA<T> {
fn from(value: T) -> Self {
NA::Value(value)
}
}
impl<T> From<Option<T>> for NA<T> {
fn from(opt: Option<T>) -> Self {
match opt {
Some(v) => NA::Value(v),
None => NA::NA,
}
}
}
impl<T> From<NA<T>> for Option<T> {
fn from(na: NA<T>) -> Self {
match na {
NA::Value(v) => Some(v),
NA::NA => None,
}
}
}
impl<T: Debug> Debug for NA<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NA::Value(v) => write!(f, "{:?}", v),
NA::NA => write!(f, "NA"),
}
}
}
impl<T: Display> Display for NA<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NA::Value(v) => write!(f, "{}", v),
NA::NA => write!(f, "NA"),
}
}
}
impl<T: PartialEq> PartialEq for NA<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(NA::Value(a), NA::Value(b)) => a == b,
(NA::NA, NA::NA) => true,
_ => false,
}
}
}
impl<T: Eq> Eq for NA<T> {}
impl<T: PartialOrd> PartialOrd for NA<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(NA::Value(a), NA::Value(b)) => a.partial_cmp(b),
(NA::NA, NA::NA) => Some(Ordering::Equal),
(NA::NA, _) => Some(Ordering::Less), (_, NA::NA) => Some(Ordering::Greater),
}
}
}
impl<T: Ord> Ord for NA<T> {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(NA::Value(a), NA::Value(b)) => a.cmp(b),
(NA::NA, NA::NA) => Ordering::Equal,
(NA::NA, _) => Ordering::Less,
(_, NA::NA) => Ordering::Greater,
}
}
}
impl<T: Hash> Hash for NA<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
NA::Value(v) => {
0.hash(state); v.hash(state);
}
NA::NA => {
1.hash(state); }
}
}
}
impl<T: Add<Output = T>> Add for NA<T> {
type Output = NA<T>;
fn add(self, other: Self) -> Self::Output {
match (self, other) {
(NA::Value(a), NA::Value(b)) => NA::Value(a + b),
_ => NA::NA, }
}
}
impl<T: Sub<Output = T>> Sub for NA<T> {
type Output = NA<T>;
fn sub(self, other: Self) -> Self::Output {
match (self, other) {
(NA::Value(a), NA::Value(b)) => NA::Value(a - b),
_ => NA::NA,
}
}
}
impl<T: Mul<Output = T>> Mul for NA<T> {
type Output = NA<T>;
fn mul(self, other: Self) -> Self::Output {
match (self, other) {
(NA::Value(a), NA::Value(b)) => NA::Value(a * b),
_ => NA::NA,
}
}
}
impl<T: Div<Output = T> + std::cmp::PartialEq + NumericCast> Div for NA<T> {
type Output = NA<T>;
fn div(self, other: Self) -> Self::Output {
match (self, other) {
(NA::Value(_), NA::Value(b)) if b == T::from(0) => NA::NA, (NA::Value(a), NA::Value(b)) => NA::Value(a / b),
_ => NA::NA,
}
}
}
trait NumericCast {
fn from(val: i32) -> Self;
}
macro_rules! impl_numeric_cast {
($($t:ty),*) => {
$(
impl NumericCast for $t {
fn from(val: i32) -> Self {
val as $t
}
}
)*
};
}
impl_numeric_cast!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);