summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-06-05 17:57:27 -0400
committerMica White <botahamec@outlook.com>2026-06-05 17:57:27 -0400
commit67fc414e1e490da951bd0fd037f8ad179a0c0824 (patch)
tree7c1e723fb3bb730eb7be3c6bb14714687a895982
parenta994d9acd6a7083349b930d6b08b25b28792344f (diff)
Documentation
-rw-r--r--Cargo.lock20
-rw-r--r--Cargo.toml2
-rw-r--r--src/lib.rs159
-rw-r--r--tests/basic.rs2
4 files changed, 163 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 331ab82..ffd45cf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 449a8f1..8310d92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "auguments"
+name = "feluments"
version = "0.1.0"
edition = "2024"
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);
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)]