summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs147
1 files changed, 131 insertions, 16 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 0289b15..315dd73 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -59,7 +59,10 @@ use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{
- Expr, ExprStruct, Ident, ItemStruct, LitBool, Type, Visibility, parse_macro_input, token::Pub,
+ Expr, ExprStruct, Ident, ItemStruct, LitBool, Path, Token, Type, Visibility,
+ parse::{Parse, ParseStream},
+ parse_macro_input,
+ token::Pub,
};
enum Default {
@@ -89,13 +92,16 @@ struct BuilderFieldOptions {
#[from_attr(ident = builder)]
struct BuilderOptions {
vis: Option<Visibility>,
+ makro: bool,
}
/// 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 the struct, it may take the
+/// following arguments:
+/// - `vis`: The visibility of the `builder` method.
+/// - `makro`: Creates a macro to construct an instance of the struct.
///
/// When the `builder` attribute is applied to a field of the struct, there are
/// several arguments it may take:
@@ -115,7 +121,7 @@ struct BuilderOptions {
/// use feluments::Builder;
///
/// #[derive(Debug, PartialEq, Eq, Builder)]
-/// #[builder(vis = pub)]
+/// #[builder(vis = pub, makro)]
/// struct Foo {
/// #[builder(default = 45)]
/// x: i32,
@@ -130,6 +136,10 @@ struct BuilderOptions {
/// Foo::builder().y("bar").z(()).build(),
/// Foo { x: 45, y: "bar".into(), z: () },
/// );
+/// assert_eq!(
+/// Foo! { y: "bar", z: () },
+/// Foo { x: 45, y: "bar".into(), z: () },
+/// )
/// # }
/// ```
///
@@ -158,18 +168,28 @@ pub fn derive_builder(input: TokenStream) -> TokenStream {
})
.collect::<Box<_>>();
- 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 builder_options = BuilderOptions::from_attributes(structure.attrs).unwrap();
+ let is_builder_visible = matches!(builder_options.vis, Some(Visibility::Public(_)))
+ || fields
+ .iter()
+ .all(|field| matches!(field.visibility, Visibility::Public(_)));
+ let builder_macro_vis = is_builder_visible.then(|| quote! { #[macro_export]});
+ let builder_macro = builder_options.makro.then(|| {
+ quote! {
+ #builder_macro_vis
+ macro_rules! #struct_name {
+ ($($t: tt)*) => { ::feluments::build!(#struct_name { $($t)* })}
+ }
+ }
+ });
+ let builder_visibility = builder_options.vis.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())
@@ -262,6 +282,8 @@ pub fn derive_builder(input: TokenStream) -> TokenStream {
#(#field_names: Option<#field_types>,)*
}
+ #builder_macro
+
impl<#(const #const_generics: bool),*> core::default::Default for #builder_name<#(#const_generics),*> {
fn default() -> Self {
Self {
@@ -339,3 +361,96 @@ pub fn build(input: TokenStream) -> TokenStream {
}
.into()
}
+
+/// Creates a declarative macro to construct a type that derives [`Builder`]
+/// using a constructor literal syntax.
+///
+/// This is used to create convenience macros 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: ()
+/// }
+///
+/// // If the `pub` keyword is used, then #[macro_export] is applied
+/// builder_macro!(pub Foo = Foo);
+///
+/// # fn main() {
+/// assert_eq!(
+/// build!(Foo { x: 32, y: "baz" }),
+/// Foo { x: 32, y: "baz".into(), z: () },
+/// );
+/// # }
+/// ```
+#[proc_macro]
+pub fn builder_macro(input: TokenStream) -> TokenStream {
+ struct BuilderAssignment {
+ vis: Visibility,
+ left: Ident,
+ _equal: Token![=],
+ metavar: Option<Token![$]>,
+ metacrate: Option<Token![crate]>,
+ path_sep: Option<Token![::]>,
+ right: Path,
+ }
+
+ impl Parse for BuilderAssignment {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let vis = input.parse()?;
+ let left = input.parse()?;
+ let _equal = input.parse()?;
+ let metavar = input.parse()?;
+ let metacrate = input.parse()?;
+ let path_sep = input.parse()?;
+ let right = input.parse()?;
+
+ Ok(Self {
+ vis,
+ left,
+ _equal,
+ metavar,
+ metacrate,
+ path_sep,
+ right,
+ })
+ }
+ }
+
+ let assignment = parse_macro_input!(input as BuilderAssignment);
+ let attribute = match assignment.vis {
+ Visibility::Public(_) => Some(quote! { #[macro_export] }),
+ Visibility::Inherited => None,
+ Visibility::Restricted(_) => panic!("Expected visibility to be `pub`"),
+ };
+ let builder_name = assignment.left;
+ let BuilderAssignment {
+ metavar,
+ metacrate,
+ path_sep,
+ ..
+ } = assignment;
+ let path = assignment.right;
+
+ quote! {
+ #attribute
+ macro_rules! #builder_name {
+ ($($t: tt)*) => { ::feluments::build!(#metavar #metacrate #path_sep #path { $($t)* }) };
+ }
+ }
+ .into()
+}