summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs159
1 files changed, 151 insertions, 8 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 056b6f1..0289b15 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);