Skip to content

Commit

Permalink
Add addable and scalable attributes (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
yunjhongwu authored Jun 17, 2024
1 parent b600811 commit 9b464bc
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 116 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ println!("{}", timestamp); // Timestamp(1701620628123456789)
- **Attributes:**
- Adding the following attributes to `#[strong_type(...)]` allows for additional features:
- `auto_operators`: Automatically implements relevant arithmetic (for numeric types) or logical (for boolean types) operators.
- `addable`: Automatically implements the `Add`, `Sub`, and other relevant traits. The attribute is a strict subset of `auto_operators`.
- `scalable`: Automatically implements the `Mul`, `Div`, `Rem`, and other relevant traits between a strong typed struct and its primitive type. Note that the attribute is not a subset of `auto_operators`.
- `custom_display`: Allows users to manually implement the `Display` trait, providing an alternative to the default display format.
- `conversion`: Automatically implements `From` and `Into` traits for the underlying type. This is optional since conversion may make strong types less distinct.
- `underlying`: Specifies the underlying primitive type for nested strong types.
Expand Down Expand Up @@ -118,6 +120,14 @@ assert_eq!(z.value(), 0);
assert!(x < y);
assert!(y >= x);
assert_eq!(x + y, Nanosecond(5));

#[derive(StrongType)]
#[strong_type(scalable)]
struct Millisecond(u32);

let x = Millisecond::new(2);

assert_eq!(x * 3, Millisecond(6));
```

#### Named bool type with logical operations:
Expand Down Expand Up @@ -178,4 +188,5 @@ struct Coin(Cash);

### Caveats:
- When using `#[derive(StrongType)]`, the traits `Eq` and `PartialEq` are implemented with `impl`.
As a result, `StructuralEq` and `StructuralPartialEq` remain unimplemented, preventing pattern matching with strong-typed primitives.
As a result, `StructuralEq` and `StructuralPartialEq` remain unimplemented, preventing pattern matching with strong-typed primitives.
- `#[strong_type(scalable)]` does not work for nested strong types.
117 changes: 117 additions & 0 deletions strong-type-derive/src/detail/addable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use proc_macro2::TokenStream;
use quote::quote;

pub(crate) fn implement_addable(ident: &syn::Ident) -> TokenStream {
let add_ops = implement_add(ident);
let sub_ops = implement_sub(ident);

quote! {
#add_ops
#sub_ops
}
}

fn implement_add(name: &syn::Ident) -> TokenStream {
quote! {
impl std::ops::Add<Self> for #name {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self::new(self.value() + rhs.value())
}
}

impl std::ops::Add<&Self> for #name {
type Output = <Self as std::ops::Add<Self>>::Output;

fn add(self, rhs: &Self) -> Self::Output {
Self::new(self.value() + rhs.value())
}
}

impl<'a> std::ops::Add<#name> for &'a #name {
type Output = #name;

fn add(self, rhs: #name) -> Self::Output {
#name::new(self.value() + rhs.value())
}
}

impl<'a> std::ops::Add<&#name> for &'a #name {
type Output = #name;

fn add(self, rhs: &#name) -> Self::Output {
#name::new(self.value() + rhs.value())
}
}

impl std::ops::AddAssign<Self> for #name {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.value()
}
}

impl std::ops::AddAssign<&Self> for #name {
fn add_assign(&mut self, rhs: &Self) {
self.0 += rhs.value()
}
}

impl std::iter::Sum<Self> for #name {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, std::ops::Add::add)
}
}

impl<'a> std::iter::Sum<&'a Self> for #name {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, std::ops::Add::add)
}
}
}
}

fn implement_sub(name: &syn::Ident) -> TokenStream {
quote! {
impl std::ops::Sub<Self> for #name {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.value() - rhs.value())
}
}

impl std::ops::Sub<&#name> for #name {
type Output = Self;
fn sub(self, rhs: &Self) -> Self::Output {
Self::new(self.value() - rhs.value())
}
}

impl<'a> std::ops::Sub<#name> for &'a #name {
type Output = #name;
fn sub(self, rhs: #name) -> Self::Output {
#name::new(self.value() - rhs.value())
}
}

impl<'a> std::ops::Sub<&#name> for &'a #name {
type Output = #name;
fn sub(self, rhs: &#name) -> Self::Output {
#name::new(self.value() - rhs.value())
}
}


impl std::ops::SubAssign<Self> for #name {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.value()
}
}

