From 51284070705b5f71bb413c4eac939a4e5d7da45a Mon Sep 17 00:00:00 2001 From: Mikhael Sokolov Date: Tue, 15 Aug 2023 11:34:05 +0400 Subject: [PATCH] Introduce @Value implementation + avoid imports in @EqualsAndHashCode implementation --- Cargo.toml | 2 +- README.md | 7 +-- src/equals_and_hash_code.rs | 6 +-- src/lib.rs | 13 ++++++ tests/tests.rs | 87 +++++++++++++++++++++++++++---------- 5 files changed, 83 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index de5b83d..f3946b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lombok" -version = "0.3.4" +version = "0.4.0" description = "Lombok port for Rust" authors = ["sokomishalov "] edition = "2021" diff --git a/README.md b/README.md index 7dd6197..020536e 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ Just because I can! -This crate is not actually the must-have one for development in Rust (unlike Java world), but if you find it useful - it -would be great. Anyway - boilerplate sucks, so get some proc macros stuff for decrease it. +This crate is not actually a must-have one for development in Rust (unlike Java world), but if you find it useful - it +would be great. Anyway - boilerplate sucks, so get some proc macros stuff to decrease it. Any feedback is appreciated. @@ -22,6 +22,7 @@ Any feedback is appreciated. - [x] `@EqualsAndHashCode` - `#[derive(EqualsAndHashCode)]` - [x] `@ToString` - `#[derive(ToString)]` - [x] `@Data` - `#[derive(Data)]` +- [x] `@Value` - `#[derive(Value)]` - [x] `@NoArgsConstructor` - `#[derive(NoArgsConstructor)]` - [x] `@AllArgsConstructor` - `#[derive(AllArgsConstructor)]` - [x] `@Builder` - `#[derive(Builder)]` @@ -32,7 +33,7 @@ Update `Cargo.toml` ```toml [dependencies] -lombok = "0.3" +lombok = "0.4" ``` Source code usage examples you can see [in tests](./tests/tests.rs). diff --git a/src/equals_and_hash_code.rs b/src/equals_and_hash_code.rs index efb89e3..b8e9b42 100644 --- a/src/equals_and_hash_code.rs +++ b/src/equals_and_hash_code.rs @@ -59,8 +59,6 @@ pub(crate) fn equals_and_hash_code(input: TokenStream) -> TokenStream { }); TokenStream::from(quote! { - use std::hash::{Hash, Hasher}; - impl #impl_generics #name #ty_generics #where_clause { fn equals(&self, other: &#name #ty_generics) -> bool { self.eq(&other) @@ -68,8 +66,8 @@ pub(crate) fn equals_and_hash_code(input: TokenStream) -> TokenStream { fn hash_code(&self) -> u64 { let mut hasher = ::std::collections::hash_map::DefaultHasher::new(); - &self.hash(&mut hasher); - hasher.finish() + ::std::hash::Hash::hash(&self, &mut hasher); + ::std::hash::Hasher::finish(&hasher) } } diff --git a/src/lib.rs b/src/lib.rs index 8c5c928..f27393a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,3 +77,16 @@ pub fn derive_data(input: TokenStream) -> TokenStream { .into_iter(), ) } + +#[proc_macro_derive(Value)] +pub fn derive_value(input: TokenStream) -> TokenStream { + TokenStream::from_iter( + vec![ + getter(input.clone()), + all_args_constructor(input.clone()), + equals_and_hash_code(input.clone()), + to_string(input), + ] + .into_iter(), + ) +} diff --git a/tests/tests.rs b/tests/tests.rs index 0b618f5..9d05d40 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,6 +1,6 @@ use lombok::{ - AllArgsConstructor, Builder, EqualsAndHashCode, Getter, GetterMut, NoArgsConstructor, Setter, - ToString, + AllArgsConstructor, Builder, Data, EqualsAndHashCode, Getter, GetterMut, NoArgsConstructor, + Setter, ToString, Value, }; #[derive( @@ -14,21 +14,33 @@ use lombok::{ ToString, Clone, )] -pub struct TestNamedStructure<'a> { +pub struct TestFullFeaturedStructure<'a> { age: u8, nick: &'a str, languages: Vec, hobby: Box<&'a str>, } +#[derive(Data)] +struct TestDataStructure<'a> { + age: u8, + nick: &'a str, +} + +#[derive(Value)] +struct TestValueStructure<'a> { + age: u8, + nick: &'a str, +} + #[cfg(test)] #[allow(dead_code)] mod tests { - use crate::TestNamedStructure; + use crate::{TestDataStructure, TestFullFeaturedStructure, TestValueStructure}; #[test] fn test_getter() { - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); assert_eq!(&default.age, default.get_age()); assert_eq!(default.nick, default.get_nick()); @@ -38,13 +50,13 @@ mod tests { #[test] fn test_getter_mut() { - let mut default = TestNamedStructure::default(); + let mut default = TestFullFeaturedStructure::default(); *default.get_age_mut() = 10; *default.get_nick_mut() = "another"; *default.get_languages_mut() = vec!["python".to_string()]; *default.get_hobby_mut() = Box::new("tennis"); - let copy = TestNamedStructure::default(); + let copy = TestFullFeaturedStructure::default(); assert_ne!(copy.age, default.age); assert_ne!(copy.nick, default.nick); @@ -54,14 +66,14 @@ mod tests { #[test] fn test_setter() { - let mut data = TestNamedStructure { + let mut data = TestFullFeaturedStructure { age: Default::default(), nick: Default::default(), languages: Default::default(), hobby: Default::default(), }; - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); let clone_default = default.clone(); data.set_age(clone_default.age); @@ -77,9 +89,9 @@ mod tests { #[test] fn test_no_args_constructor() { - let mut data = TestNamedStructure::new_default(); + let mut data = TestFullFeaturedStructure::new_default(); - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); let clone_default = default.clone(); data.set_age(clone_default.age); @@ -95,10 +107,10 @@ mod tests { #[test] fn test_all_args_constructor() { - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); let clone_default = default.clone(); - let data = TestNamedStructure::new( + let data = TestFullFeaturedStructure::new( clone_default.age, clone_default.nick, clone_default.languages, @@ -113,34 +125,36 @@ mod tests { #[test] fn test_equals_and_hashcode() { - let default = TestNamedStructure::default(); - let mut clone_default = default.clone(); + let default = TestFullFeaturedStructure::default(); + let mut adjusted_default = default.clone(); - assert!(&default.equals(&clone_default)); - assert_eq!(&default.hash_code(), &clone_default.hash_code()); + assert!(&default.equals(&adjusted_default)); + assert_eq!(&default.hash_code(), &adjusted_default.hash_code()); - clone_default.age = 10; + adjusted_default.age = 10; - assert!(!&default.equals(&clone_default)); - assert_ne!(&default.hash_code(), &clone_default.hash_code()); + assert!(!&default.equals(&adjusted_default)); + assert_ne!(&default.hash_code(), &adjusted_default.hash_code()); } #[test] fn test_to_string() { - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); let string = default.to_string(); assert_eq!( - String::from("TestNamedStructure { age: 0, nick: \"\", languages: [], hobby: \"\" }"), + String::from( + "TestFullFeaturedStructure { age: 0, nick: \"\", languages: [], hobby: \"\" }" + ), string ) } #[test] fn test_builder() { - let default = TestNamedStructure::default(); + let default = TestFullFeaturedStructure::default(); let clone_default = default.clone(); - let data = TestNamedStructure::builder() + let data = TestFullFeaturedStructure::builder() .age(clone_default.age) .nick(clone_default.nick) .languages(clone_default.languages) @@ -149,4 +163,29 @@ mod tests { assert_eq!(&default, &data); } + + #[test] + fn test_data() { + let mut data = TestDataStructure::new(28, "sokomishalov"); + + data.set_age(29); + + assert_eq!(29, *data.get_age()); + assert_eq!("sokomishalov", data.get_nick()); + assert_eq!( + String::from("TestDataStructure { age: 29, nick: \"sokomishalov\" }"), + data.to_string() + ) + } + + #[test] + fn test_value() { + let value = TestValueStructure::new(28, "sokomishalov"); + + assert_eq!(28, *value.get_age()); + assert_eq!( + String::from("TestValueStructure { age: 28, nick: \"sokomishalov\" }"), + value.to_string() + ) + } }