diff --git a/Cargo.lock b/Cargo.lock index b614e32..9fdab11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,14 +33,14 @@ dependencies = [ [[package]] name = "ters" -version = "0.2.0" +version = "0.2.1" dependencies = [ "ters-macros", ] [[package]] name = "ters-macros" -version = "0.2.0" +version = "0.2.1" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index e646137..18b2c90 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Generate getters and setters procedurally. Annotate fields with `#[get]` to generate a getter method. + ```rust use ters::ters; @@ -19,7 +20,26 @@ fn getters() { } ``` +Annotate fields with `#[get(deref)]` to generate a getter method that returns the dereferenced value. + +```rust +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get(deref)] + b: bool, +} + +fn getters() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.b(), true); +} +``` + Annotate fields with `#[set]` to generate a setter method. + ```rust use ters::ters; @@ -37,6 +57,7 @@ fn setters() { ``` Annotate fields with `#[get]` and `#[set]` to generate both a getter and a setter method. + ```rust use ters::ters; @@ -57,7 +78,8 @@ fn getters_and_setters() { ``` Unannotated fields will not have generated getters or setters. -```rust + +```rust,compile_fail,E0599 use ters::ters; #[ters] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 7450770..adc9412 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ters-macros" -version = "0.2.0" +version = "0.2.1" edition = "2021" description = "Supporting macros for the ters crate." license = "MIT" diff --git a/macros/src/docs.md b/macros/src/docs.md new file mode 100644 index 0000000..bc1947b --- /dev/null +++ b/macros/src/docs.md @@ -0,0 +1,94 @@ +Generate getters and setters procedurally. + +Annotate fields with `#[get]` to generate a getter method. + +```rust,ignore +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get] + b: bool, +} + +fn getters() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.b(), &true); +} +``` + +Annotate fields with `#[get(deref)]` to generate a getter method that returns the dereferenced value. + +```rust,ignore +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get(deref)] + b: bool, +} + +fn getters() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.b(), true); +} +``` + +Annotate fields with `#[set]` to generate a setter method. + +```rust,ignore +use ters::ters; + +#[ters] +struct Foo { + #[set] + a: i32, + b: bool, +} + +fn setters() { + let mut foo = Foo { a: 42, b: true }; + foo.set_a(31); +} +``` + +Annotate fields with `#[get]` and `#[set]` to generate both a getter and a setter method. + +```rust,ignore +use ters::ters; + +#[ters] +struct Foo { + #[get] + #[set] + a: i32, + b: bool, +} + +fn getters_and_setters() { + let mut foo = Foo { a: 42, b: true }; + assert_eq!(foo.a(), &42); + foo.set_a(31); + assert_eq!(foo.a(), &31); +} +``` + +Unannotated fields will not have generated getters or setters. + +```rust,ignore +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get] + b: bool, +} + +fn getters_not_generated() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.a(), &42); // this method doesn't exist +} +``` diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 7947393..e0f42e2 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,80 +1,8 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; -use syn::{parse_macro_input, ItemStruct}; - -/// Generate getters and setters procedurally. -/// -/// Annotate fields with `#[get]` to generate a getter method. -/// ```ignore -/// use ters::ters; -/// -/// #[ters] -/// struct Foo { -/// a: i32, -/// #[get] -/// b: bool, -/// } -/// -/// fn getters() { -/// let foo = Foo { a: 42, b: true }; -/// assert_eq!(foo.b(), &true); -/// } -/// ``` -/// -/// Annotate fields with `#[set]` to generate a setter method. -/// ```ignore -/// use ters::ters; -/// -/// #[ters] -/// struct Foo { -/// #[set] -/// a: i32, -/// b: bool, -/// } -/// -/// fn setters() { -/// let mut foo = Foo { a: 42, b: true }; -/// foo.set_a(31); -/// } -/// ``` -/// -/// Annotate fields with `#[get]` and `#[set]` to generate both a getter and a setter method. -/// ```ignore -/// use ters::ters; -/// -/// #[ters] -/// struct Foo { -/// #[get] -/// #[set] -/// a: i32, -/// b: bool, -/// } -/// -/// fn getters_and_setters() { -/// let mut foo = Foo { a: 42, b: true }; -/// assert_eq!(foo.a(), &42); -/// foo.set_a(31); -/// -/// assert_eq!(foo.a(), &31); -/// } -/// ``` -/// -/// Unannotated fields will not have generated getters or setters. -/// ```ignore -/// use ters::ters; -/// -/// #[ters] -/// struct Foo { -/// a: i32, -/// #[get] -/// b: bool, -/// } -/// -/// fn getters_not_generated() { -/// let foo = Foo { a: 42, b: true }; -/// assert_eq!(foo.a(), &42); // this method doesn't exist -/// } -/// ``` +use syn::{parse_macro_input, Ident, ItemStruct}; + +#[doc = include_str!("docs.md")] #[proc_macro_attribute] pub fn ters(_args: TokenStream, tokens: TokenStream) -> TokenStream { let item = parse_macro_input!(tokens as ItemStruct); @@ -90,10 +18,17 @@ fn ters_inner(mut item: ItemStruct) -> proc_macro2::TokenStream { for field in item.fields.iter_mut() { let mut get = false; let mut set = false; + let mut deref = false; field.attrs.retain(|attr| { if attr.path().is_ident("get") { get = true; + + // super dumb, gotta add error emission + if attr.parse_args::().is_ok_and(|x| x.eq("deref")) { + deref = true + } + false } else if attr.path().is_ident("set") { set = true; @@ -108,6 +43,7 @@ fn ters_inner(mut item: ItemStruct) -> proc_macro2::TokenStream { field.ty.clone(), get, set, + deref, field .attrs .iter() @@ -124,21 +60,27 @@ fn ters_inner(mut item: ItemStruct) -> proc_macro2::TokenStream { let accessors = fields .iter() - .filter_map(|(ident, ty, get, set, docs)| { + .filter_map(|(ident, ty, get, set, deref, docs)| { let set_ident = format_ident!("set_{ident}"); let str_ident = ident.to_string(); let mut body = quote! {}; if *get { + let (content, return_) = if *deref { + (quote! { self.#ident }, quote! { #ty }) + } else { + (quote! { &self.#ident }, quote! { &#ty }) + }; + body.extend(quote! { #[doc = "Getter for `"] #[doc = #str_ident] #[doc = "`.\n\n"] #(#docs)* #[inline] - pub fn #ident(&self) -> &#ty { - &self.#ident + pub fn #ident(&self) -> #return_ { + #content } }); } diff --git a/ters/Cargo.toml b/ters/Cargo.toml index a0f015f..6a3fded 100644 --- a/ters/Cargo.toml +++ b/ters/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "ters" -version = "0.2.0" +version = "0.2.1" edition = "2021" description = "Generate getters and setters procedurally." license = "MIT" repository = "https://github.com/adinack/ters" [dependencies] -ters-macros = { path = "../macros", version = "0.2.0" } +ters-macros = { path = "../macros", version = "0.2.1" } diff --git a/ters/src/docs.md b/ters/src/docs.md new file mode 100644 index 0000000..b2edceb --- /dev/null +++ b/ters/src/docs.md @@ -0,0 +1,94 @@ +Generate getters and setters procedurally. + +Annotate fields with `#[get]` to generate a getter method. + +```rust +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get] + b: bool, +} + +fn getters() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.b(), &true); +} +``` + +Annotate fields with `#[get(deref)]` to generate a getter method that returns the dereferenced value. + +```rust +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get(deref)] + b: bool, +} + +fn getters() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.b(), true); +} +``` + +Annotate fields with `#[set]` to generate a setter method. + +```rust +use ters::ters; + +#[ters] +struct Foo { + #[set] + a: i32, + b: bool, +} + +fn setters() { + let mut foo = Foo { a: 42, b: true }; + foo.set_a(31); +} +``` + +Annotate fields with `#[get]` and `#[set]` to generate both a getter and a setter method. + +```rust +use ters::ters; + +#[ters] +struct Foo { + #[get] + #[set] + a: i32, + b: bool, +} + +fn getters_and_setters() { + let mut foo = Foo { a: 42, b: true }; + assert_eq!(foo.a(), &42); + foo.set_a(31); + assert_eq!(foo.a(), &31); +} +``` + +Unannotated fields will not have generated getters or setters. + +```rust,compile_fail,E0599 +use ters::ters; + +#[ters] +struct Foo { + a: i32, + #[get] + b: bool, +} + +fn getters_not_generated() { + let foo = Foo { a: 42, b: true }; + assert_eq!(foo.a(), &42); // this method doesn't exist +} +``` diff --git a/ters/src/lib.rs b/ters/src/lib.rs index 1e3681c..169aeba 100644 --- a/ters/src/lib.rs +++ b/ters/src/lib.rs @@ -1,77 +1,4 @@ -//! Generate getters and setters procedurally. -//! -//! Annotate fields with `#[get]` to generate a getter method. -//! ``` -//! use ters::ters; -//! -//! #[ters] -//! struct Foo { -//! a: i32, -//! #[get] -//! b: bool, -//! } -//! -//! fn getters() { -//! let foo = Foo { a: 42, b: true }; -//! assert_eq!(foo.b(), &true); -//! } -//! ``` -//! -//! Annotate fields with `#[set]` to generate a setter method. -//! ``` -//! use ters::ters; -//! -//! #[ters] -//! struct Foo { -//! #[set] -//! a: i32, -//! b: bool, -//! } -//! -//! fn setters() { -//! let mut foo = Foo { a: 42, b: true }; -//! foo.set_a(31); -//! } -//! ``` -//! -//! Annotate fields with `#[get]` and `#[set]` to generate both a getter and a setter method. -//! ``` -//! use ters::ters; -//! -//! #[ters] -//! struct Foo { -//! #[get] -//! #[set] -//! a: i32, -//! b: bool, -//! } -//! -//! fn getters_and_setters() { -//! let mut foo = Foo { a: 42, b: true }; -//! assert_eq!(foo.a(), &42); -//! foo.set_a(31); -//! -//! assert_eq!(foo.a(), &31); -//! } -//! ``` -//! -//! Unannotated fields will not have generated getters or setters. -//! ```compile_fail,E0599 -//! use ters::ters; -//! -//! #[ters] -//! struct Foo { -//! a: i32, -//! #[get] -//! b: bool, -//! } -//! -//! fn getters_not_generated() { -//! let foo = Foo { a: 42, b: true }; -//! assert_eq!(foo.a(), &42); // this method doesn't exist -//! } -//! ``` - +#![doc = include_str!("docs.md")] #![no_std] pub use ters_macros::ters; @@ -86,12 +13,12 @@ mod tests { struct Foo { #[allow(unused)] a: i32, - #[get] + #[get(deref)] b: bool, } let foo = Foo { a: 42, b: true }; - assert_eq!(foo.b(), &true); + assert_eq!(foo.b(), true); } #[test] @@ -100,16 +27,16 @@ mod tests { struct Foo { #[allow(unused)] a: i32, - #[get] + #[get(deref)] #[set] b: bool, } let mut foo = Foo { a: 42, b: true }; - assert_eq!(foo.b(), &true); + assert_eq!(foo.b(), true); foo.set_b(false); - assert_eq!(foo.b(), &false); + assert_eq!(foo.b(), false); } #[test]