impl std::ops::SubAssign<&Self> for #name {
fn sub_assign(&mut self, rhs: &Self) {
self.0 -= rhs.value()
}
}
}
}
112 changes: 3 additions & 109 deletions strong-type-derive/src/detail/arithmetic.rs
Original file line number Diff line number Diff line change
@@ -1,111 +1,7 @@
use crate::detail::addable::implement_addable;
use proc_macro2::TokenStream;
use quote::quote;

pub(crate) fn implement_add(name: &syn::Ident) -> TokenStream {
quote! {
impl std::ops::Add<Self> for #name {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self::new(self.value() + rhs.value())
}
}

impl std::ops::Add<&Self> for #name {
type Output = <Self as std::ops::Add<Self>>::Output;

fn add(self, rhs: &Self) -> Self::Output {
Self::new(self.value() + rhs.value())
}
}

impl<'a> std::ops::Add<#name> for &'a #name {
type Output = #name;

fn add(self, rhs: #name) -> Self::Output {
#name::new(self.value() + rhs.value())
}
}

impl<'a> std::ops::Add<&#name> for &'a #name {
type Output = #name;

fn add(self, rhs: &#name) -> Self::Output {
#name::new(self.value() + rhs.value())
}
}

impl std::ops::AddAssign<Self> for #name {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.value()
}
}

impl std::ops::AddAssign<&Self> for #name {
fn add_assign(&mut self, rhs: &Self) {
self.0 += rhs.value()
}
}

impl std::iter::Sum<Self> for #name {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, std::ops::Add::add)
}
}

impl<'a> std::iter::Sum<&'a Self> for #name {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, std::ops::Add::add)
}
}
}
}

pub(crate) fn implement_sub(name: &syn::Ident) -> TokenStream {
quote! {
impl std::ops::Sub<Self> for #name {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.value() - rhs.value())
}
}

impl std::ops::Sub<&#name> for #name {
type Output = Self;
fn sub(self, rhs: &Self) -> Self::Output {
Self::new(self.value() - rhs.value())
}
}

impl<'a> std::ops::Sub<#name> for &'a #name {
type Output = #name;
fn sub(self, rhs: #name) -> Self::Output {
#name::new(self.value() - rhs.value())
}
}

impl<'a> std::ops::Sub<&#name> for &'a #name {
type Output = #name;
fn sub(self, rhs: &#name) -> Self::Output {
#name::new(self.value() - rhs.value())
}
}


impl std::ops::SubAssign<Self> for #name {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.value()
}
}

impl std::ops::SubAssign<&Self> for #name {
fn sub_assign(&mut self, rhs: &Self) {
self.0 -= rhs.value()
}
}
}
}

pub(crate) fn implement_mul(name: &syn::Ident) -> TokenStream {
quote! {
impl std::ops::Mul<Self> for #name {
Expand Down Expand Up @@ -251,15 +147,13 @@ pub(crate) fn implement_rem(name: &syn::Ident) -> TokenStream {
}

pub(crate) fn implement_arithmetic(name: &syn::Ident) -> TokenStream {
let add = implement_add(name);
let sub = implement_sub(name);
let addable = implement_addable(name);
let mul = implement_mul(name);
let div = implement_div(name);
let rem = implement_rem(name);

quote! {
#add
#sub
#addable
#mul
#div
#rem
Expand Down
4 changes: 4 additions & 0 deletions strong-type-derive/src/detail/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod addable;
mod arithmetic;
mod basic;
mod basic_primitive;
Expand All @@ -10,9 +11,11 @@ mod display;
mod hash;
mod nan;
mod negate;
mod scalable;
mod underlying_type_utils;
mod utils;

pub(crate) use addable::implement_addable;
pub(crate) use arithmetic::implement_arithmetic;
pub(crate) use basic::implement_basic;
pub(crate) use basic_primitive::{
Expand All @@ -32,5 +35,6 @@ pub(crate) use display::implement_display;
pub(crate) use hash::implement_hash;
pub(crate) use nan::implement_nan;
pub(crate) use negate::implement_negate;
pub(crate) use scalable::implement_scalable;
pub(crate) use underlying_type_utils::{get_type, TypeInfo, UnderlyingType, ValueTypeGroup};
pub(crate) use utils::{get_attributes, validate_struct, StrongTypeAttributes};
Loading

0 comments on commit 9b464bc

Please sign in to comment.