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

Withers #87

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ 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, Withers};

#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
#[derive(Getters, Setters, Withers, 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, with, get_mut)]
private: T,

/// Doc comments are supported!
Expand All @@ -35,15 +35,16 @@ where
fn main() {
let mut foo = Foo::default();
foo.set_private(1);
foo = foo.with_private(2)
(*foo.private_mut()) += 1;
assert_eq!(*foo.private(), 2);
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::{Getters, MutGetters, CopyGetters, Setters, Withers};
pub struct Foo<T>
where
T: Copy + Clone + Default,
Expand All @@ -54,7 +55,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", with = "pub", get_mut = "pub")]
public: T,
}
impl<T> Foo<T>
Expand All @@ -79,6 +80,15 @@ where
self.public = val;
self
}

/// Doc comments are supported!
/// Multiline, even.
#[inline(always)]
pub fn with_public(self, val: T) -> Self {
let mut v = self;
v.public = val;
v
}
}
impl<T> Foo<T>
where
Expand Down
11 changes: 6 additions & 5 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use getset::{CopyGetters, Getters, MutGetters, Setters};
use getset::{CopyGetters, Getters, MutGetters, Setters, Withers};

#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
#[derive(Getters, Setters, Withers, 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, with, get_mut)]
private: T,

/// Doc comments are supported!
Expand All @@ -19,6 +19,7 @@ where
fn main() {
let mut foo = Foo::default();
foo.set_private(1);
foo = foo.with_private(2);
(*foo.private_mut()) += 1;
assert_eq!(*foo.private(), 2);
}
assert_eq!(*foo.private(), 3);
}
18 changes: 16 additions & 2 deletions src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum GenMode {
Get,
GetCopy,
Set,
With,
GetMut,
}

Expand All @@ -26,27 +27,29 @@ impl GenMode {
GetCopy => "get_copy",
Set => "set",
GetMut => "get_mut",
With => "with"
}
}

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

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

fn is_get(self) -> bool {
match self {
GenMode::Get | GenMode::GetCopy | GenMode::GetMut => true,
GenMode::Set => false,
GenMode::Set | With => false,
}
}
}
Expand Down Expand Up @@ -182,6 +185,17 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 {
}
}
}
GenMode::With => {
quote! {
#(#doc)*
#[inline(always)]
#visibility fn #fn_name(self, val: #ty) -> Self {
let mut v = self;
v.#field_name = val;
v
}
}
}
GenMode::GetMut => {
quote! {
#(#doc)*
Expand Down
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,23 @@ pub fn setters(input: TokenStream) -> TokenStream {
gen.into()
}

#[proc_macro_derive(Withers, attributes(with, getset))]
#[proc_macro_error]
pub fn withers(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::With,
global_attr: parse_global_attr(&ast.attrs, GenMode::With),
};

// Build the impl
let gen = produce(&ast, &params);

// Return the generated impl
gen.into()
}

fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
attrs
.iter()
Expand All @@ -276,6 +293,7 @@ fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<Meta> {
|| meta.path().is_ident("get_copy")
|| meta.path().is_ident("get_mut")
|| meta.path().is_ident("set")
|| meta.path().is_ident("with")
|| meta.path().is_ident("skip"))
{
abort!(meta.path().span(), "unknown setter or getter")
Expand Down
129 changes: 129 additions & 0 deletions tests/withers.rs
Original file line number Diff line number Diff line change
@@ -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(Withers, Default)]
#[with]
pub struct Plain {
/// A doc comment.
/// Multiple lines, even.
private_accessible: usize,

/// A doc comment.
#[with = "pub"]
public_accessible: usize,

/// This field is used for testing chaining.
#[with = "pub"]
second_public_accessible: bool,
// /// A doc comment.
// #[with = "pub(crate)"]
// crate_accessible: usize,

// /// A doc comment.
// #[with = "pub(super)"]
// super_accessible: usize,

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

#[derive(Withers, Default)]
#[with]
pub struct Generic<T: Copy + Clone + Default> {
/// A doc comment.
/// Multiple lines, even.
private_accessible: T,

/// A doc comment.
#[with = "pub"]
public_accessible: T,
// /// A doc comment.
// #[with = "pub(crate)"]
// crate_accessible: usize,

// /// A doc comment.
// #[with = "pub(super)"]
// super_accessible: usize,

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

#[derive(Withers, Default)]
#[with]
pub struct Where<T>
where
T: Copy + Clone + Default,
{
/// A doc comment.
/// Multiple lines, even.
private_accessible: T,

/// A doc comment.
#[with = "pub"]
public_accessible: T,
// /// A doc comment.
// #[with = "pub(crate)"]
// crate_accessible: usize,

// /// A doc comment.
// #[with = "pub(super)"]
// super_accessible: usize,

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

#[test]
fn test_plain() {
let mut val = Plain::default();
val.with_private_accessible(1);
}

#[test]
fn test_generic() {
let mut val = Generic::default();
val.with_private_accessible(1);
}

#[test]
fn test_where() {
let mut val = Where::default();
val.with_private_accessible(1);
}
}
}

#[test]
fn test_plain() {
let mut val = Plain::default();
val.with_public_accessible(1);
}

#[test]
fn test_generic() {
let mut val = Generic::default();
val.with_public_accessible(1);
}

#[test]
fn test_where() {
let mut val = Where::default();
val.with_public_accessible(1);
}

#[test]
fn test_chaining() {
let mut val = Plain::default();
val.with_public_accessible(1)
.with_second_public_accessible(true);
}