macro_rules! _nt_base {
($name:ident, $inner:ty) => {
#[repr(transparent)]
#[derive(Debug)]
struct $name($inner);
impl Into<$inner> for $name {
fn into(self) -> $inner {
self.0
}
}
};
}
macro_rules! _nt_clone_traits {
($name:ident) => {
impl Clone for $name {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
};
}
macro_rules! _nt_copy_traits {
($name:ident) => {
_nt_clone_traits!($name);
impl Copy for $name {}
};
}
macro_rules! _nt_partialeq_traits {
($name:ident) => {
impl PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
}
}
macro_rules! _nt_eq_traits {
($name:ident) => {
_nt_partialeq_traits!($name);
impl Eq for $name {}
}
}
macro_rules! _nt_partialord_traits {
($name:ident) => {
impl PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
}
}
macro_rules! _nt_ord_traits {
($name:ident) => {
_nt_partialord_traits!($name);
impl Ord for $name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
}
}
macro_rules! _nt_hash_traits {
($name:ident) => {
impl std::hash::Hash for $name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
}
}
macro_rules! _nt_addsub_traits {
($name:ident) => {
impl Default for $name {
fn default() -> Self {
Self(Default::default())
}
}
impl std::ops::Add<$name> for $name {
type Output = $name;
fn add(self, rhs: Self) -> Self::Output {
$name(self.0 + rhs.0)
}
}
impl std::ops::Add<&$name> for $name {
type Output = $name;
fn add(self, rhs: &$name) -> Self::Output {
$name(self.0 + rhs.0)
}
}
impl std::ops::Add<$name> for &$name {
type Output = $name;
fn add(self, rhs: $name) -> Self::Output {
$name(self.0 + rhs.0)
}
}
impl std::ops::Add<&$name> for &$name {
type Output = $name;
fn add(self, rhs: &$name) -> Self::Output {
$name(self.0 + rhs.0)
}
}
impl std::ops::Sub<$name> for $name {
type Output = $name;
fn sub(self, rhs: Self) -> Self::Output {
$name(self.0 - rhs.0)
}
}
impl std::ops::Sub<&$name> for $name {
type Output = $name;
fn sub(self, rhs: &$name) -> Self::Output {
$name(self.0 - rhs.0)
}
}
impl std::ops::Sub<$name> for &$name {
type Output = $name;
fn sub(self, rhs: $name) -> Self::Output {
$name(self.0 - rhs.0)
}
}
impl std::ops::Sub<&$name> for &$name {
type Output = $name;
fn sub(self, rhs: &$name) -> Self::Output {
$name(self.0 - rhs.0)
}
}
impl std::ops::AddAssign<$name> for $name {
fn add_assign(&mut self, rhs: $name) {
self.0 += rhs.0;
}
}
impl std::ops::AddAssign<&$name> for $name {
fn add_assign(&mut self, rhs: &$name) {
self.0 += rhs.0;
}
}
impl std::ops::SubAssign<$name> for $name {
fn sub_assign(&mut self, rhs: $name) {
self.0 -= rhs.0;
}
}
impl std::ops::SubAssign<&$name> for $name {
fn sub_assign(&mut self, rhs: &$name) {
self.0 -= rhs.0;
}
}
}
}
macro_rules! _nt_clone {
($name:ident, $inner:ty) => {
_nt_base!($name, $inner);
_nt_clone_traits!($name);
}
}
macro_rules! _nt_copy {
($name:ident, $inner:ty) => {
_nt_base!($name, $inner);
_nt_copy_traits!($name);
}
}
macro_rules! _nt_int {
($name:ident, $inner:ty) => {
_nt_copy!($name, $inner);
_nt_eq_traits!($name);
_nt_hash_traits!($name);
}
}
macro_rules! _nt_float {
($name:ident, $inner:ty) => {
_nt_copy!($name, $inner);
_nt_partialeq_traits!($name);
}
}
macro_rules! _nt_string {
($name:ident) => {
_nt_clone!($name, String);
_nt_eq_traits!($name);
_nt_hash_traits!($name);
};
}
macro_rules! _nt_int_ord {
($name:ident, $inner:ty) => {
_nt_int!($name, $inner);
_nt_ord_traits!($name);
}
}
macro_rules! _nt_float_ord {
($name:ident, $inner:ty) => {
_nt_float!($name, $inner);
_nt_partialord_traits!($name);
}
}
macro_rules! _nt_string_ord {
($name:ident) => {
_nt_string!($name);
_nt_ord_traits!($name);
};
}
macro_rules! _nt_int_unit {
($name:ident, $inner:ty) => {
_nt_int_ord!($name, $inner);
_nt_addsub_traits!($name);
}
}
macro_rules! _nt_float_unit {
($name:ident, $inner:ty) => {
_nt_float_ord!($name, $inner);
_nt_addsub_traits!($name);
}
}
macro_rules! _nt_from_traits {
($name:ident, $inner:ty) => {
impl From<$inner> for $name {
#[inline(always)]
fn from(value: $inner) -> Self {
$name(value)
}
}
};
}
macro_rules! _nt_fromstr_traits {
($name:ident, $inner:ty, $err:ty) => {
impl std::str::FromStr for $name {
type Err = $err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match <$inner>::from_str(s) {
Ok(v) => Ok($name(v)),
Err(e) => Err(e),
}
}
}
}
}
macro_rules! _nt_int_from {
($name:ident, $inner:ty) => {
_nt_from_traits!($name, $inner);
_nt_fromstr_traits!($name, $inner, std::num::ParseIntError);
}
}
macro_rules! _nt_float_from {
($name:ident, $inner:ty) => {
_nt_from_traits!($name, $inner);
_nt_fromstr_traits!($name, $inner, std::num::ParseFloatError);
}
}
#[macro_export]
macro_rules! newtype {
($name:ident, i8) => { _nt_int!($name, i8); };
($name:ident, u8) => { _nt_int!($name, u8); };
($name:ident, i16) => { _nt_int!($name, i16); };
($name:ident, u16) => { _nt_int!($name, u16); };
($name:ident, i32) => { _nt_int!($name, i32); };
($name:ident, u32) => { _nt_int!($name, u32); };
($name:ident, i64) => { _nt_int!($name, i64); };
($name:ident, u64) => { _nt_int!($name, u64); };
($name:ident, i128) => { _nt_int!($name, i128); };
($name:ident, u128) => { _nt_int!($name, u128); };
($name:ident, f32) => { _nt_float!($name, f32); };
($name:ident, f64) => { _nt_float!($name, f64); };
($name:ident, String) => { _nt_string!($name); };
($name:ident, $inner:ty) => { _nt_base!($name, $inner); };
}
#[macro_export]
macro_rules! newtype_ord {
($name:ident, i8) => { _nt_int_ord!($name, i8); };
($name:ident, u8) => { _nt_int_ord!($name, u8); };
($name:ident, i16) => { _nt_int_ord!($name, i16); };
($name:ident, u16) => { _nt_int_ord!($name, u16); };
($name:ident, i32) => { _nt_int_ord!($name, i32); };
($name:ident, u32) => { _nt_int_ord!($name, u32); };
($name:ident, i64) => { _nt_int_ord!($name, i64); };
($name:ident, u64) => { _nt_int_ord!($name, u64); };
($name:ident, i128) => { _nt_int_ord!($name, i128); };
($name:ident, u128) => { _nt_int_ord!($name, u128); };
($name:ident, f32) => { _nt_float_ord!($name, f32); };
($name:ident, f64) => { _nt_float_ord!($name, f64); };
($name:ident, String) => { _nt_string_ord!($name); };
}
#[macro_export]
macro_rules! newtype_unit {
($name:ident, i8) => { _nt_int_unit!($name, i8); };
($name:ident, u8) => { _nt_int_unit!($name, u8); };
($name:ident, i16) => { _nt_int_unit!($name, i16); };
($name:ident, u16) => { _nt_int_unit!($name, u16); };
($name:ident, i32) => { _nt_int_unit!($name, i32); };
($name:ident, u32) => { _nt_int_unit!($name, u32); };
($name:ident, i64) => { _nt_int_unit!($name, i64); };
($name:ident, u64) => { _nt_int_unit!($name, u64); };
($name:ident, i128) => { _nt_int_unit!($name, i128); };
($name:ident, u128) => { _nt_int_unit!($name, u128); };
($name:ident, f32) => { _nt_float_unit!($name, f32); };
($name:ident, f64) => { _nt_float_unit!($name, f64); };
}
#[macro_export]
macro_rules! newtype_from {
($name:ident, i8) => { _nt_int_from!($name, i8); };
($name:ident, u8) => { _nt_int_from!($name, u8); };
($name:ident, i16) => { _nt_int_from!($name, i16); };
($name:ident, u16) => { _nt_int_from!($name, u16); };
($name:ident, i32) => { _nt_int_from!($name, i32); };
($name:ident, u32) => { _nt_int_from!($name, u32); };
($name:ident, i64) => { _nt_int_from!($name, i64); };
($name:ident, u64) => { _nt_int_from!($name, u64); };
($name:ident, i128) => { _nt_int_from!($name, i128); };
($name:ident, u128) => { _nt_int_from!($name, u128); };
($name:ident, f32) => { _nt_float_from!($name, f32); };
($name:ident, f64) => { _nt_float_from!($name, f64); };
($name:ident, String) => {
impl From<String> for $name {
#[inline(always)]
fn from(value: String) -> Self {
$name(value)
}
}
impl From<&str> for $name {
#[inline(always)]
fn from(value: &str) -> Self {
$name(value.into())
}
}
impl std::str::FromStr for $name {
type Err = std::convert::Infallible;
#[inline(always)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok($name(String::from_str(s).unwrap()))
}
}
};
($name:ident, $inner:ty) => {
impl From<$inner> for $name {
#[inline(always)]
fn from(value: $inner) -> Self {
$name(value)
}
}
};
}
#[cfg(test)]
mod tests_newtype {
newtype!(UserId, u32);
newtype_from!(UserId, u32);
newtype!(Weight, f32);
newtype_from!(Weight, f32);
newtype!(StrId, String);
newtype_from!(StrId, String);
#[test]
fn test_integers_cmp() {
let v1 = UserId::from(42);
let v2 = UserId::from(1001);
let v3 = UserId::from(42);
assert!(v1 != v2);
assert!(v2 != v3);
assert!(v1 == v1);
assert!(v1 == v3);
}
#[test]
fn test_integers_copy() {
let v1 = UserId::from(42);
let v2 = UserId::from(1001);
#[allow(unused)]
let mut v3 = UserId::from(0);
v3 = v1;
assert_eq!(UserId(42), v3);
v3 = v2;
assert_eq!(UserId(1001), v3);
v3 = v1.clone();
assert_eq!(UserId(42), v3);
v3 = v2.clone();
assert_eq!(UserId(1001), v3);
}
#[test]
fn test_integers_into() {
let v1 = UserId::from(42);
assert_eq!(42_u32, v1.into());
}
#[test]
fn test_float_cmp() {
let v1 = Weight::from(42.0);
let v2 = Weight::from(1001.0);
let v3 = Weight::from(42.0);
assert!(v1 != v2);
assert!(v2 != v3);
assert!(v1 == v1);
assert!(v1 == v3);
}
#[test]
fn test_float_copy() {
let v1 = Weight::from(42.0);
let v2 = Weight::from(1001.0);
#[allow(unused)]
let mut v3 = Weight::from(0.0);
v3 = v1;
assert_eq!(Weight(42.0), v3);
v3 = v2;
assert_eq!(Weight(1001.0), v3);
v3 = v1.clone();
assert_eq!(Weight(42.0), v3);
v3 = v2.clone();
assert_eq!(Weight(1001.0), v3);
}
#[test]
fn test_float_into() {
let v1 = Weight::from(42.0);
assert_eq!(42_f32, v1.into());
}
#[test]
fn test_string_cmp() {
let v1 = StrId::from("42");
let v2 = StrId::from("1001");
let v3 = StrId::from("42");
assert!(v1 != v2);
assert!(v2 != v3);
assert!(v1 == v1);
assert!(v1 == v3);
}
#[test]
fn test_string_copy() {
let v1 = StrId::from("42");
let v2 = StrId::from("1001");
#[allow(unused)]
let mut v3 = StrId::from("0");
v3 = v1.clone();
assert_eq!(StrId("42".into()), v3);
v3 = v2.clone();
assert_eq!(StrId("1001".into()), v3);
}
#[test]
fn test_string_into() {
let v1 = StrId::from("42");
let s: String = v1.into();
assert_eq!("42", s);
}
}
#[cfg(test)]
mod tests_newtype_ord {
newtype_ord!(Weight, u32);
newtype_from!(Weight, u32);
newtype_ord!(Height, f32);
newtype_from!(Height, f32);
newtype_ord!(Name, String);
newtype_from!(Name, String);
#[test]
fn test_integers_cmp() {
let w1 = Weight::from(100);
let w2 = Weight::from(150);
assert!(w1 <= w2);
assert!(w1 < w2);
assert!(w2 >= w1);
assert!(w2 > w1);
}
#[test]
fn test_float_cmp() {
let h1 = Height::from(100.0);
let h2 = Height::from(150.0);
assert!(h1 <= h2);
assert!(h1 < h2);
assert!(h2 >= h1);
assert!(h2 > h1);
}
#[test]
fn test_string_cmp() {
let n1 = Name::from("Alan");
let n2 = Name::from("Julia");
assert!(n1 <= n2);
assert!(n1 < n2);
assert!(n2 >= n1);
assert!(n2 > n1);
}
}
#[cfg(test)]
mod tests_newtype_unit {
newtype_unit!(Units, i32);
newtype_from!(Units, i32);
#[test]
fn test_integers_add() {
let u1 = Units::from(100);
let u2 = Units::from(50);
let mut u3 = Units::from(200);
assert_eq!(Units(150), u1 + u2);
assert_eq!(Units(150), u2 + u1);
u3 += u2;
assert_eq!(Units(250), u3);
}
#[test]
fn test_integers_sub() {
let u1 = Units::from(100);
let u2 = Units::from(50);
let mut u3 = Units::from(200);
assert_eq!(Units(50), u1 - u2);
assert_eq!(Units(-50), u2 - u1);
u3 -= u2;
assert_eq!(Units(150), u3);
}
}
#[cfg(test)]
mod tests_newtype_from {
use std::str::FromStr;
newtype!(UserId, u32);
newtype_from!(UserId, u32);
newtype!(Weight, f32);
newtype_from!(Weight, f32);
newtype!(Name, String);
newtype_from!(Name, String);
#[test]
fn test_integer_fromstr() {
assert_eq!(Ok(UserId(42)), UserId::from_str("42"));
assert!(UserId::from_str("hello").is_err());
}
#[test]
fn test_float_fromstr() {
assert_eq!(Ok(Weight(42.0)), Weight::from_str("42.0"));
assert!(Weight::from_str("hello").is_err());
}
#[test]
fn test_string_fromstr() {
assert_eq!(Ok(Name("John".into())), Name::from_str("John"));
}
}