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 'WithSetters' proc_macro_derive #102

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ Getset, we're ready to go!
A procedural macro for generating the most basic getters and setters on fields.
"""
version = "0.1.3"
authors = ["Ana Hobden <[email protected]>", "John Baublitz <[email protected]"]
authors = [
"Ana Hobden <[email protected]>",
"John Baublitz <[email protected]",
]
license = "MIT"
edition = "2018"

Expand Down
33 changes: 26 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ Getters are generated as `fn field(&self) -> &type`, while setters are generated
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
use getset::{CopyGetters, Getters, MutGetters, Setters};
use getset::{CopyGetters, Getters, MutGetters, Setters, WithSetters};

#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
#[derive(Getters, Setters, MutGetters, CopyGetters, WithSetters, Default)]
pub struct Foo<T>
where
T: Copy + Clone + Default,
{
/// Doc comments are supported!
/// Multiline, even.
#[getset(get, set, get_mut)]
#[getset(get, set, get_mut, set_with)]
private: T,

/// Doc comments are supported!
/// Multiline, even.
#[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
#[getset(get_copy = "pub", set = "pub", get_mut = "pub", set_with = "pub")]
public: T,
}

Expand All @@ -37,13 +37,15 @@ fn main() {
foo.set_private(1);
(*foo.private_mut()) += 1;
assert_eq!(*foo.private(), 2);
foo = foo.with_private(3);
assert_eq!(*foo.private(), 3);
}
```

You can use `cargo-expand` to generate the output. Here are the functions that the above generates (Replicate with `cargo expand --example simple`):

```rust
use getset::{Getters, MutGetters, CopyGetters, Setters};
use getset::{CopyGetters, Getters, MutGetters, Setters, WithSetters};
pub struct Foo<T>
where
T: Copy + Clone + Default,
Expand All @@ -54,7 +56,7 @@ where
private: T,
/// Doc comments are supported!
/// Multiline, even.
#[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
#[getset(get_copy = "pub", set = "pub", get_mut = "pub", set_with = "pub")]
public: T,
}
impl<T> Foo<T>
Expand Down Expand Up @@ -108,6 +110,18 @@ where
self.public
}
}
impl<T> Foo<T>
where
T: Copy + Clone + Default,
{
/// Doc comments are supported!
/// Multiline, even.
#[inline(always)]
pub fn with_public(mut self, val: T) -> Self {
self.public = val;
self
}
}
```

Attributes can be set on struct level for all fields in struct as well. Field level attributes take
Expand Down Expand Up @@ -162,7 +176,7 @@ is possible with `#[getset(skip)]`.
use getset::{CopyGetters, Setters};

#[derive(CopyGetters, Setters)]
#[getset(get_copy, set)]
#[getset(get_copy, set, set_with)]
pub struct Foo {
// If the field was not skipped, the compiler would complain about moving
// a non-copyable type in copy getter.
Expand All @@ -184,5 +198,10 @@ impl Foo {
self.skipped = val.to_string();
self
}

fn with_skipped(mut self, val: &str) -> Self {
self.skipped = val.to_string();
self
}
}
```
10 changes: 6 additions & 4 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use getset::{CopyGetters, Getters, MutGetters, Setters};
use getset::{CopyGetters, Getters, MutGetters, Setters, WithSetters};

#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
#[derive(Getters, Setters, WithSetters, MutGetters, CopyGetters, Default)]
pub struct Foo<T>
where
T: Copy + Clone + Default,
{
/// Doc comments are supported!
/// Multiline, even.
#[getset(get, set, get_mut)]
#[getset(get, set, get_mut, set_with)]
private: T,

/// Doc comments are supported!
/// Multiline, even.
#[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
#[getset(get_copy = "pub", set = "pub", get_mut = "pub", set_with = "pub")]
public: T,
}

Expand All @@ -21,4 +21,6 @@ fn main() {
foo.set_private(1);
(*foo.private_mut()) += 1;
assert_eq!(*foo.private(), 2);
foo = foo.with_private(3);
assert_eq!(*foo.private(), 3);
}
34 changes: 29 additions & 5 deletions src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use syn::{
self, ext::IdentExt, spanned::Spanned, Expr, Field, Lit, Meta, MetaNameValue, Visibility,
};

use self::GenMode::{Get, GetCopy, GetMut, Set};
use self::GenMode::{Get, GetCopy, GetMut, Set, SetWith};
use super::parse_attr;

pub struct GenParams {
Expand All @@ -16,38 +16,41 @@ pub struct GenParams {
pub enum GenMode {
Get,
GetCopy,
Set,
GetMut,
Set,
SetWith,
}

impl GenMode {
pub fn name(self) -> &'static str {
match self {
Get => "get",
GetCopy => "get_copy",
Set => "set",
GetMut => "get_mut",
Set => "set",
SetWith => "set_with",
}
}

pub fn prefix(self) -> &'static str {
match self {
Get | GetCopy | GetMut => "",
Set => "set_",
SetWith => "with_",
}
}

pub fn suffix(self) -> &'static str {
match self {
Get | GetCopy | Set => "",
Get | GetCopy | Set | SetWith => "",
GetMut => "_mut",
}
}

fn is_get(self) -> bool {
match self {
GenMode::Get | GenMode::GetCopy | GenMode::GetMut => true,
GenMode::Set => false,
GenMode::Set | GenMode::SetWith => false,
}
}
}
Expand Down Expand Up @@ -197,6 +200,16 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 {
}
}
}
GenMode::SetWith => {
quote! {
#(#doc)*
#[inline(always)]
#visibility fn #fn_name(mut self, val: #ty) -> Self {
self.#field_name = val;
self
}
}
}
},
None => quote! {},
}
Expand Down Expand Up @@ -258,6 +271,17 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2
}
}
}
GenMode::SetWith => {
let fn_name = Ident::new("set_with", Span::call_site());
quote! {
#(#doc)*
#[inline(always)]
#visibility fn #fn_name(mut self, val: #ty) -> Self {
self.0 = val;
self
}
}
}
},
None => quote! {},
}
Expand Down
30 changes: 24 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ assert_eq!(*foo.private(), 2);
You can use `cargo-expand` to generate the output. Here are the functions that the above generates (Replicate with `cargo expand --example simple`):

```rust,ignore
use getset::{Getters, MutGetters, CopyGetters, Setters};
use getset::{Getters, MutGetters, CopyGetters, Setters, WithSetters};
pub struct Foo<T>
where
T: Copy + Clone + Default,
Expand Down Expand Up @@ -107,7 +107,7 @@ precedence.

```rust
mod submodule {
use getset::{Getters, MutGetters, CopyGetters, Setters};
use getset::{Getters, MutGetters, CopyGetters, Setters, WithSetters};
#[derive(Getters, CopyGetters, Default)]
#[getset(get_copy = "pub")] // By default add a pub getting for all fields.
pub struct Foo {
Expand All @@ -129,7 +129,7 @@ For some purposes, it's useful to have the `get_` prefix on the getters for
either legacy of compatibility reasons. It is done with `with_prefix`.

```rust
use getset::{Getters, MutGetters, CopyGetters, Setters};
use getset::{Getters, MutGetters, CopyGetters, Setters, WithSetters};

#[derive(Getters, Default)]
pub struct Foo {
Expand All @@ -146,10 +146,10 @@ Skipping setters and getters generation for a field when struct level attribute
is possible with `#[getset(skip)]`.

```rust
use getset::{CopyGetters, Setters};
use getset::{CopyGetters, Setters, WithSetters};

#[derive(CopyGetters, Setters)]
#[getset(get_copy, set)]
#[derive(CopyGetters, Setters, WithSetters)]
#[getset(get_copy, set, set_with)]
pub struct Foo {
// If the field was not skipped, the compiler would complain about moving
// a non-copyable type in copy getter.
Expand All @@ -171,6 +171,11 @@ impl Foo {
self.skipped = val.to_string();
self
}

fn with_skipped(mut self, val: &str) -> Self {
self.skipped = val.to_string();
self
}
}
```

Expand Down Expand Up @@ -257,6 +262,18 @@ pub fn setters(input: TokenStream) -> TokenStream {
produce(&ast, &params).into()
}

#[proc_macro_derive(WithSetters, attributes(set_with, getset))]
#[proc_macro_error]
pub fn with_setters(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let params = GenParams {
mode: GenMode::SetWith,
global_attr: parse_global_attr(&ast.attrs, GenMode::SetWith),
};

produce(&ast, &params).into()
}

fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
attrs.iter().filter_map(|v| parse_attr(v, mode)).last()
}
Expand All @@ -278,6 +295,7 @@ fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<syn::Meta> {
|| meta.path().is_ident("get_copy")
|| meta.path().is_ident("get_mut")
|| meta.path().is_ident("set")
|| meta.path().is_ident("set_with")
|| meta.path().is_ident("skip"))
{
abort!(meta.path().span(), "unknown setter or getter")
Expand Down
13 changes: 11 additions & 2 deletions tests/skip.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[macro_use]
extern crate getset;

#[derive(CopyGetters, Setters)]
#[getset(get_copy, set)]
#[derive(CopyGetters, Setters, WithSetters)]
#[getset(get_copy, set, set_with)]
pub struct Plain {
// If the field was not skipped, the compiler would complain about moving a
// non-copyable type.
Expand Down Expand Up @@ -30,6 +30,13 @@ impl Plain {
self.non_copyable = val;
self
}

// If the field was not skipped, the compiler would complain about duplicate
// definitions of `with_non_copyable`.
fn with_non_copyable(mut self, val: String) -> Self {
self.non_copyable = val;
self
}
}

impl Default for Plain {
Expand All @@ -47,4 +54,6 @@ fn test_plain() {
val.copyable();
val.custom_non_copyable();
val.set_non_copyable("bar".to_string());
val = val.with_non_copyable("foo".to_string());
let _ = val;
}
14 changes: 9 additions & 5 deletions tests/unary_tuple.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use getset::{CopyGetters, Getters, MutGetters, Setters};
use getset::{CopyGetters, Getters, MutGetters, Setters, WithSetters};

#[test]
fn test_unary_tuple() {
#[derive(Setters, Getters, MutGetters)]
struct UnaryTuple(#[getset(set, get, get_mut)] i32);
#[derive(Setters, Getters, MutGetters, WithSetters)]
struct UnaryTuple(#[getset(set, get, get_mut, set_with)] i32);

let mut tup = UnaryTuple(42);
assert_eq!(tup.get(), &42);
assert_eq!(tup.get_mut(), &mut 42);
tup.set(43);
assert_eq!(tup.get(), &43);
tup = tup.set_with(44);
assert_eq!(tup.get(), &44);

#[derive(CopyGetters)]
struct CopyUnaryTuple(#[getset(get_copy)] i32);
Expand All @@ -20,15 +22,17 @@ fn test_unary_tuple() {

#[test]
fn test_unary_tuple_with_attrs() {
#[derive(Setters, Getters, MutGetters)]
#[getset(set, get, get_mut)]
#[derive(Setters, Getters, MutGetters, WithSetters)]
#[getset(set, get, get_mut, set_with)]
struct UnaryTuple(i32);

let mut tup = UnaryTuple(42);
assert_eq!(tup.get(), &42);
assert_eq!(tup.get_mut(), &mut 42);
tup.set(43);
assert_eq!(tup.get(), &43);
tup = tup.set_with(44);
assert_eq!(tup.get(), &44);

#[derive(CopyGetters)]
#[getset(get_copy)]
Expand Down
Loading
Loading