Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add with_prefix option for getters #23

Merged
merged 2 commits into from
Mar 7, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,18 @@ pub fn implement(field: &Field, mode: GenMode, params: GenParams) -> TokenStream
}
}
},
// `#[get = "pub"]` or `#[set = "pub"]`
// `#[get = "pub with_prefix"]` or `#[set = "pub"]`
Some(Meta::NameValue(MetaNameValue {
lit: Lit::Str(ref s),
..
})) => {
let visibility = Ident::new(&s.value(), s.span());
let tokens = s.value();
let visibility =
if let Some(t) = tokens.split(" ").find(|v| v.starts_with("pub")) {
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
Some(Ident::new(&t, s.span()))
} else {
None
};
match mode {
GenMode::Get => {
quote! {
Expand Down
67 changes: 61 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ Getset, we're ready to go!

A procedural macro for generating the most basic getters and setters on fields.

Getters are generated as `fn field(&self) -> &type`, while setters are generated as `fn field(&mut self, val: type)`.
Getters are generated as `fn field(&self) -> &type`, while setters are generated
as `fn field(&mut self, val: type)`.

These macros are not intended to be used on fields which require custom logic inside of their setters and getters. Just write your own in that case!
These macros are not intended to be used on fields which require custom logic
inside of their setters and getters. Just write your own in that case!

```rust
#[macro_use]
Expand All @@ -31,6 +33,26 @@ fn main() {
assert_eq!(*foo.private(), 2);
}
```

For some purposes, it's useful to have the `get_` prefix on the getters for
either legacy of compatability reasons. It is done with `get-prefix`.

```rust
#[macro_use]
extern crate getset;

#[derive(Getters, Default)]
pub struct Foo {
#[get = "pub with_prefix"]
field: bool,
}

fn main() {
let mut foo = Foo::default();
let val = foo.get_field();
}
```

*/

extern crate proc_macro;
Expand All @@ -41,24 +63,55 @@ extern crate proc_macro2;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::{DataStruct, DeriveInput, Field};
use syn::{Attribute, DataStruct, DeriveInput, Field, Ident, Lit, Meta};

mod generate;
use generate::{GenMode, GenParams};

#[proc_macro_derive(Getters, attributes(get))]
fn attr_name(attr: &Attribute) -> Option<Ident> {
attr.interpret_meta().map(|v| v.name())
}

/// Some users want legacy/compatability.
/// (Getters are often prefixed with `get_`)
fn has_with_prefix_set(f: &Field) -> bool {
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
let inner = f
.attrs
.iter()
.filter(|v| attr_name(v).expect("attribute") == "get")
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
.last()
.and_then(|v| v.parse_meta().ok());
match inner {
Some(Meta::NameValue(meta)) => {
if let Lit::Str(lit) = meta.lit {
// Naive tokenization to avoid a possible visibility mod named `with_prefix`.
lit.value()
.split(" ")
.find(|v| *v == "with_prefix")
.is_some()
} else {
false
}
}
_ => false,
}
}

#[proc_macro_derive(Getters, attributes(get, with_prefix))]
pub fn getters(input: TokenStream) -> TokenStream {
// Parse the string representation
let ast = syn::parse(input).expect("Couldn't parse for getters");

// Build the impl
let gen = produce(&ast, |f| {
let prefix = if has_with_prefix_set(f) { "get_" } else { "" };

generate::implement(
f,
GenMode::Get,
GenParams {
attribute_name: "get",
fn_name_prefix: "",
fn_name_prefix: prefix,
fn_name_suffix: "",
},
)
Expand All @@ -74,12 +127,14 @@ pub fn mut_getters(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).expect("Couldn't parse for getters");
// Build the impl
let gen = produce(&ast, |f| {
let prefix = if has_with_prefix_set(f) { "get_" } else { "" };

generate::implement(
f,
GenMode::GetMut,
GenParams {
attribute_name: "get_mut",
fn_name_prefix: "",
fn_name_prefix: prefix,
fn_name_suffix: "_mut",
},
)
Expand Down
31 changes: 27 additions & 4 deletions tests/getters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use submodule::other::{Generic, Plain, Where};

// For testing `pub(super)`
mod submodule {
// For testing `pub(in super::other)`
// For testing `pub(super::other)`
pub mod other {
#[derive(Getters)]
pub struct Plain {
Expand All @@ -26,15 +26,25 @@ mod submodule {
// super_accessible: usize,

// /// A doc comment.
// #[get = "pub(in super::other)"]
// #[get = "pub(super::other)"]
// scope_accessible: usize,

// Prefixed getter.
#[get = "with_prefix"]
private_prefixed: usize,

// Prefixed getter.
#[get = "pub with_prefix"]
public_prefixed: usize,
}

impl Default for Plain {
fn default() -> Plain {
Plain {
private_accessible: 17,
public_accessible: 18,
private_prefixed: 19,
public_prefixed: 20,
}
}
}
Expand All @@ -58,7 +68,7 @@ mod submodule {
// super_accessible: usize,

// /// A doc comment.
// #[get = "pub(in super::other)"]
// #[get = "pub(super::other)"]
// scope_accessible: usize,
}

Expand All @@ -84,7 +94,7 @@ mod submodule {
// super_accessible: usize,

// /// A doc comment.
// #[get = "pub(in super::other)"]
// #[get = "pub(super::other)"]
// scope_accessible: usize,
}

Expand All @@ -105,6 +115,13 @@ mod submodule {
let val = Where::<usize>::default();
val.private_accessible();
}

#[test]
fn test_prefixed_plain() {
let val = Plain::default();
assert_eq!(19, *val.get_private_prefixed());
}

}
}

Expand All @@ -125,3 +142,9 @@ fn test_where() {
let val = Where::<usize>::default();
assert_eq!(usize::default(), *val.public_accessible());
}

#[test]
fn test_prefixed_plain() {
let val = Plain::default();
assert_eq!(20, *val.get_public_prefixed());
}