Skip to content

Commit

Permalink
Implement bool logic operations and move them to StrongNumericType (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
yunjhongwu authored Dec 6, 2023
1 parent 056b160 commit f82e8be
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 27 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ assert_eq!(x.type_id(), y.type_id()); // Same type: Second
assert_ne!(y.type_id(), z.type_id()); // Different types: Second versus Minute
```

#### Named type with arithmetic operations:
#### Named integer type with arithmetic operations:

```rust
use strong_type::StrongNumericType;
Expand All @@ -75,6 +75,23 @@ assert!(y >= x);
assert_eq!(x + y, Second(5));
```

#### Named bool type with logical operations:

```rust
use strong_type::StrongNumericType;

#[derive(StrongNumericType)]
struct IsTrue(bool);

let x = IsTrue::new(true);
let y = IsTrue::new(false);

assert_eq!(x & y, IsTrue::new(false));
assert_eq!(x | y, IsTrue::new(true));
assert_eq!(x ^ y_ref, IsTrue::new(true));
assert_eq!(!x, IsTrue::new(false));
```

#### Named type with `custom_display`:

```rust
Expand Down
154 changes: 154 additions & 0 deletions src/detail/bool_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use proc_macro2::TokenStream;
use quote::quote;

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

fn not(self) -> Self::Output {
Self::new(!self.value())
}
}

impl std::ops::Not for &#name {
type Output = #name;

fn not(self) -> Self::Output {
#name::new(!self.value())
}
}

impl std::ops::BitAnd<Self> for #name {
type Output = Self;

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

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

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

impl std::ops::BitAnd<#name> for &#name {
type Output = #name;

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

impl std::ops::BitAnd<&#name> for &#name {
type Output = #name;

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

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

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

impl std::ops::BitOr<Self> for #name {
type Output = Self;

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

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

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

impl std::ops::BitOr<#name> for &#name {
type Output = #name;

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

impl std::ops::BitOr<&#name> for &#name {
type Output = #name;

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

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

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

impl std::ops::BitXor<Self> for #name {
type Output = Self;

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

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

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

impl std::ops::BitXor<#name> for &#name {
type Output = #name;

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

impl std::ops::BitXor<&#name> for &#name {
type Output = #name;

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

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

impl std::ops::BitXorAssign<&Self> for #name {
fn bitxor_assign(&mut self, rhs: &Self) {
self.0 ^= rhs.value()
}
}
}
}
4 changes: 2 additions & 2 deletions src/detail/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ mod arithmetic;
mod basic;
mod basic_primitive;
mod basic_string;
mod bool_ops;
mod display;
mod hash;
mod min_max;
mod nan;
mod negate;
mod not;
mod underlying_type;

pub(crate) use arithmetic::implement_arithmetic;
pub(crate) use basic::implement_basic;
pub(crate) use basic_primitive::implement_basic_primitive;
pub(crate) use basic_string::implement_basic_string;
pub(crate) use bool_ops::implement_bool_ops;
pub(crate) use display::{custom_display, implement_display};
pub(crate) use hash::implement_hash;
pub(crate) use min_max::implement_min_max;
pub(crate) use nan::implement_nan;
pub(crate) use negate::implement_negate;
pub(crate) use not::implement_not;
pub(crate) use underlying_type::{get_type_group, get_type_ident, UnderlyingTypeGroup};
14 changes: 0 additions & 14 deletions src/detail/not.rs

This file was deleted.

10 changes: 6 additions & 4 deletions src/strong_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::detail::{
custom_display, get_type_group, get_type_ident, implement_arithmetic, implement_basic,
implement_basic_primitive, implement_basic_string, implement_display, implement_hash,
implement_min_max, implement_nan, implement_negate, implement_not, UnderlyingTypeGroup,
implement_basic_primitive, implement_basic_string, implement_bool_ops, implement_display,
implement_hash, implement_min_max, implement_nan, implement_negate, UnderlyingTypeGroup,
};
use proc_macro2::TokenStream;
use quote::quote;
Expand Down Expand Up @@ -31,7 +31,6 @@ pub(super) fn expand_strong_type(input: DeriveInput, impl_arithmetic: bool) -> T
}
UnderlyingTypeGroup::Bool => {
ast.extend(implement_basic_primitive(name, value_type));
ast.extend(implement_not(name));
ast.extend(implement_hash(name));
}
UnderlyingTypeGroup::Char => {
Expand All @@ -53,7 +52,10 @@ pub(super) fn expand_strong_type(input: DeriveInput, impl_arithmetic: bool) -> T
UnderlyingTypeGroup::UInt => {
ast.extend(implement_arithmetic(name));
}
_ => panic!("Non-arithmetic type {value_type}"),
UnderlyingTypeGroup::Bool => {
ast.extend(implement_bool_ops(name));
}
_ => panic!("Non-numeric type: {value_type}"),
}
}

Expand Down
30 changes: 24 additions & 6 deletions tests/strong_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,32 @@ mod tests {
}

#[test]
fn test_bool_negate() {
#[derive(StrongType)]
fn test_bool_ops() {
#[derive(StrongNumericType)]
struct IsTrue(bool);
let is_true = IsTrue(true);

assert!(is_true.value());
assert!(!(!is_true).value());
assert!((!!is_true).value());
let x = IsTrue::new(true);
let y = IsTrue::new(false);
let x_ref = &x;
let y_ref = &y;

assert_eq!(x & y, IsTrue::new(false));
assert_eq!(x | y, IsTrue::new(true));
assert_eq!(x ^ y, IsTrue::new(true));

assert_eq!(x_ref & y, IsTrue::new(false));
assert_eq!(x_ref | y, IsTrue::new(true));
assert_eq!(x_ref ^ y, IsTrue::new(true));

assert_eq!(x & y_ref, IsTrue::new(false));
assert_eq!(x | y_ref, IsTrue::new(true));
assert_eq!(x ^ y_ref, IsTrue::new(true));

assert_eq!(x_ref & y_ref, IsTrue::new(false));
assert_eq!(x_ref | y_ref, IsTrue::new(true));

assert_eq!(!x, IsTrue::new(false));
assert_eq!(!x_ref, IsTrue::new(false));
}

#[test]
Expand Down

0 comments on commit f82e8be

Please sign in to comment.