diff --git a/src/lib.rs b/src/lib.rs index 26b9c77..62e8f4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -952,3 +952,61 @@ pub fn float(input: TokenStream) -> TokenStream { import.wrap("Float", &name, impl_).into() } + +/// Derives [`num_traits::Signed`][signed] for newtypes. The inner type must already implement +/// `Signed`. +/// +/// [signed]: https://docs.rs/num-traits/0.2/num_traits/sign/trait.Signed.html +#[proc_macro_derive(Signed, attributes(num_traits))] +pub fn signed(input: TokenStream) -> TokenStream { + let ast = parse!(input as syn::DeriveInput); + let name = &ast.ident; + let inner_ty = newtype_inner(&ast.data).expect(NEWTYPE_ONLY); + + let import = NumTraits::new(&ast); + + let impl_ = quote! { + impl #import::Signed for #name { + #[inline] + fn abs(&self) -> Self { + #name(<#inner_ty as #import::Signed>::abs(&self.0)) + } + #[inline] + fn abs_sub(&self, other: &Self) -> Self { + #name(<#inner_ty as #import::Signed>::abs_sub(&self.0, &other.0)) + } + #[inline] + fn signum(&self) -> Self { + #name(<#inner_ty as #import::Signed>::signum(&self.0)) + } + #[inline] + fn is_positive(&self) -> bool { + <#inner_ty as #import::Signed>::is_positive(&self.0) + } + #[inline] + fn is_negative(&self) -> bool { + <#inner_ty as #import::Signed>::is_negative(&self.0) + } + } + }; + + import.wrap("Signed", &name, impl_).into() +} + +/// Derives [`num_traits::Unsigned`][unsigned]. The inner type must already implement +/// `Unsigned`. +/// +/// [unsigned]: https://docs.rs/num/latest/num/traits/trait.Unsigned.html +#[proc_macro_derive(Unsigned, attributes(num_traits))] +pub fn unsigned(input: TokenStream) -> TokenStream { + let ast = parse!(input as syn::DeriveInput); + let name = &ast.ident; + + let import = NumTraits::new(&ast); + + let impl_ = quote! { + impl #import::Unsigned for #name {} + }; + + import.wrap("Unsigned", &name, impl_).into() +} diff --git a/tests/newtype.rs b/tests/newtype.rs index 6eafd09..71b06b3 100644 --- a/tests/newtype.rs +++ b/tests/newtype.rs @@ -2,9 +2,22 @@ extern crate num as num_renamed; #[macro_use] extern crate num_derive; -use crate::num_renamed::{Float, FromPrimitive, Num, NumCast, One, ToPrimitive, Zero}; +use crate::num_renamed::{ + Float, FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Unsigned, Zero, +}; use std::ops::Neg; +#[derive(PartialEq, Zero, One, NumOps, Num, Unsigned)] +struct MyNum(u32); + +#[test] +fn test_derive_unsigned_works() { + fn do_nothing_on_unsigned(_input: impl Unsigned) {} + + let x = MyNum(42); + do_nothing_on_unsigned(x); +} + #[derive( Debug, Clone, @@ -19,6 +32,7 @@ use std::ops::Neg; Zero, Num, Float, + Signed, )] struct MyFloat(f64); @@ -87,3 +101,8 @@ fn test_num() { fn test_float() { assert_eq!(MyFloat(4.0).log(MyFloat(2.0)), MyFloat(2.0)); } + +#[test] +fn test_signed() { + assert!(MyFloat(-2.0).is_negative()) +}