diff --git a/src/generate.rs b/src/generate.rs index c76448d..040750a 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -4,7 +4,7 @@ use syn::{ self, ext::IdentExt, spanned::Spanned, Expr, Field, Lit, Meta, MetaNameValue, Visibility, }; -use self::GenMode::{Get, GetCopy, GetMut, Set, SetWith}; +use self::GenMode::{Get, GetClone, GetCopy, GetMut, Set, SetWith}; use super::parse_attr; pub struct GenParams { @@ -15,6 +15,7 @@ pub struct GenParams { #[derive(PartialEq, Eq, Copy, Clone)] pub enum GenMode { Get, + GetClone, GetCopy, GetMut, Set, @@ -25,6 +26,7 @@ impl GenMode { pub fn name(self) -> &'static str { match self { Get => "get", + GetClone => "get_clone", GetCopy => "get_copy", GetMut => "get_mut", Set => "set", @@ -34,7 +36,7 @@ impl GenMode { pub fn prefix(self) -> &'static str { match self { - Get | GetCopy | GetMut => "", + Get | GetClone | GetCopy | GetMut => "", Set => "set_", SetWith => "with_", } @@ -42,15 +44,15 @@ impl GenMode { pub fn suffix(self) -> &'static str { match self { - Get | GetCopy | Set | SetWith => "", + Get | GetClone | GetCopy | Set | SetWith => "", GetMut => "_mut", } } fn is_get(self) -> bool { match self { - GenMode::Get | GenMode::GetCopy | GenMode::GetMut => true, - GenMode::Set | GenMode::SetWith => false, + Get | GetClone | GetCopy | GetMut => true, + Set | SetWith => false, } } } @@ -112,6 +114,7 @@ fn has_prefix_attr(f: &Field, params: &GenParams) -> bool { .filter_map(|attr| parse_attr(attr, params.mode)) .find(|meta| { meta.path().is_ident("get") + || meta.path().is_ident("get_clone") || meta.path().is_ident("get_copy") || meta.path().is_ident("get_mut") }) @@ -167,7 +170,7 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { // Generate nothing for skipped field Some(meta) if meta.path().is_ident("skip") => quote! {}, Some(_) => match params.mode { - GenMode::Get => { + Get => { quote! { #(#doc)* #[inline(always)] @@ -176,7 +179,16 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { } } } - GenMode::GetCopy => { + GetClone => { + quote! { + #(#doc)* + #[inline(always)] + #visibility fn #fn_name(&self) -> #ty { + self.#field_name.clone() + } + } + } + GetCopy => { quote! { #(#doc)* #[inline(always)] @@ -185,7 +197,7 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { } } } - GenMode::Set => { + Set => { quote! { #(#doc)* #[inline(always)] @@ -195,7 +207,7 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { } } } - GenMode::GetMut => { + GetMut => { quote! { #(#doc)* #[inline(always)] @@ -204,7 +216,7 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 { } } } - GenMode::SetWith => { + SetWith => { quote! { #(#doc)* #[inline(always)] @@ -234,7 +246,7 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2 // Generate nothing for skipped field Some(meta) if meta.path().is_ident("skip") => quote! {}, Some(_) => match params.mode { - GenMode::Get => { + Get => { let fn_name = Ident::new("get", Span::call_site()); quote! { #(#doc)* @@ -244,7 +256,17 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2 } } } - GenMode::GetCopy => { + GetClone => { + let fn_name = Ident::new("get", Span::call_site()); + quote! { + #(#doc)* + #[inline(always)] + #visibility fn #fn_name(&self) -> #ty { + self.0.clone() + } + } + } + GetCopy => { let fn_name = Ident::new("get", Span::call_site()); quote! { #(#doc)* @@ -254,7 +276,7 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2 } } } - GenMode::Set => { + Set => { let fn_name = Ident::new("set", Span::call_site()); quote! { #(#doc)* @@ -265,7 +287,7 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2 } } } - GenMode::GetMut => { + GetMut => { let fn_name = Ident::new("get_mut", Span::call_site()); quote! { #(#doc)* @@ -275,7 +297,7 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2 } } } - GenMode::SetWith => { + SetWith => { let fn_name = Ident::new("set_with", Span::call_site()); quote! { #(#doc)* diff --git a/src/lib.rs b/src/lib.rs index 12b5d8d..9675d37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,6 +226,18 @@ pub fn getters(input: TokenStream) -> TokenStream { produce(&ast, ¶ms).into() } +#[proc_macro_derive(CloneGetters, attributes(get_clone, with_prefix, getset))] +#[proc_macro_error] +pub fn clone_getters(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let params = GenParams { + mode: GenMode::GetClone, + global_attr: parse_global_attr(&ast.attrs, GenMode::GetClone), + }; + + produce(&ast, ¶ms).into() +} + #[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))] #[proc_macro_error] pub fn copy_getters(input: TokenStream) -> TokenStream { @@ -292,6 +304,7 @@ fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option { .into_iter() .inspect(|meta| { if !(meta.path().is_ident("get") + || meta.path().is_ident("get_clone") || meta.path().is_ident("get_copy") || meta.path().is_ident("get_mut") || meta.path().is_ident("set") diff --git a/tests/clone_getters.rs b/tests/clone_getters.rs new file mode 100644 index 0000000..39c716a --- /dev/null +++ b/tests/clone_getters.rs @@ -0,0 +1,244 @@ +#[macro_use] +extern crate getset; + +use crate::submodule::other::{Generic, Plain, ReferenceCounted, Where}; +use std::rc::Rc; +use std::sync::Arc; + +// For testing `pub(super)` +mod submodule { + // For testing `pub(super::other)` + pub mod other { + use std::rc::Rc; + use std::sync::Arc; + + #[derive(CloneGetters)] + #[get_clone] + pub struct Plain { + /// A doc comment. + /// Multiple lines, even. + private_accessible: Box, + + /// A doc comment. + #[get_clone = "pub"] + public_accessible: Box, + // /// A doc comment. + // #[get_clone = "pub(crate)"] + // crate_accessible: Box, + + // /// A doc comment. + // #[get_clone = "pub(super)"] + // super_accessible: Box, + + // /// A doc comment. + // #[get_clone = "pub(super::other)"] + // scope_accessible: Box, + + // Prefixed getter. + #[get_clone = "with_prefix"] + private_prefixed: Box, + + // Prefixed getter. + #[get_clone = "pub with_prefix"] + public_prefixed: Box, + } + + impl Default for Plain { + fn default() -> Plain { + Plain { + private_accessible: Box::new(17), + public_accessible: Box::new(18), + private_prefixed: Box::new(19), + public_prefixed: Box::new(20), + } + } + } + + #[derive(CloneGetters)] + #[get_clone] + pub struct ReferenceCounted { + /// A doc comment. + /// Multiple lines, even. + private_accessible: Arc, + + /// A doc comment. + #[get_clone = "pub"] + public_accessible: Arc, + // /// A doc comment. + // #[get_clone = "pub(crate)"] + // crate_accessible: Arc, + + // /// A doc comment. + // #[get_clone = "pub(super)"] + // super_accessible: Arc, + + // /// A doc comment. + // #[get_clone = "pub(super::other)"] + // scope_accessible: Arc, + + // Prefixed getter. + #[get_clone = "with_prefix"] + private_prefixed: Arc, + + // Prefixed getter. + #[get_clone = "pub with_prefix"] + public_prefixed: Arc, + } + + impl Default for ReferenceCounted { + fn default() -> ReferenceCounted { + ReferenceCounted { + private_accessible: Arc::new(17), + public_accessible: Arc::new(18), + private_prefixed: Arc::new(19), + public_prefixed: Arc::new(20), + } + } + } + + #[derive(CloneGetters, Default)] + #[get_clone] + pub struct Generic { + /// A doc comment. + /// Multiple lines, even. + private_accessible: T, + + /// A doc comment. + #[get_clone = "pub"] + public_accessible: T, + // /// A doc comment. + // #[get_clone = "pub(crate)"] + // crate_accessible: T, + + // /// A doc comment. + // #[get_clone = "pub(super)"] + // super_accessible: T, + + // /// A doc comment. + // #[get_clone = "pub(super::other)"] + // scope_accessible: T, + } + + #[derive(CloneGetters, Getters, Default)] + #[get_clone] + pub struct Where + where + T: Clone + Default, + { + /// A doc comment. + /// Multiple lines, even. + private_accessible: T, + + /// A doc comment. + #[get_clone = "pub"] + public_accessible: T, + // /// A doc comment. + // #[get_clone = "pub(crate)"] + // crate_accessible: T, + + // /// A doc comment. + // #[get_clone = "pub(super)"] + // super_accessible: T, + + // /// A doc comment. + // #[get_clone = "pub(super::other)"] + // scope_accessible: T, + } + + #[test] + fn test_plain() { + let val = Plain::default(); + val.private_accessible(); + } + + #[test] + fn test_reference_counted() { + let val = ReferenceCounted::default(); + val.private_accessible(); + } + + #[test] + fn test_generic() { + let val = Generic::>::default(); + val.private_accessible(); + + let val = Generic::>::default(); + val.private_accessible(); + + let val = Generic::>::default(); + val.private_accessible(); + } + + #[test] + fn test_where() { + let val = Where::>::default(); + val.private_accessible(); + + let val = Where::>::default(); + val.private_accessible(); + + let val = Where::>::default(); + val.private_accessible(); + } + + #[test] + fn test_prefixed_plain() { + let val = Plain::default(); + assert_eq!(19, *val.get_private_prefixed()); + } + + #[test] + fn test_prefixed_reference_counted() { + let val = ReferenceCounted::default(); + assert_eq!(19, *val.get_private_prefixed()); + } + } +} + +#[test] +fn test_plain() { + let val = Plain::default(); + assert_eq!(18, *val.public_accessible()); +} + +#[test] +fn test_reference_counted() { + let val = ReferenceCounted::default(); + assert_eq!(18, *val.public_accessible()); +} + +#[test] +fn test_generic() { + let val = Generic::>::default(); + assert_eq!(Box::default(), val.public_accessible()); + + let val = Generic::>::default(); + assert_eq!(Rc::default(), val.public_accessible()); + + let val = Generic::>::default(); + assert_eq!(Arc::default(), val.public_accessible()); +} + +#[test] +fn test_where() { + let val = Where::>::default(); + assert_eq!(Box::default(), val.public_accessible()); + + let val = Where::>::default(); + assert_eq!(Rc::default(), val.public_accessible()); + + let val = Where::>::default(); + assert_eq!(Arc::default(), val.public_accessible()); +} + +#[test] +fn test_prefixed_plain() { + let val = Plain::default(); + assert_eq!(20, *val.get_public_prefixed()); +} + +#[test] +fn test_prefixed_reference_counted() { + let val = ReferenceCounted::default(); + assert_eq!(20, *val.get_public_prefixed()); +}