From 1d73c9ab14e4b4833ef7991af425506cb8a5bccc Mon Sep 17 00:00:00 2001 From: Alexander Shishenko Date: Thu, 31 Mar 2022 00:12:15 +0300 Subject: [PATCH] Add fluent setters, that can be used for chaining setters, like in modern Rust --- src/generate.rs | 19 +++++- src/lib.rs | 17 ++++++ tests/fluent_setters.rs | 129 +++++++++++++++++++++++++++++++++++++++ tests/raw_identifiers.rs | 10 ++- 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 tests/fluent_setters.rs diff --git a/src/generate.rs b/src/generate.rs index 4ab18bf..0b93e24 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -17,6 +17,7 @@ pub enum GenMode { GetCopy, Set, GetMut, + SetFluent } impl GenMode { @@ -26,19 +27,20 @@ impl GenMode { GetCopy => "get_copy", Set => "set", GetMut => "get_mut", + SetFluent => "set_fluent" } } pub fn prefix(self) -> &'static str { match self { Get | GetCopy | GetMut => "", - Set => "set_", + Set | SetFluent => "set_", } } pub fn suffix(self) -> &'static str { match self { - Get | GetCopy | Set => "", + Get | GetCopy | Set | SetFluent => "", GetMut => "_mut", } } @@ -46,7 +48,7 @@ impl GenMode { fn is_get(self) -> bool { match self { GenMode::Get | GenMode::GetCopy | GenMode::GetMut => true, - GenMode::Set => false, + GenMode::Set | SetFluent => false, } } } @@ -191,6 +193,17 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { } } } + GenMode::SetFluent => { + quote! { + #(#doc)* + #[inline(always)] + #visibility fn #fn_name(self, val: #ty) -> Self { + let mut moved_out = self; + moved_out.#field_name = val; + moved_out + } + } + } }, // Don't need to do anything. None => quote! {}, diff --git a/src/lib.rs b/src/lib.rs index faf8c08..d4026aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,6 +256,23 @@ pub fn setters(input: TokenStream) -> TokenStream { gen.into() } +#[proc_macro_derive(FluentSetters, attributes(set_fluent, getset))] +#[proc_macro_error] +pub fn fluent_setters(input: TokenStream) -> TokenStream { + // Parse the string representation + let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for setters"); + let params = GenParams { + mode: GenMode::SetFluent, + global_attr: parse_global_attr(&ast.attrs, GenMode::SetFluent), + }; + + // Build the impl + let gen = produce(&ast, ¶ms); + + // Return the generated impl + gen.into() +} + fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option { attrs .iter() diff --git a/tests/fluent_setters.rs b/tests/fluent_setters.rs new file mode 100644 index 0000000..d60f2af --- /dev/null +++ b/tests/fluent_setters.rs @@ -0,0 +1,129 @@ +#[macro_use] +extern crate getset; + +use crate::submodule::other::{Generic, Plain, Where}; + +// For testing `pub(super)` +mod submodule { + // For testing `pub(in super::other)` + pub mod other { + #[derive(FluentSetters, Default)] + #[set_fluent] + pub struct Plain { + /// A doc comment. + /// Multiple lines, even. + private_accessible: usize, + + /// A doc comment. + #[set_fluent = "pub"] + public_accessible: usize, + + /// This field is used for testing chaining. + #[set_fluent = "pub"] + second_public_accessible: bool, + // /// A doc comment. + // #[set_fluent = "pub(crate)"] + // crate_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(super)"] + // super_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(in super::other)"] + // scope_accessible: usize, + } + + #[derive(FluentSetters, Default)] + #[set_fluent] + pub struct Generic { + /// A doc comment. + /// Multiple lines, even. + private_accessible: T, + + /// A doc comment. + #[set_fluent = "pub"] + public_accessible: T, + // /// A doc comment. + // #[set_fluent = "pub(crate)"] + // crate_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(super)"] + // super_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(in super::other)"] + // scope_accessible: usize, + } + + #[derive(FluentSetters, Default)] + #[set_fluent] + pub struct Where + where + T: Copy + Clone + Default, + { + /// A doc comment. + /// Multiple lines, even. + private_accessible: T, + + /// A doc comment. + #[set_fluent = "pub"] + public_accessible: T, + // /// A doc comment. + // #[set_fluent = "pub(crate)"] + // crate_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(super)"] + // super_accessible: usize, + + // /// A doc comment. + // #[set_fluent = "pub(in super::other)"] + // scope_accessible: usize, + } + + #[test] + fn test_plain() { + let mut val = Plain::default(); + val.set_private_accessible(1); + } + + #[test] + fn test_generic() { + let mut val = Generic::default(); + val.set_private_accessible(1); + } + + #[test] + fn test_where() { + let mut val = Where::default(); + val.set_private_accessible(1); + } + } +} + +#[test] +fn test_plain() { + let mut val = Plain::default(); + val.set_public_accessible(1); +} + +#[test] +fn test_generic() { + let mut val = Generic::default(); + val.set_public_accessible(1); +} + +#[test] +fn test_where() { + let mut val = Where::default(); + val.set_public_accessible(1); +} + +#[test] +fn test_chaining() { + let mut val = Plain::default(); + val.set_public_accessible(1) + .set_second_public_accessible(true); +} diff --git a/tests/raw_identifiers.rs b/tests/raw_identifiers.rs index 7e67c42..45df04f 100644 --- a/tests/raw_identifiers.rs +++ b/tests/raw_identifiers.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate getset; -#[derive(CopyGetters, Default, Getters, MutGetters, Setters)] +#[derive(CopyGetters, Default, Getters, MutGetters, Setters, FluentSetters)] struct RawIdentifiers { #[get] r#type: usize, @@ -15,6 +15,8 @@ struct RawIdentifiers { r#const: usize, #[get_copy = "with_prefix"] r#if: usize, + #[set_fluent] + r#else: usize, // Ensure having no gen mode doesn't break things. #[allow(dead_code)] r#loop: usize, @@ -55,3 +57,9 @@ fn test_get_copy_with_prefix() { let val = RawIdentifiers::default(); let _ = val.get_if(); } + +#[test] +fn test_set_fluent() { + let mut val = RawIdentifiers::default(); + let _ = val.set_else(42); +}