diff --git a/README.md b/README.md index a50f998..c0b8919 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ which will generate the corresponding methods for you. - `AddGetterMut` gives you access to the `#[get_mut]` tag which will generate a function which returns a mutable reference to the field(s) the tag is on. - `AddGetterVal` gives you access to the `#[get_val]` tag which will generate a function which returns a copy of the fields data (only available since version 1.1.0). - `AddSetter` gives you access to the `#[set]` tag which will generate a function that you can use to set the value of the field. + - `AddWith` gives you access to the `#[with]` tag which will generate a function that you can use to set the value of the field consuming self and the value. - Add the appropriate meta tags to each field to generate the methods, or since version 1.0.0, you can put the tags onto the struct itself which will generate the corresponding methods on every field of the struct (see example at the bottom of this file). @@ -29,6 +30,7 @@ which will generate the corresponding methods for you. | AddGetterVal | #[get_val] | `pub fn {field name}(&self) -> {field data type}` | | AddGetterMut | #[get_mut] | `pub fn get_{field name}_mut(&mut self) -> &mut {field data type}` | | AddSetter | #[set] | `pub fn set_{field name}(&mut self, v: {field data type})` | +| AddWith | #[with] | `pub fn with_{field name}(mut self, v: {field data type}) -> Self` | Note that all generated functions are public methods. @@ -38,7 +40,7 @@ Note that all generated functions are public methods. //stuff here } - #[derive(AddGetter, AddGetterVal, AddGetterMut, AddSetter)] + #[derive(AddGetter, AddGetterVal, AddGetterMut, AddSetter, AddWith)] struct RaceHorse { #[get] name: String, @@ -46,6 +48,7 @@ Note that all generated functions are public methods. #[get] #[get_val] #[set] + #[with] speed: i16, #[get] @@ -71,6 +74,11 @@ With this code, these methods would be generared for you... self.speed = v; } + pub fn with_speed(mut self, v: i16) -> Self { + self.speed = v; + self + } + pub fn get_rider(&self) -> &HorseRider { &self.rider } @@ -84,9 +92,10 @@ With this code, these methods would be generared for you... Add a getter and a setter for every field by adding the `#[get]` and `#[set]` tags to the struct definition: - #[derive(AddGetter, AddSetter)] + #[derive(AddGetter, AddSetter, AddWith)] #[get] #[set] + #[with] struct Dragon { name: String, age: u64, @@ -98,13 +107,16 @@ which has the same effect as doing the following: struct Dragon { #[get] #[set] + #[with] name: String, #[get] #[set] + #[with] age: u64, #[get] #[set] + #[with] weight: u32 - } \ No newline at end of file + } diff --git a/src/lib.rs b/src/lib.rs index 2cab928..a020261 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ enum GetterType { enum GsType { Getter(GetterType), + With, Setter, } @@ -25,6 +26,7 @@ impl GsType { match self { Self::Getter(ref gt) => gt, Self::Setter => panic!("error occurred while creating getters and setters"), + Self::With => panic!("error occurred while creating getters and setters"), } } } @@ -54,7 +56,8 @@ impl<'a> Gs<'a> { GetterType::Val => "get_val", } }, - GsType::Setter => "set" + GsType::Setter => "set", + GsType::With => "with", }; // rust does actually try the conditions in order - the complier doesn't put the least expensive first, // so check the boolean variable before calling the expensive function, for optimisation. @@ -63,6 +66,7 @@ impl<'a> Gs<'a> { match self.ty { GsType::Getter(_) => Some(self.gen_getter(f)), GsType::Setter => Some(self.gen_setter(f)), + GsType::With => Some(self.gen_with(f)), } }else{ None @@ -86,8 +90,22 @@ impl<'a> Gs<'a> { let ty = field.ty.clone(); quote! { #[inline(always)] - pub fn #fn_name(&mut self, v: #ty) { + pub fn #fn_name(&mut self, v: #ty) -> &Self { self.#field_name = v; + self + } + } + } + + fn gen_with(&self, field: &Field) -> TokenStream2 { + let field_name = field.clone().ident.unwrap(); + let fn_name = Ident::new(&format!("with_{}", field_name), Span::call_site()); + let ty = field.ty.clone(); + quote! { + #[inline(always)] + pub fn #fn_name(mut self, v: #ty) -> Self { + self.#field_name = v; + self } } } @@ -169,6 +187,17 @@ pub fn add_setter(input: TokenStream) -> TokenStream { }; gs_builder.gen(&ast).into() } +#[proc_macro_derive(AddWith, attributes(with))] +pub fn add_with(input: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(input).unwrap(); + let gs_builder = Gs { + ty: GsType::With, + prefix: "with_", + suffix: "", + apply_to_all: has_tag(ast.attrs.iter(), "with") + }; + gs_builder.gen(&ast).into() +} /// Pass in an Iterator and a tag to search for in the meta data, and it will return weather the tag was found or not fn has_tag<'a, T: Iterator>(mut attribs: T, tag_name: &str) -> bool { @@ -182,4 +211,4 @@ fn has_tag<'a, T: Iterator>(mut attribs: T, tag_name: &str } }) .is_some() -} \ No newline at end of file +} diff --git a/tests/tests.rs b/tests/tests.rs index 0809b33..b77964b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,12 +1,13 @@ #[macro_use] extern crate add_getters_setters; -#[derive(AddGetter, AddGetterVal, AddGetterMut, AddSetter)] +#[derive(AddWith, AddGetter, AddGetterVal, AddGetterMut, AddSetter)] struct Ts { jaf: u8, #[set] #[get_val] + #[with] field_1: u8, #[get] @@ -44,6 +45,13 @@ fn test_add_setter() { assert_eq!(a.field_1, 14); } +#[test] +fn test_add_with() { + let a = Ts {jaf: 4, field_1: 0, field_2: String::from("hello")} + .with_field_1(42); + assert_eq!(a.field_1, 42); +} + #[test] #[should_panic] fn test_add_setter_should_panic() { @@ -102,10 +110,11 @@ enum DragonClassifications { LuckDragon, } -#[derive(AddGetter, AddGetterMut, AddSetter)] +#[derive(AddWith, AddGetter, AddGetterMut, AddSetter)] #[get] #[get_mut] #[set] +#[with] struct Dragon { name: String, age: u64, // 18446744073709551615 year old dragons cos why not @@ -143,6 +152,15 @@ fn set_dragon_type() { falkor.set_ty(DragonClassifications::LuckDragon); assert_eq!(*falkor.get_ty(), DragonClassifications::LuckDragon); } +#[test] +fn with_dragon_type() { + let falkor = Dragon { + name: "Falkor".to_owned(), + age: 0xffffffffffffffff, + ty: DragonClassifications::BlackDragon } + .with_ty(DragonClassifications::LuckDragon); + assert_eq!(*falkor.get_ty(), DragonClassifications::LuckDragon); +} // *************************** // * if statement benchmarks * @@ -196,4 +214,4 @@ fn set_dragon_type() { // ) // }); // } -// } \ No newline at end of file +// }