diff options
| -rw-r--r-- | Cargo.lock | 20 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/lib.rs | 159 | ||||
| -rw-r--r-- | tests/basic.rs | 2 |
4 files changed, 163 insertions, 20 deletions
@@ -33,16 +33,6 @@ dependencies = [ ] [[package]] -name = "auguments" -version = "0.1.0" -dependencies = [ - "attribute-derive", - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "collection_literals" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -60,6 +50,16 @@ dependencies = [ ] [[package]] +name = "feluments" +version = "0.1.0" +dependencies = [ + "attribute-derive", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "interpolator" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,5 +1,5 @@ [package] -name = "auguments" +name = "feluments" version = "0.1.0" edition = "2024" @@ -1,3 +1,59 @@ +//! A macro for optional arguments +//! +//! There are many builder crates for Rust. Thanks to Rust's powerful macro +//! system, it is easy to make a derive macro that creates a builder for a +//! struct. But none of them take the next step of adding a macro to allow +//! using literal syntax. +//! ``` +//! use feluments::*; +//! +//! #[derive(Debug, PartialEq, Eq, Builder)] +//! #[builder(vis = pub)] +//! struct Foo { +//! #[builder(default = 45)] +//! x: i32, +//! #[builder(into)] +//! y: String, +//! #[builder(optional)] +//! z: () +//! } +//! +//! # fn main() { +//! assert_eq!( +//! build!(Foo { x: 32, y: "baz" }), +//! Foo { x: 32, y: "baz".into(), z: () }, +//! ); +//! # } +//! ``` +//! +//! The above call to the [`build`] macro expands to the following: +//! +//! ``` +//! # use feluments::*; +//! # #[derive(Debug, PartialEq, Eq, Builder)] +//! # #[builder(vis = pub)] +//! # struct Foo { +//! # #[builder(default = 45)] +//! # x: i32, +//! # #[builder(into)] +//! # y: String, +//! # #[builder(optional)] +//! # z: () +//! # } +//! # fn main() { +//! # assert_eq!( +//! Foo::builder().x(32).y("baz").build(), +//! # Foo { x: 32, y: "baz".into(), z: () }, +//! # ); +//! # } +//! ``` +//! +//! Although this library provides a [`Builder`] derive macro to make the +//! [`build`] macro work, the `build!` macro is also compatible with other +//! builder crates, such as [bon](https://bon-rs.com/), +//! [buildstructor](https://crates.io/crates/buildstructor), or +//! [typed-builder](https://crates.io/crates/typed-builder). + use attribute_derive::FromAttr; use proc_macro::TokenStream; use proc_macro2::Span; @@ -29,6 +85,55 @@ struct BuilderFieldOptions { into: bool, } +#[derive(FromAttr)] +#[from_attr(ident = builder)] +struct BuilderOptions { + vis: Option<Visibility>, +} + +/// Creates a builder implementation for struct that is compatible with the +/// build! macro. +/// +/// When the `builder` attribute is applied to the struct, it may take a `vis` +/// argument, followed by the visibility of the `builder` method. +/// +/// When the `builder` attribute is applied to a field of the struct, there are +/// several arguments it may take: +/// - `vis`: The visibility of the setter for this field. The default is the +/// visibility of the field. +/// - `optional`: Makes the field optional. If the `default` argument is +/// provided, then the default value will be the one provided. Otherwise, it +/// will be [`Default::default`]. +/// - `default`: The default value of the field. Setting this parameter also +/// implies `optional` +/// - `into`: Arguments to the setter may be of any type that implements `Into` +/// for the type of the field. +/// +/// # Examples +/// +/// ``` +/// use feluments::Builder; +/// +/// #[derive(Debug, PartialEq, Eq, Builder)] +/// #[builder(vis = pub)] +/// struct Foo { +/// #[builder(default = 45)] +/// x: i32, +/// #[builder(into)] +/// y: String, +/// #[builder(optional)] +/// z: () +/// } +/// +/// # fn main() { +/// assert_eq!( +/// Foo::builder().y("bar").z(()).build(), +/// Foo { x: 45, y: "bar".into(), z: () }, +/// ); +/// # } +/// ``` +/// +/// [`Default::default`]: std::default::Default::default #[proc_macro_derive(Builder, attributes(builder))] pub fn derive_builder(input: TokenStream) -> TokenStream { let structure = parse_macro_input!(input as ItemStruct); @@ -53,12 +158,18 @@ pub fn derive_builder(input: TokenStream) -> TokenStream { }) .collect::<Box<_>>(); - let builder_visibility = fields - .iter() - .all(|field| matches!(field.visibility, Visibility::Public(_))) - .then_some(Visibility::Public(Pub { - span: Span::call_site(), - })); + let builder_visibility = BuilderOptions::from_attributes(structure.attrs) + .map(|options| options.vis) + .ok() + .flatten() + .or_else(|| { + fields + .iter() + .all(|field| matches!(field.visibility, Visibility::Public(_))) + .then_some(Visibility::Public(Pub { + span: Span::call_site(), + })) + }); let field_names = fields .iter() .map(|field| field.name.clone()) @@ -87,7 +198,7 @@ pub fn derive_builder(input: TokenStream) -> TokenStream { let visibility = &field.visibility; let setter_name = &field.name; let ty = &field.ty; - let param_ty = if field.auto_into { quote! {#ty} } else { quote! { impl ::core::convert::Into<#ty> } }; + let param_ty = if field.auto_into { quote! { impl ::core::convert::Into<#ty> } } else { quote! {#ty} }; let field_name = &field.name; let fields_head = &field_names[0..i]; let fields_tail = &field_names[i+1..]; @@ -96,7 +207,7 @@ pub fn derive_builder(input: TokenStream) -> TokenStream { let generics_tail = &const_generics[j+1..]; j += 1; quote! { - #visibility fn #setter_name(self, value: #param_ty>) -> #builder_name<#(#generics_head,)* true, #(#generics_tail),*> { + #visibility fn #setter_name(self, value: #param_ty) -> #builder_name<#(#generics_head,)* true, #(#generics_tail),*> { #builder_name { #(#fields_head: self.#fields_head,)* #field_name: Some(value.into()), @@ -180,6 +291,38 @@ pub fn derive_builder(input: TokenStream) -> TokenStream { .into() } +/// Builds a value of a type that derives [`Builder`] using a constructor +/// literal syntax. +/// +/// This is meant to be a convenience macro to using `Builder`, with a less +/// verbose syntax. Although this crate contains a `Builder` derive macro that +/// works well with this macro, this macro is also compatible with other crates +/// such as [bon](https://bon-rs.com/), [buildstructor](https://crates.io/crates/buildstructor), +/// or [typed-builder](https://crates.io/crates/typed-builder). +/// +/// # Examples +/// +/// ``` +/// use feluments::*; +/// +/// #[derive(Debug, PartialEq, Eq, Builder)] +/// #[builder(vis = pub)] +/// struct Foo { +/// #[builder(default = 45)] +/// x: i32, +/// #[builder(into)] +/// y: String, +/// #[builder(optional)] +/// z: () +/// } +/// +/// # fn main() { +/// assert_eq!( +/// build!(Foo { x: 32, y: "baz" }), +/// Foo { x: 32, y: "baz".into(), z: () }, +/// ); +/// # } +/// ``` #[proc_macro] pub fn build(input: TokenStream) -> TokenStream { let structure = parse_macro_input!(input as ExprStruct); diff --git a/tests/basic.rs b/tests/basic.rs index 68cefb7..5031b02 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -1,4 +1,4 @@ -use auguments::{Builder, build}; +use feluments::{Builder, build}; #[derive(Builder)] #[allow(dead_code)] |
