+
+
+
+
+# Overview
+
+`bon` is a Rust crate for generating compile-time-checked builders for structs and functions. It also provides idiomatic partial application with optional and named parameters for functions and methods.
+
+If you wonder "Why would I use builders?", see the [motivational blog post](https://bon-rs.com/blog/how-to-do-named-function-arguments-in-rust).
+
+## Function Builder
+
+You can turn a function with positional parameters into a function with named parameters with `#[builder]`.
+
+```rust
+use bon::builder;
+
+#[builder]
+fn greet(name: &str, level: Option) -> String {
+ let level = level.unwrap_or(0);
+
+ format!("Hello {name}! Your level is {level}")
+}
+
+let greeting = greet()
+ .name("Bon")
+ .level(24) // <- setting `level` is optional, we could omit it
+ .call();
+
+assert_eq!(greeting, "Hello Bon! Your level is 24");
+```
+
+Any syntax for functions is supported including `async`, fallible, generic functions, `impl Trait`, etc.
+
+Many things are customizable with additional attributes described in the [API reference](https://bon-rs.com/reference/builder), but let's see what else `bon` offers.
+
+## Struct Builder
+
+Use `#[derive(Builder)]` to generate a builder for a struct.
+
+```rust
+use bon::Builder;
+
+#[derive(Builder)]
+struct User {
+ name: String,
+ is_admin: bool,
+ level: Option,
+}
+
+let user = User::builder()
+ .name("Bon".to_owned())
+ // `level` is optional, we could omit it here
+ .level(24)
+ // call setters in any order
+ .is_admin(true)
+ .build();
+
+assert_eq!(user.name, "Bon");
+assert_eq!(user.level, Some(24));
+assert!(user.is_admin);
+```
+
+## Method Builder
+
+Associated methods require `#[bon]` on top of the impl block additionally.
+
+### Method `new`
+
+The method named `new` generates `builder()/build()` methods.
+
+```rust
+use bon::bon;
+
+struct User {
+ id: u32,
+ name: String,
+}
+
+#[bon]
+impl User {
+ #[builder]
+ fn new(id: u32, name: String) -> Self {
+ Self { id, name }
+ }
+}
+
+let user = User::builder()
+ .id(1)
+ .name("Bon".to_owned())
+ .build();
+
+assert_eq!(user.id, 1);
+assert_eq!(user.name, "Bon");
+```
+
+`#[derive(Builder)]` on a struct generates builder API that is fully compatible with placing `#[builder]` on the `new()` method with a signature similar to the struct's fields (more details on the [Compatibility](https://bon-rs.com/guide/misc/compatibility#switching-between-derive-builder-and-builder-on-the-new-method) page).
+
+### Other Methods
+
+All other methods generate `{method_name}()/call()` methods.
+
+```rust
+use bon::bon;
+
+struct Greeter {
+ name: String,
+}
+
+#[bon]
+impl Greeter {
+ #[builder]
+ fn greet(&self, target: &str, prefix: Option<&str>) -> String {
+ let prefix = prefix.unwrap_or("INFO");
+ let name = &self.name;
+
+ format!("[{prefix}] {name} says hello to {target}")
+ }
+}
+
+let greeter = Greeter { name: "Bon".to_owned() };
+
+let greeting = greeter
+ .greet()
+ .target("the world")
+ // `prefix` is optional, omitting it is fine
+ .call();
+
+assert_eq!(greeting, "[INFO] Bon says hello to the world");
+```
+
+Methods with or without `self` are both supported.
+
+## No Panics Possible
+
+Builders generated by `bon`'s macros use the typestate pattern to ensure all required parameters are filled, and the same setters aren't called repeatedly to prevent unintentional overwrites.
+
+If something is wrong, a compile error will be created. No matter how you use the generated builder a panic is never possible.
+
+| ⭐ Don't forget to give our repo a [star on Github ⭐](https://github.com/elastio/bon)! |
+| --- |
+
+## What's Next?
+
+What you've seen above is the first page of the 📖 [Guide Book](https://bon-rs.com/guide/overview). Consider reading the `Basics` section. Begin [here](https://bon-rs.com/guide/basics/optional-members) with the optional/default values topic. Remember, knowledge is power 🐱!
+
+Feel free to jump to code and use the `#[builder]` and `#[derive(Builder)]` once you've seen enough docs to get started.
+
+The [🔍 API Reference](https://bon-rs.com/reference/builder) will help you navigate the attributes once you feel comfortable with the basics of `bon`. Both `#[derive(Builder)]` on structs and `#[builder]` on functions/methods have almost identical attributes API, so the documentation for them is common.
+
+## Installation
+
+Add `bon` to your `Cargo.toml`.
+
+```toml
+[dependencies]
+bon = "2.3"
+```
+
+You can opt out of `std` and `alloc` cargo features with `default-features = false` for `no_std` environments.
+
+## Getting Help
+
+If you can't figure something out, consult the docs and maybe use the `🔍 Search` bar on our [docs website](https://bon-rs.com). You may also create an issue or a discussion on the [Github repository](https://github.com/elastio/bon) for help or write us a message on [Discord](https://bon-rs.com/discord).
+
+## Socials
+
+
+
Profile of the maintainer. There are only posts about bon and Rust in general here.
+
+
+
+
+## Acknowledgments
+
+This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them.
+
+See [alternatives](https://bon-rs.com/guide/misc/alternatives) for comparison.
+
+## License
+
+
+Licensed under either of Apache License, Version
+2.0 or MIT license at your option.
+
+
+
+
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+
+
diff --git a/bon-macros/src/builder/builder_gen/member/config/mod.rs b/bon-macros/src/builder/builder_gen/member/config/mod.rs
index 033774da..ebe0455d 100644
--- a/bon-macros/src/builder/builder_gen/member/config/mod.rs
+++ b/bon-macros/src/builder/builder_gen/member/config/mod.rs
@@ -20,7 +20,7 @@ pub(crate) struct MemberConfig {
/// Assign a default value to the member it it's not specified.
///
/// An optional expression can be provided to set the value for the member,
- /// otherwise its [`Default`] trait impl will be used.
+ /// otherwise its [`Default`] trait impl will be used.
#[darling(with = parse_optional_expr, map = Some)]
pub(crate) default: Option>>,
diff --git a/bon-macros/src/builder/builder_gen/mod.rs b/bon-macros/src/builder/builder_gen/mod.rs
index db1b5b11..a90f263c 100644
--- a/bon-macros/src/builder/builder_gen/mod.rs
+++ b/bon-macros/src/builder/builder_gen/mod.rs
@@ -368,7 +368,7 @@ impl BuilderGenCtx {
if you found yourself needing it, then you are probably doing something wrong; \
feel free to open an issue/discussion in our GitHub repository \
(https://github.com/elastio/bon) or ask for help in our Discord server \
- (https://discord.gg/QcBYSamw4c)";
+ (https://bon-rs.com/discord)";
quote! {
#[doc(hidden)]
diff --git a/bon-macros/src/builder/builder_gen/state_mod.rs b/bon-macros/src/builder/builder_gen/state_mod.rs
index 170fbb35..e380d95a 100644
--- a/bon-macros/src/builder/builder_gen/state_mod.rs
+++ b/bon-macros/src/builder/builder_gen/state_mod.rs
@@ -233,8 +233,8 @@ impl<'a> StateModGenCtx<'a> {
let docs = format!(
"Marker trait that indicates that all required members are set.\n\n\
- In this state, you can finish the building by calling the method \
- [`{builder_ident}::{finish_fn}()`]",
+ In this state, you can finish building by calling the method \
+ [`{builder_ident}::{finish_fn}()`](super::{builder_ident}::{finish_fn}())",
);
quote! {
diff --git a/bon-macros/src/util/ide.rs b/bon-macros/src/util/ide.rs
index f869c4ab..5d5ab87d 100644
--- a/bon-macros/src/util/ide.rs
+++ b/bon-macros/src/util/ide.rs
@@ -153,7 +153,16 @@ pub(crate) fn generate_completion_triggers(meta: Vec) -> TokenStream {
CompletionsSchema::leaf("finish_fn"),
CompletionsSchema::leaf("state_mod"),
CompletionsSchema::leaf("on").set_custom_filter(|meta| {
- if !meta.is_empty() {
+ if let Some(first) = meta.first() {
+ if let Meta::Path(path) = first {
+ if path.is_ident("into")
+ || path.is_ident("transparent")
+ || path.is_ident("overwritable")
+ {
+ return;
+ }
+ }
+
meta.remove(0);
}
}),
diff --git a/bon/Cargo.toml b/bon/Cargo.toml
index 84a88077..71f42c6a 100644
--- a/bon/Cargo.toml
+++ b/bon/Cargo.toml
@@ -14,7 +14,7 @@ categories = [
keywords = ["builder", "macro", "derive", "constructor", "setter"]
edition = "2021"
-homepage = "https://bon-rs.com/"
+homepage = "https://bon-rs.com"
license = "MIT OR Apache-2.0"
repository = "https://github.com/elastio/bon"
diff --git a/bon/src/__/ide.rs b/bon/src/__/ide.rs
index 8babac5e..59ddcb50 100644
--- a/bon/src/__/ide.rs
+++ b/bon/src/__/ide.rs
@@ -8,87 +8,93 @@
pub mod builder_top_level {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const builder_type: Option = None;
pub mod builder_type {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const name: Identifier = Identifier;
- /// See the docs at
+ /// See the docs at
pub const vis: VisibilityString = VisibilityString;
- /// See the docs at
+ /// See the docs at
pub const doc: DocComments = DocComments;
}
- /// See the docs at
+ /// See the docs at
pub const finish_fn: Option = None;
- /// See the docs at
+ /// See the docs at
pub mod finish_fn {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const name: Identifier = Identifier;
- /// See the docs at
+ /// See the docs at
pub const vis: VisibilityString = VisibilityString;
- /// See the docs at
+ /// See the docs at
pub const doc: DocComments = DocComments;
}
- /// See the docs at
+ /// See the docs at
pub const start_fn: Option = None;
- /// See the docs at
+ /// See the docs at
pub mod start_fn {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const name: Identifier = Identifier;
- /// See the docs at
+ /// See the docs at
pub const vis: VisibilityString = VisibilityString;
- /// See the docs at
+ /// See the docs at
pub const doc: DocComments = DocComments;
}
- /// See the docs at
+ /// See the docs at
pub const state_mod: Option = None;
- /// See the docs at
+ /// See the docs at
pub mod state_mod {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const name: Identifier = Identifier;
- /// See the docs at
+ /// See the docs at
pub const vis: VisibilityString = VisibilityString;
- /// See the docs at
+ /// See the docs at
pub const doc: DocComments = DocComments;
}
- /// See the docs at
+ /// See the docs at
pub mod on {
use super::*;
- /// See the docs at
+ /// See the docs at
pub const into: Flag = Flag;
+
+ /// See the docs at
+ pub const transparent: Flag = Flag;
+
+ /// See the docs at
+ pub const overwritable: Flag = Flag;
}
- /// See the docs at
+ /// See the docs at
pub mod derive {
- /// See the docs at
+ /// See the docs at
pub use core::fmt::Debug;
- /// See the docs at
+ /// See the docs at
pub use core::clone::Clone;
}
@@ -96,7 +102,7 @@ pub mod builder_top_level {
/// It's hinted with an underscore due to the limitations of the current
/// completions limitation. This will be fixed in the future.
///
- /// See the docs at
+ /// See the docs at
pub const crate_: Option = None;
}
diff --git a/bon/src/examples/comprehensive.rs b/bon/src/examples/comprehensive.rs
new file mode 100644
index 00000000..4333f590
--- /dev/null
+++ b/bon/src/examples/comprehensive.rs
@@ -0,0 +1,27 @@
+//! Comprehensive example of the generated builder and its typestate API.
+//!
+//! The preliminary reading of [Typestate API](https://bon-rs.com/guide/typestate-api)
+//! guide is recommended to understand how the pieces in this example fit together.
+//!
+//! This module contains a struct [`Example`] that was annotated with [`#[derive(Builder)]`](crate::Builder).
+//! The config [`#[builder(state_mod(vis = "pub"))]`](https://bon-rs.com/reference/builder/top-level/state_mod)
+//! was applied to make the generated builder's typestate API public and visible here in the docs.
+//!
+//! The following was generated by the macro:
+//! - [`ExampleBuilder`] - the builder struct itself
+//! - [`example_builder`] - the builder's typestate API module
+
+/// Example struct with the `#[derive(Builder)]` annotation.
+#[derive(crate::Builder)]
+#[builder(crate = crate, state_mod(vis = "pub"))]
+pub struct Example {
+ /// Example required member
+ x1: u32,
+
+ /// Example optional member
+ x2: Option,
+
+ /// Example member with a default value.
+ #[builder(default = 2 + 2)]
+ x3: u32,
+}
diff --git a/bon/src/examples/minimal.rs b/bon/src/examples/minimal.rs
new file mode 100644
index 00000000..751c9fb9
--- /dev/null
+++ b/bon/src/examples/minimal.rs
@@ -0,0 +1,14 @@
+//! Minimal example of the generated builder and its typestate API.
+//!
+//! This documentation was generated as a showcase for the [Builder's Type Signature]
+//! guide
+//!
+//! [Builder's Type Signature]: https://bon-rs.com/guide/typestate-api/builders-type-signature
+
+/// Example struct with the `#[derive(Builder)]` annotation.
+#[derive(crate::Builder)]
+#[builder(crate = crate, state_mod(vis = "pub"))]
+pub struct Example {
+ x1: u32,
+ x2: u32,
+}
diff --git a/bon/src/examples/mod.rs b/bon/src/examples/mod.rs
new file mode 100644
index 00000000..4192a67c
--- /dev/null
+++ b/bon/src/examples/mod.rs
@@ -0,0 +1,2 @@
+pub mod comprehensive;
+pub mod minimal;
diff --git a/bon/src/lib.rs b/bon/src/lib.rs
index 36f6a57c..9076d325 100644
--- a/bon/src/lib.rs
+++ b/bon/src/lib.rs
@@ -22,7 +22,11 @@ mod collections;
they should not be used directly; if you found a need for this, then you are probably \
doing something wrong; feel free to open an issue/discussion in our GitHub repository \
(https://github.com/elastio/bon) or ask for help in our Discord server \
- (https://discord.gg/QcBYSamw4c)"]
+ (https://bon-rs.com/discord)"]
pub mod __;
mod builder_state;
+
+/// Examples of generated builders' APIs.
+#[cfg(doc)]
+pub mod examples;
diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts
index c6972e62..aeaa51c0 100644
--- a/website/.vitepress/config.mts
+++ b/website/.vitepress/config.mts
@@ -1,8 +1,25 @@
-import { defineConfig } from "vitepress";
+import { defineConfig, HeadConfig } from "vitepress";
import { abbr } from "@mdit/plugin-abbr";
import * as v1 from "../src/v1/config.mjs";
import * as v2 from "../src/v2/config.mjs";
+const head: HeadConfig[] = [
+ ["link", { rel: "icon", href: `bon-logo-thumb.png` }],
+ ["meta", { property: "og:image", content: `bon-logo-thumb.png` }],
+];
+
+// Enable analytics only in the final build on CI. Locally, it's not needed.
+if (process.env.CI) {
+ head.push([
+ "script",
+ {
+ defer: "",
+ src: "https://umami.bon-rs.com/script.js",
+ "data-website-id": "10c1ad05-7a6e-49ee-8633-5f8f75de4ab9",
+ },
+ ]);
+}
+
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Bon",
@@ -46,6 +63,7 @@ export default defineConfig({
markdown: {
languageAlias: {
+ // Attributes highlighting works better with JS tokenizer 😳
attr: "js",
},
@@ -62,25 +80,10 @@ export default defineConfig({
srcExclude: ["README.md", "infra/**"],
- head: [
- ["link", { rel: "icon", href: `bon-logo-thumb.png` }],
- ["meta", { property: "og:image", content: `bon-logo-thumb.png` }],
- [
- "script",
- {
- defer: "",
- src: "https://umami.bon-rs.com/script.js",
- "data-website-id": "10c1ad05-7a6e-49ee-8633-5f8f75de4ab9",
- },
- ],
- ],
+ head,
srcDir: "src",
- rewrites: {
- "guide/:subdir/:page": "guide/:page",
- },
-
// https://vitepress.dev/reference/default-theme-config
themeConfig: {
logo: "/bon-logo-thumb.png",
@@ -98,9 +101,13 @@ export default defineConfig({
text: "Edit this page on GitHub",
},
- search: {
- provider: "local",
- },
+ // Enable the search only in the final build on CI. Locally, it takes additional
+ // time during the dev HMR server startup and config reloads.
+ search: !process.env.CI
+ ? undefined
+ : {
+ provider: "local",
+ },
nav: [
{ text: "Guide", link: "/guide/overview" },
@@ -111,7 +118,7 @@ export default defineConfig({
socialLinks: [
{ icon: "github", link: "https://github.com/elastio/bon" },
- { icon: "discord", link: "https://discord.gg/QcBYSamw4c" },
+ { icon: "discord", link: "https://bon-rs.com/discord" },
{ icon: "x", link: "https://x.com/veetaha" },
],
@@ -120,56 +127,70 @@ export default defineConfig({
...v2.sidebars,
"/guide": [
{
- text: "Guide",
+ text: "Overview",
+ link: "/guide/overview",
+ },
+ {
+ text: "Basics",
items: [
{
- text: "Overview",
- link: "/guide/overview",
+ text: "Optional Members",
+ link: "/guide/basics/optional-members",
},
{
- text: "Optional Members",
- link: "/guide/optional-members",
+ text: "Into Conversions",
+ link: "/guide/basics/into-conversions",
},
{
- text: "Customizing Setters",
- link: "/guide/customizing-setters",
+ text: "Custom Conversions",
+ link: "/guide/basics/custom-conversions",
},
{
text: "Positional Members",
- link: "/guide/positional-members",
+ link: "/guide/basics/positional-members",
},
{
text: "Inspecting",
- link: "/guide/inspecting",
+ link: "/guide/basics/inspecting",
},
{
text: "Documenting",
- link: "/guide/documenting",
+ link: "/guide/basics/documenting",
},
+ ],
+ },
+ {
+ text: "Typestate API",
+ link: "/guide/typestate-api",
+ items: [
{
- text: "Builder Extensions",
- link: "/guide/builder-extensions",
+ text: "Builder's Type Signature",
+ link: "/guide/typestate-api/builders-type-signature",
+ },
+ {
+ text: "Custom Methods",
+ link: "/guide/typestate-api/custom-methods",
}
- ]
+ ],
},
{
text: "Patterns",
items: [
{
text: "Conditional Building",
- link: "/guide/conditional-building",
+ link: "/guide/patterns/conditional-building",
},
{
text: "Fallible Builders",
- link: "/guide/fallible-builders",
+ link: "/guide/patterns/fallible-builders",
},
{
text: "Into Conversions In-Depth",
- link: "/guide/into-conversions-in-depth",
+ link: "/guide/patterns/into-conversions-in-depth",
},
{
text: "Shared Configuration",
- link: "/guide/shared-configuration",
+ link: "/guide/patterns/shared-configuration",
},
],
},
@@ -178,23 +199,23 @@ export default defineConfig({
items: [
{
text: "Compatibility",
- link: "/guide/compatibility",
+ link: "/guide/misc/compatibility",
},
{
text: "Limitations",
- link: "/guide/limitations",
+ link: "/guide/misc/limitations",
},
{
text: "Benchmarks",
- link: "/guide/benchmarks",
+ link: "/guide/misc/benchmarks",
},
{
text: "Alternatives",
- link: "/guide/alternatives",
+ link: "/guide/misc/alternatives",
},
{
text: "Troubleshooting",
- link: "/guide/troubleshooting",
+ link: "/guide/misc/troubleshooting",
},
],
},
@@ -203,7 +224,7 @@ export default defineConfig({
items: [
{
text: "Contributing",
- link: "/guide/contributing",
+ link: "/guide/internal/contributing",
},
],
},
@@ -220,7 +241,7 @@ export default defineConfig({
items: [
{
text: "builder_type",
- link: "/reference/builder/top-level/builder-type",
+ link: "/reference/builder/top-level/builder_type",
},
{
text: "crate",
@@ -232,7 +253,7 @@ export default defineConfig({
},
{
text: "finish_fn",
- link: "/reference/builder/top-level/finish-fn",
+ link: "/reference/builder/top-level/finish_fn",
},
{
text: "on",
@@ -240,11 +261,11 @@ export default defineConfig({
},
{
text: "start_fn",
- link: "/reference/builder/top-level/start-fn",
+ link: "/reference/builder/top-level/start_fn",
},
{
text: "state_mod",
- link: "/reference/builder/top-level/state-mod",
+ link: "/reference/builder/top-level/state_mod",
},
],
},
@@ -259,7 +280,7 @@ export default defineConfig({
},
{
text: "finish_fn",
- link: "/reference/builder/member/finish-fn",
+ link: "/reference/builder/member/finish_fn",
},
{
text: "into",
@@ -283,7 +304,7 @@ export default defineConfig({
},
{
text: "start_fn",
- link: "/reference/builder/member/start-fn",
+ link: "/reference/builder/member/start_fn",
},
{
text: "transparent",
@@ -295,10 +316,6 @@ export default defineConfig({
},
],
},
- {
- text: "Typestate API",
- link: "/reference/builder/typestate-api",
- },
],
},
{
diff --git a/website/data/version.data.ts b/website/data/version.data.ts
deleted file mode 100644
index 27afdbdf..00000000
--- a/website/data/version.data.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import toml from "smol-toml";
-import fs from "node:fs/promises";
-
-export default {
- watch: ['../../bon/Cargo.toml'],
- async load([cargoTomlPath]: [string]) {
- const cargoTomlContent = await fs.readFile(cargoTomlPath, "utf-8");
- const cargoToml = toml.parse(cargoTomlContent) as any;
- return cargoToml.package.version;
- }
-}
diff --git a/website/package-lock.json b/website/package-lock.json
index 2d03cda3..cacbda17 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -18,10 +18,9 @@
"htmlparser2": "^9.1.0",
"leven": "^4.0.0",
"medium-zoom": "^1.1.0",
- "smol-toml": "^1.3.0",
"ts-node": "^10.9.2",
"ts-pattern": "^5.5.0",
- "vitepress": "^1.4.2"
+ "vitepress": "^1.4.5"
}
},
"node_modules/@algolia/autocomplete-core": {
@@ -2671,18 +2670,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/smol-toml": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.0.tgz",
- "integrity": "sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==",
- "dev": true,
- "engines": {
- "node": ">= 18"
- },
- "funding": {
- "url": "https://github.com/sponsors/cyyynthia"
- }
- },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -3019,9 +3006,9 @@
}
},
"node_modules/vitepress": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.4.2.tgz",
- "integrity": "sha512-10v92Lqx0N4r7YC3cQLBvu+gRS2rHviE7vgdKiwlupUGfSWkyiQDqYccxM5iPStDGSi1Brnec1lf+lmhaQcZXw==",
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.4.5.tgz",
+ "integrity": "sha512-9K0k8kvdEbeowVCpKF/x0AySSq0Pr9pM8xufLgQcKMjsifwxtDjXJjcFhZv4LYw2dcpdYiBq2j7PnWi0tCaMCg==",
"dev": true,
"dependencies": {
"@docsearch/css": "^3.6.2",
diff --git a/website/package.json b/website/package.json
index 5ac9560d..51a11a5f 100644
--- a/website/package.json
+++ b/website/package.json
@@ -17,10 +17,9 @@
"htmlparser2": "^9.1.0",
"leven": "^4.0.0",
"medium-zoom": "^1.1.0",
- "smol-toml": "^1.3.0",
"ts-node": "^10.9.2",
"ts-pattern": "^5.5.0",
- "vitepress": "^1.4.2"
+ "vitepress": "^1.4.5"
},
"dependencies": {
"vue": "^3.5.12"
diff --git a/website/src/blog/bon-builder-generator-v2-release.md b/website/src/blog/bon-builder-generator-v2-release.md
index d29e0632..d029403d 100644
--- a/website/src/blog/bon-builder-generator-v2-release.md
+++ b/website/src/blog/bon-builder-generator-v2-release.md
@@ -60,11 +60,11 @@ Now the documentation was split into the ["Guide"](../guide/overview) and ["Refe
We added 3 new pages with guides on how to use builders idiomatically or solve some common problems (e.g. validating inputs):
-- [Conditional Building](../guide/conditional-building)
-- [Fallible Builders](../guide/fallible-builders)
-- [Into Conversions In-Depth](../guide/into-conversions-in-depth)
+- [Conditional Building](../guide/patterns/conditional-building)
+- [Fallible Builders](../guide/patterns/fallible-builders)
+- [Into Conversions In-Depth](../guide/patterns/into-conversions-in-depth)
-I recommend you to check out the ["Into Conversions In-Depth"](../guide/into-conversions-in-depth) especially because it's highly related to one of the breaking changes that we'll review below.
+I recommend you to check out the ["Into Conversions In-Depth"](../guide/patterns/into-conversions-in-depth) especially because it's highly related to one of the breaking changes that we'll review below.
## Breaking changes
@@ -72,7 +72,7 @@ I recommend you to check out the ["Into Conversions In-Depth"](../guide/into-con
This has been a topic of [controversy](https://github.com/elastio/bon/issues/15), but finally, we aligned on the decision to remove the magical automatic `Into` conversions.
-The main reason for removing this is to make `bon` more obvious and intuitive. Rust's core pillar is "being explicit". By having automatic `Into` conversions `bon` v1 introduced magical implicit behaviour, that also could lead to some footguns. For a detailed explanation of the potential footguns, see the ["Into Conversions In-Depth"](../guide/into-conversions-in-depth) page.
+The main reason for removing this is to make `bon` more obvious and intuitive. Rust's core pillar is "being explicit". By having automatic `Into` conversions `bon` v1 introduced magical implicit behaviour, that also could lead to some footguns. For a detailed explanation of the potential footguns, see the ["Into Conversions In-Depth"](../guide/patterns/into-conversions-in-depth) page.
Now, if you want to enable `Into` conversions for a set of members, you can use the new [`#[builder(on(type_pattern, into))]`](../reference/builder/top-level/on) attribute. It allows you to specify the type that you want to enable `Into` conversions for explicitly.
diff --git a/website/src/blog/bon-builder-v2-2-release.md b/website/src/blog/bon-builder-v2-2-release.md
index fcd09799..1712fd92 100644
--- a/website/src/blog/bon-builder-v2-2-release.md
+++ b/website/src/blog/bon-builder-v2-2-release.md
@@ -61,9 +61,9 @@ cargo fmt
A new attribute is now supported at the top level. You can add [`#[builder(derive(...))]`](../reference/builder/top-level/derive) to ask `bon` to generate implementations of `Clone` or `Debug` for the builder.
-This helps with reusing [partial builders](../guide/conditional-building#shared-partial-builder), because now you can clone the builder where only part of the fields are set.
+This helps with reusing [partial builders](../guide/patterns/conditional-building#shared-partial-builder), because now you can clone the builder where only part of the fields are set.
-The `Debug` derive allows you to [inspect](../guide/inspecting) the builder state for debugging purposes.
+The `Debug` derive allows you to [inspect](../guide/basics/inspecting) the builder state for debugging purposes.
**Example:**
@@ -222,7 +222,7 @@ If you like or dislike this change in syntax feel free to write a comment on Red
## Summary
-We are listening to your feedback! If you'd like to propose a change in `bon`, or ask a question, or just say "thank you", consider joining our [newly launched Discord server](https://discord.gg/QcBYSamw4c)!
+We are listening to your feedback! If you'd like to propose a change in `bon`, or ask a question, or just say "thank you", consider joining our [newly launched Discord server](https://bon-rs.com/discord)!
Also, a huge thank you for 750 stars ⭐ [on Github](https://github.com/elastio/bon)! Consider giving [`bon`] a star if you haven't already. Your support and feedback are a big motivation and together we can build a better builder 🐱!
diff --git a/website/src/blog/bon-builder-v2-3-release.md b/website/src/blog/bon-builder-v2-3-release.md
index 9d8447dd..9a889513 100644
--- a/website/src/blog/bon-builder-v2-3-release.md
+++ b/website/src/blog/bon-builder-v2-3-release.md
@@ -22,7 +22,7 @@ If you don't know about [`bon`], then see the [motivational blog post](./how-to-
### Positional arguments in starting and finishing functions
-While having the ability to use separate setters for the members gives you a ton of flexibility and extensibility described on the ["Compatibility"](../guide/compatibility) page, sometimes you don't need all of that.
+While having the ability to use separate setters for the members gives you a ton of flexibility and extensibility described on the ["Compatibility"](../guide/misc/compatibility) page, sometimes you don't need all of that.
Maybe you'd like to pick out some specific members and let the user pass their values as positional parameters to the starting function that creates the builder or to the finishing function that consumes it. This reduces the syntax a bit at the cost of some extensibility loss ⚖️, but it may be worth it!
@@ -32,8 +32,8 @@ As an example, suppose we have a `Treasure` struct with `x` and `y` coordinates
To do that we can use the `#[builder(start_fn)]` attribute. There are two contexts where we can place it, and they both have a different meaning:
-- [Top-level `#[builder(start_fn = ...)]`](../reference/builder/top-level/start-fn) - configures the name of the starting function and optionally its visibility
-- [Member-level `#[builder(start_fn)]`](../reference/builder/member/start-fn) - configures the member to be a positional parameter on the starting function
+- [Top-level `#[builder(start_fn = ...)]`](../reference/builder/top-level/start_fn) - configures the name of the starting function and optionally its visibility
+- [Member-level `#[builder(start_fn)]`](../reference/builder/member/start_fn) - configures the member to be a positional parameter on the starting function
We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure `x` and `y` as positional parameters on the starting function as well.
@@ -78,7 +78,7 @@ impl Treasure {
Now let's say we need to know the person who claimed the `Treasure`. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process.
-We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder/top-level/finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder/member/finish-fn) attributes to do that.
+We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder/top-level/finish_fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder/member/finish_fn) attributes to do that.
**Example:**
@@ -115,7 +115,7 @@ assert_eq!(treasure.claimed_by_first_name, "Lyra"); // [!code highlight]
assert_eq!(treasure.claimed_by_last_name, "Heartstrings"); // [!code highlight]
```
-You may also combine these attributes with [`#[builder(into)]`](../reference/builder/member/into) or [`#[builder(on(..., into))]`](../reference/builder/top-level/on) to reduce the number of `to_owned()` calls a bit. See this described in detail on the new ["Positional members"](../guide/positional-members#into-conversions) page in the guide.
+The new [Positional Members](../guide/basics/positional-members) page was added to the guide.
### Guaranteed MSRV is 1.59.0 now
@@ -143,7 +143,7 @@ This isn't a breaking change, and the code that uses `#[bon::builder]` on a stru
Huge thank you for 925 stars ⭐ [on Github](https://github.com/elastio/bon)! Consider giving [`bon`] a star if you haven't already. Your support and feedback are a big motivation and together we can build a better builder 🐱!
-Bon's goal is to empower everyone to build beautiful APIs with great flexibility and extensibility. If you have any feedback or ideas for improvement consider joining [our Discord server](https://discord.gg/QcBYSamw4c) to discuss them, or just [open an issue on Github](https://github.com/elastio/bon/issues).
+Bon's goal is to empower everyone to build beautiful APIs with great flexibility and extensibility. If you have any feedback or ideas for improvement consider joining [our Discord server](https://bon-rs.com/discord) to discuss them, or just [open an issue on Github](https://github.com/elastio/bon/issues).
::: tip
diff --git a/website/src/blog/how-to-do-named-function-arguments-in-rust.md b/website/src/blog/how-to-do-named-function-arguments-in-rust.md
index a9a597dc..0a02afa0 100644
--- a/website/src/blog/how-to-do-named-function-arguments-in-rust.md
+++ b/website/src/blog/how-to-do-named-function-arguments-in-rust.md
@@ -146,7 +146,7 @@ User::builder()
.build();
```
-So, you can use just one builder crate solution consistently for everything. Builders for functions and structs both share the same API design, which allows you, for example, to switch between a `#[derive(Builder)]` on a struct and a `#[builder]` attribute on a method that creates a struct. This won't be an API-breaking change for your consumers ([details](../guide/compatibility#switching-between-derive-builder-and-builder-on-the-new-method)).
+So, you can use just one builder crate solution consistently for everything. Builders for functions and structs both share the same API design, which allows you, for example, to switch between a `#[derive(Builder)]` on a struct and a `#[builder]` attribute on a method that creates a struct. This won't be an API-breaking change for your consumers ([details](../guide/misc/compatibility#switching-between-derive-builder-and-builder-on-the-new-method)).
## Summary
diff --git a/website/src/changelog.md b/website/src/changelog.md
index ddc0e7a0..d15651e6 100644
--- a/website/src/changelog.md
+++ b/website/src/changelog.md
@@ -49,7 +49,7 @@ All the breaking changes are very unlikely to actually break your code that was
- ⚠️ **Breaking.** Builder macros now generate additional `mod builder_name {}` where `builder_name` is the snake_case version of the name of the builder struct. This new module contains the type state API of the builder. There is a low probability that this new module name may conflict with existing symbols in your scope, so this change is marked as breaking.
-- Add [`#[builder(builder_type(vis = "...", doc { ... }))]`](https://bon-rs.com/reference/builder/top-level/builder-type) that allows overriding the visibility and docs of the builder struct ([#145](https://github.com/elastio/bon/pull/145))
+- Add [`#[builder(builder_type(vis = "...", doc { ... }))]`](https://bon-rs.com/reference/builder/top-level/builder_type) that allows overriding the visibility and docs of the builder struct ([#145](https://github.com/elastio/bon/pull/145))
- Add [`#[builder(finish_fn(vis = "...", doc { ... } ))]`](https://bon-rs.com/reference/builder/top-level/finish-fn) that allows overriding the visibility and docs of the finishing function ([#145](https://github.com/elastio/bon/pull/145))
diff --git a/website/src/guide/basics/custom-conversions.md b/website/src/guide/basics/custom-conversions.md
new file mode 100644
index 00000000..68e0e8b9
--- /dev/null
+++ b/website/src/guide/basics/custom-conversions.md
@@ -0,0 +1,31 @@
+# Custom Conversions
+
+[`#[builder(into)]`](../../reference/builder/member/into) is great and it works in many cases. However, what if you need to do a conversion that isn't a simple `Into`? What if you want your setter to accept several parameters? What if your setter should be fallible? The answer to all these questions is the bigger brother [`#[builder(with)]`](../../reference/builder/member/with).
+
+You can pass a custom closure to `#[builder(with)]`. It will define the signature of the setter and perform a conversion.
+
+```rust
+use bon::Builder;
+
+struct Point {
+ x: u32,
+ y: u32,
+}
+
+#[derive(Builder)]
+struct Example {
+ #[builder(with = |x: u32, y: u32| Point { x, y })]
+ point: Point,
+}
+
+let value = Example::builder()
+ .point(2, 3)
+ .build();
+
+assert_eq!(value.point.x, 2);
+assert_eq!(value.point.y, 3);
+```
+
+You can also pass a [fallible closure](../../reference/builder/member/with#fallible-setters) and some [well-known functions](../../reference/builder/member/with#well-known-functions) to `#[builder(with)]`.
+
+If your setter needs more complex logic that isn't expressible with `#[builder(with)]` (e.g. mark the setter `unsafe`, set several members at once), then [Custom Methods](../typestate-api/custom-methods) can cover that.
diff --git a/website/src/guide/basics/documenting.md b/website/src/guide/basics/documenting.md
new file mode 100644
index 00000000..6413a496
--- /dev/null
+++ b/website/src/guide/basics/documenting.md
@@ -0,0 +1,70 @@
+# Documenting
+
+In regular Rust, it's not possible to place doc comments on function arguments. But with `#[builder]` it is. Documentation written on the arguments will be placed on the generated setter methods.
+
+**Example:**
+
+```rust
+use bon::builder;
+
+/// Function that returns a greeting special-tailored for a given person
+#[builder]
+fn greet(
+ /// Name of the person to greet.
+ ///
+ /// **Example:**
+ /// ```
+ /// greet().name("John");
+ /// ```
+ name: &str,
+
+ /// Age expressed in full years passed since the birth date.
+ age: u32
+) -> String {
+ format!("Hello {name} with age {age}!")
+}
+```
+
+::: details How does this work? 🤔
+
+This works because Rust compiler checks for invalid placement of `#[doc = ...]` attributes only after the macro expansion stage. `#[builder]` removes the docs from the function's arguments in the expanded code, and instead moves them to the docs on setter methods.
+
+:::
+
+When `#[derive(Builder)]` is placed on top of a struct, then documentation on the struct fields will be copied to the docs on the setter methods.
+
+## Custom `doc` attributes
+
+You can override documentation on other items generated by builder macros. There are multiple attributes accepting a `doc { ... }` block.
+
+```rust
+use bon::Builder;
+
+#[derive(Builder)]
+#[builder(
+ builder_type(doc {
+ /// Custom docs on the builder struct itself
+ }),
+ start_fn(doc {
+ /// Custom docs on the starting function
+ }),
+ // ...
+)]
+struct Example {}
+```
+
+You can document the following items this way:
+
+| Attribute | Documentation target
+|------------------------------------------------------------------------|----------------------
+| [`builder_type`](../../reference/builder/top-level/builder_type#doc) | Builder struct
+| [`start_fn`](../../reference/builder/top-level/start_fn#doc) | Starting function
+| [`finish_fn`](../../reference/builder/top-level/finish_fn#doc) | Finishing function
+| [`state_mod`](../../reference/builder/top-level/state_mod#doc) | Builder state API module (more details in [Typestate API](../typestate-api))
+| [`setters`](../../reference/builder/member/setters#doc) | Custom docs for setters. Prevents copying them from the field/argument
+
+
+
+## Positional members
+
+Documentation comments are allowed on [positional members](./positional-members). However, since there are no separate setter methods generated for them, the docs on these members will not be copied anywhere, and thus they won't appear in `rustdoc`. Instead, it's recommended to write documentation for these members on the top level of the struct or function.
diff --git a/website/src/guide/inspecting.md b/website/src/guide/basics/inspecting.md
similarity index 79%
rename from website/src/guide/inspecting.md
rename to website/src/guide/basics/inspecting.md
index 1872c70b..938a93ec 100644
--- a/website/src/guide/inspecting.md
+++ b/website/src/guide/basics/inspecting.md
@@ -1,6 +1,6 @@
# Inspecting
-If you want to inspect the values set in the builder for debugging purposes you can leverage the [`#[builder(derive(...))]`](../reference/builder/top-level/derive) attribute to derive the [`Debug`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) trait for your builder.
+If you want to inspect the values set in the builder for debugging purposes you can leverage the [`#[builder(derive(...))]`](../../reference/builder/top-level/derive) attribute to derive the [`Debug`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) trait for your builder.
**Example:**
@@ -30,4 +30,4 @@ assert_eq!(
builder.is_admin(true).call();
```
-You can also derive the [`Clone`](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) trait for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../reference/builder/top-level/derive).
+You can also derive the [`Clone`](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) trait for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../../reference/builder/top-level/derive).
diff --git a/website/src/guide/basics/into-conversions.md b/website/src/guide/basics/into-conversions.md
new file mode 100644
index 00000000..e56330af
--- /dev/null
+++ b/website/src/guide/basics/into-conversions.md
@@ -0,0 +1,50 @@
+# `Into` conversions
+
+If you have members of type `String`, or `PathBuf`, and you need to set them to a hard-coded string literal, then you have to write `.to_owned()` or `.to_string()` or `.into()`.
+
+```rust
+use bon::Builder;
+use std::path::PathBuf;
+
+#[derive(Builder)] // [!code focus]
+struct Project { // [!code focus]
+ name: String, // [!code focus]
+ description: String, // [!code focus]
+ path: PathBuf, // [!code focus]
+} // [!code focus]
+
+Project::builder()
+ .name("Bon".to_owned()) // [!code focus]
+ .description("Awesome crate 🐱".to_string()) // [!code focus]
+ .path("/path/to/bon".into()) // [!code focus]
+ .build();
+```
+
+However, you can ask `bon` to generate setters that accept `impl Into` to remove the need for manual conversion.
+
+This can be configured with [`#[builder(into)]`](../../reference/builder/member/into) for a single member or with [`#[builder(on({type}, into))]`](../../reference/builder/top-level/on) for many members at once.
+
+```rust
+use bon::Builder;
+use std::path::PathBuf;
+
+// All setters for members of type `String` will accept `impl Into` // [!code highlight]
+#[derive(Builder)] // [!code highlight]
+#[builder(on(String, into))] // [!code highlight]
+struct Project {
+ name: String,
+ description: String,
+
+ // The setter only for this member will accept `impl Into` // [!code highlight]
+ #[builder(into)] // [!code highlight]
+ path: PathBuf,
+}
+
+Project::builder()
+ .name("Bon") // [!code highlight]
+ .description("Awesome crate 🐱") // [!code highlight]
+ .path("/path/to/your/heart") // [!code highlight]
+ .build();
+```
+
+`Into` conversions don't always make sense, and you should be aware of their downsides as well. The article [Into Conversions In-Depth](../patterns/into-conversions-in-depth) provides recommendations on when it makes sense to use and to avoid `Into` conversions.
diff --git a/website/src/guide/optional-members.md b/website/src/guide/basics/optional-members.md
similarity index 62%
rename from website/src/guide/optional-members.md
rename to website/src/guide/basics/optional-members.md
index 1294e2c4..2ba1556a 100644
--- a/website/src/guide/optional-members.md
+++ b/website/src/guide/basics/optional-members.md
@@ -6,7 +6,7 @@ outline: deep
## `Option`
-Setters generated for members of `Option` type are optional to call. If they aren't invoked, then `None` will be used as the default.
+If your function argument or struct field (or member for short) is of type `Option`, then the generated builder will not enforce setting a value for this member, defaulting to `None`.
```rust
#[bon::builder]
@@ -16,7 +16,7 @@ fn example(level: Option) {}
example().call();
```
-You can use [`#[builder(transparent)]`](../reference/builder/member/transparent) to opt-out from this.
+You can use [`#[builder(transparent)]`](../../reference/builder/member/transparent) to opt-out from this.
### Setters pair
@@ -27,10 +27,10 @@ The builder provides a **pair** of setters for each optional member:
| `{member}` | `T` | Accepts a non-`None` value. | [`some_fn`][setters]
| `maybe_{member}` | `Option` | Accepts an `Option` directly. | [`option_fn`][setters]
-[setters]: ../reference/builder/member/setters
+[setters]: ../../reference/builder/member/setters
-::: details See how the setters look like in the generated code
+::: details See how the setters look in the generated code
```rust ignore
// [GENERATED CODE (simplified)]
@@ -45,7 +45,7 @@ impl ExampleBuilder {
:::
-Thanks to this design, changing the member from required to optional [preserves compatibility](./compatibility#making-a-required-member-optional).
+Thanks to this design, changing the member from required to optional [preserves compatibility](../misc/compatibility#making-a-required-member-optional).
### Examples
@@ -69,16 +69,14 @@ example().maybe_level(value).call();
## `#[builder(default)]`
-To make a member of non-`Option` type optional you may use the attribute [`#[builder(default)]`](../reference/builder/member/default). This attribute uses the [`Default`](https://doc.rust-lang.org/stable/std/default/trait.Default.html) trait or the provided expression to assign the default value for the member.
+To make a member of non-`Option` type optional you may use [`#[builder(default)]`](../../reference/builder/member/default). This attribute uses the [`Default`](https://doc.rust-lang.org/stable/std/default/trait.Default.html) trait or the provided expression to assign the default value for the member.
::: tip
-Switching between `#[builder(default)]` and `Option` is [compatible](./compatibility#switching-between-option-t-and-builder-default).
+Switching between `#[builder(default)]` and `Option` is [compatible](../misc/compatibility#switching-between-option-t-and-builder-default).
:::
-### Examples
-
```rust
#[bon::builder]
fn example(
@@ -105,11 +103,14 @@ The same [pair of optional setters](#setters-pair) is generated for members with
let result = example()
// Pass a non-None value
.a(3)
- // Pass an `Option` value directly
- .maybe_b(Some(5))
+ // Pass an `Option` value directly. `None` means the default
+ // value will be used (4 in this case)
+ .maybe_b(None)
.call();
```
+You can also reference other members in the default expression. See [`#[builder(default)]`](../../reference/builder/member/default#evaluation-context) reference for details.
+
## Conditional building
-Now that you know how optional members work you can check out the ["Conditional building" design patterns](./conditional-building) or continue studying other features of `bon` by following the "Next page" link at the bottom.
+Now that you know how optional members work you can check out the [Conditional building](../patterns/conditional-building) design patterns or continue studying other features of `bon` by following the "Next page" link at the bottom.
diff --git a/website/src/guide/basics/positional-members.md b/website/src/guide/basics/positional-members.md
new file mode 100644
index 00000000..a44741a6
--- /dev/null
+++ b/website/src/guide/basics/positional-members.md
@@ -0,0 +1,79 @@
+# Positional Members
+
+You can let the caller pass some values as positional parameters to the starting function, that creates the builder or to the finishing function, that consumes it.
+
+## Starting function
+
+Use `#[builder(start_fn)]` to move some members to the parameters of the starting function.
+
+```rust
+use bon::Builder;
+
+#[derive(Builder)]
+// Top-level attribute to give a better name for the starting function // [!code highlight]
+#[builder(start_fn = with_coordinates)] // [!code highlight]
+struct Treasure {
+ // Member-level attributes to move members // [!code highlight]
+ // to the parameters of `with_coordinates()` // [!code highlight]
+ #[builder(start_fn)] // [!code highlight]
+ x: u32,
+
+ #[builder(start_fn)] // [!code highlight]
+ y: u32,
+
+ label: Option,
+}
+
+let treasure = Treasure::with_coordinates(2, 9) // [!code highlight]
+ .label("oats".to_owned())
+ .build();
+
+assert_eq!(treasure.x, 2);
+assert_eq!(treasure.y, 9);
+assert_eq!(treasure.label.as_deref(), Some("oats"));
+```
+
+::: tip
+
+There are two versions of the `#[builder(start_fn)]` used here: [top-level](../../reference/builder/top-level/start_fn) and [member-level](../../reference/builder/member/start_fn).
+They have different meanings.
+
+:::
+
+## Finishing function
+
+Use `#[builder(finish_fn)]` to move some members to the parameters of the finishing function.
+
+```rust
+use bon::Builder;
+
+#[derive(Builder)]
+// Top-level attribute to give a better name for the finishing function // [!code highlight]
+#[builder(finish_fn = located_at)] // [!code highlight]
+struct Treasure {
+ // Member-level attributes to move members // [!code highlight]
+ // to the parameters of `located_at()` // [!code highlight]
+ #[builder(finish_fn)] // [!code highlight]
+ x: u32,
+
+ #[builder(finish_fn)] // [!code highlight]
+ y: u32,
+
+ label: Option,
+}
+
+let treasure = Treasure::builder()
+ .label("oats".to_owned())
+ .located_at(2, 9); // [!code highlight]
+
+assert_eq!(treasure.x, 2);
+assert_eq!(treasure.y, 9);
+assert_eq!(treasure.label.as_deref(), Some("oats"));
+```
+
+::: tip
+
+There are two versions of the `#[builder(finish_fn)]` used here: [top-level](../../reference/builder/top-level/finish_fn) and [member-level](../../reference/builder/member/finish_fn).
+They have different meanings.
+
+:::
diff --git a/website/src/guide/builder-extensions.md b/website/src/guide/builder-extensions.md
deleted file mode 100644
index 253c9cb7..00000000
--- a/website/src/guide/builder-extensions.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Builder Extensions
-
-
-## Custom setters
diff --git a/website/src/guide/customizing-setters.md b/website/src/guide/customizing-setters.md
deleted file mode 100644
index ef8086d2..00000000
--- a/website/src/guide/customizing-setters.md
+++ /dev/null
@@ -1 +0,0 @@
-# Customizing Setters
diff --git a/website/src/guide/documenting.md b/website/src/guide/documenting.md
deleted file mode 100644
index aec8207e..00000000
--- a/website/src/guide/documenting.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Documenting
-
-In regular Rust, it's not possible to place doc comments on function arguments. But with `#[builder]` it is. Documentation written on the arguments will be placed on the generated setter methods.
-
-**Example:**
-
-```rust
-use bon::builder;
-
-/// Function that returns a greeting special-tailored for a given person
-#[builder]
-fn greet(
- /// Name of the person to greet.
- ///
- /// **Example:**
- /// ```
- /// greet().name("John");
- /// ```
- name: &str,
-
- /// Age expressed in full years passed since the birth date.
- age: u32
-) -> String {
- format!("Hello {name} with age {age}!")
-}
-```
-
-::: details How does this work? 🤔
-
-This works because Rust compiler checks for invalid placement of `#[doc = ...]` attributes only after the macro expansion stage. `#[builder]` makes sure to remove the docs from the function's arguments in the expanded code, and instead moves them to the docs on setter methods.
-
-:::
-
-When `#[derive(Builder)]` is placed on top of a struct, then documentation on the struct fields will be copied to the docs on the setter methods.
-
-## Positional members
-
-Documentation comments are allowed on [positional members](./positional-members). However, since there are no separate setter methods generated for them, the docs on these members will not be copied anywhere, and thus they won't appear in `rustdoc`. Instead, it's recommended to write documentation for these members on the top level of the struct or function.
diff --git a/website/src/guide/misc/alternatives.md b/website/src/guide/misc/alternatives.md
index a48e6df1..44f1ba13 100644
--- a/website/src/guide/misc/alternatives.md
+++ b/website/src/guide/misc/alternatives.md
@@ -53,7 +53,7 @@ fn main() {
::: tip
-Why is there an explicit `main()` function in this code snippet 🤔? It's a long story explained in a [blog post](../blog/the-weird-of-function-local-types-in-rust) (feel free to skip).
+Why is there an explicit `main()` function in this code snippet 🤔? It's a long story explained in a [blog post](/blog/the-weird-of-function-local-types-in-rust) (feel free to skip).
:::
@@ -98,8 +98,8 @@ Another difference is that fields of collection types are considered required by
[map]: https://docs.rs/bon/latest/bon/macro.map.html
[set]: https://docs.rs/bon/latest/bon/macro.set.html
[mutators]: https://docs.rs/typed-builder/latest/typed_builder/derive.TypedBuilder.html#mutators
-[bon-on]: ../reference/builder/top-level/on
-[bon-into]: ../reference/builder/member/into
+[bon-on]: ../../reference/builder/top-level/on
+[bon-into]: ../../reference/builder/member/into
[bs-into]: https://docs.rs/buildstructor/latest/buildstructor/#into-field
[db-into]: https://docs.rs/derive_builder/latest/derive_builder/#generic-setters
[r1]: #special-setter-methods-for-collections
diff --git a/website/src/guide/misc/compatibility.md b/website/src/guide/misc/compatibility.md
index 6f47b26a..5b23793c 100644
--- a/website/src/guide/misc/compatibility.md
+++ b/website/src/guide/misc/compatibility.md
@@ -2,7 +2,7 @@
## Making a required member optional
-It's totally backwards compatible to make a required member optional by changing the type from `T` to `Option` or by adding [`#[builder(default)]`](../reference/builder/member/default) to it.
+It's totally backwards compatible to make a required member optional by changing the type from `T` to `Option` or by adding [`#[builder(default)]`](../../reference/builder/member/default) to it.
This is because both required and optional members have a setter that accepts `T` (not wrapped in an `Option`). The only change to the public API when making the required member optional is that a `maybe_`-prefixed setter is added to the builder. That new method accepts an `Option`.
@@ -179,4 +179,4 @@ let user = User::builder()
Let's suppose you have existing code that defines functions with positional parameters in the public API. You'd like to change it to expose builder syntax instead, but you want to keep the old code compatible with the positional functions API.
-In this case, you may use the top-level attribute `#[builder(start_fn)]` to keep both syntaxes available. See examples in the [docs for this attribute](../reference/builder/top-level/start-fn#exposing-original-function).
+In this case, you may use the top-level attribute `#[builder(start_fn)]` to keep both syntaxes available. See examples in the [docs for this attribute](../../reference/builder/top-level/start_fn#exposing-original-function).
diff --git a/website/src/guide/overview.md b/website/src/guide/overview.md
index 8cde4882..796583ec 100644
--- a/website/src/guide/overview.md
+++ b/website/src/guide/overview.md
@@ -1,313 +1 @@
-
-
-# Overview
-
-`bon` is a Rust crate for generating compile-time-checked builders for functions and structs. It also provides idiomatic partial application with optional and named parameters for functions and methods.
-
-If you are new to the concept of builders or named function arguments, and you don't know what problems they may solve for you, then check out the motivational [blog post](../blog/how-to-do-named-function-arguments-in-rust).
-
-## Installation
-
-Add this to your `Cargo.toml` to use this crate:
-
-```toml-vue
-[dependencies]
-bon = "{{ versionWildcard }}"
-```
-
-You can opt out of `std` and `alloc` cargo features with `default-features = false` for `no_std` environments.
-
-
-## Builder for a function
-
-`bon` can turn a function with positional parameters into a function with "named" parameters via a builder. It's as easy as placing the `#[builder]` macro on top of it.
-
-**Example:**
-
-```rust
-use bon::builder;
-
-#[builder] // [!code highlight]
-fn greet(name: &str, age: u32) -> String {
- format!("Hello {name} with age {age}!")
-}
-
-let greeting = greet()
- .name("Bon")
- .age(24)
- .call();
-
-assert_eq!(greeting, "Hello Bon with age 24!");
-```
-
-::: tip
-
-Many things are customizable with additional attributes. [`#[builder]` macro reference](../reference/builder) describes all of them.
-
-:::
-
-Any syntax for functions is supported including `async`, fallible, generic functions, `impl Trait`, etc. If you find an edge case where `bon` doesn't work, please [create an issue on GitHub](https://github.com/elastio/bon/issues).
-
-## Builder for an associated method
-
-You can also generate a builder for associated methods. For this to work you need to add a `#[bon]` macro on top of the `impl` block additionally.
-
-**Example:**
-
-```rust
-use bon::bon;
-
-struct Counter {
- val: u32,
-}
-
-#[bon] // <- this macro is required on the impl block // [!code highlight]
-impl Counter {
- #[builder] // [!code highlight]
- fn new(initial: Option) -> Self {
- Self {
- val: initial.unwrap_or_default(),
- }
- }
-
- #[builder] // [!code highlight]
- fn increment(&mut self, diff: u32) {
- self.val += diff;
- }
-}
-
-let mut counter = Counter::builder()
- .initial(3)
- .build();
-
-counter
- .increment()
- .diff(3)
- .call();
-
-assert_eq!(counter.val, 6);
-```
-
-::: details Why is that `#[bon]` macro on top of the `impl` block required? 🤔 (feel free to skip)
-
-There are a couple of technical reasons.
-
-First, it's the lack of surrounding context given to a proc macro in Rust. A proc macro sees only the syntax it is placed on top of. For example, the `#[builder]` macro inside of the `impl` block can't see the `impl Counter` part of the impl block above it. However, it needs that information to tell the actual type of `Self`.
-
-Second, the `#[builder]` proc macro generates new items such as the builder struct type definition, which it needs to output **adjacently** to the `impl` block itself. However, proc macros in Rust can only modify the part of the syntax they are placed on and generate new items on the same level of nesting. The `#[builder]` macro inside of the `impl` block can't just break out of it.
-
-:::
-
-::: details Why does it compile without an import of `bon::builder`? 🤔 (feel free to skip)
-
-This is because there is no separate `#[builder]` proc macro running in this case. Only the `#[bon]` macro handles code generation, it's an active attribute, while `#[builder]` is a dumb inert data attribute (see [the Rust Reference](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes) for details about active and inert attributes).
-
-It wouldn't harm if `bon::builder` was imported. It won't shadow the inert `#[builder]` attribute, but the compiler will report that the import of that macro is unused.
-
-:::
-
-
-To follow the usual Rust builder naming conventions `bon` treats the method named `new` inside of the impl block specially. It generates functions with a bit of different names.
-
-If `#[builder]` is placed on the method called `new`, then the generated functions are called:
-
-| Starting function | Finishing function
-|---------------------------|---------------------
-| `builder() -> {T}Builder` | `build(self) -> T`
-
-For any other methods not called `new` and for any free function the naming is a bit different:
-
-| Starting function | Finishing function
-|------------------------------------------------|---------------------
-| `{fn_name}() -> {T?}{PascalCaseFnName}Builder` | `call(self) -> T`
-
-## Builder for a struct
-
-`bon` supports the classic pattern of annotating a struct to generate a builder with the `#[derive(Builder)]` syntax.
-
-**Example:**
-
-```rust
-use bon::Builder;
-
-#[derive(Builder)]
-struct User {
- id: u32,
- name: String,
-}
-
-let user = User::builder()
- .id(1)
- .name("Bon".to_owned())
- .build();
-
-assert_eq!(user.id, 1);
-assert_eq!(user.name, "Bon");
-```
-
-::: tip
-
-`#[derive(Builder)]` on a struct generates builder API that is fully compatible with placing `#[builder]` attribute on the `new()` method with a signature similar to the struct's fields.
-
-See [compatibility](./compatibility#switching-between-derive-builder-and-builder-on-the-new-method) page for details.
-:::
-
-In general, both `#[derive(Builder)]` on structs and `#[builder]` on functions/methods have almost the same API. We'll use both of them throughout the documentation to provide examples. If the example shows only the usage of one syntax (e.g. `#[builder]`), it's very likely that the other syntax (e.g. `#[derive(Builder)]`) works similarly unless explicitly stated otherwise.
-
-## No panics possible
-
-The builders generated by `#[builder]` and `#[derive(Builder)]` use the typestate pattern to make sure all required parameters are filled, and the same setters aren't called repeatedly to prevent unintentional overwrites and typos. If something is wrong, a compile error will be created. There are no potential panics and `unwrap()`s involved.
-
-## `Option` values are optional
-
-If your function argument or struct field (or member for short) is of type `Option`, then the generated builder will not enforce setting a value for this member, defaulting to `None`.
-
-It also generates two setters: one accepts `T` and the other accepts `Option`. The first avoids wrapping values with `Some()` on the call site. The second allows passing the `Option` value directly.
-
-```rust
-use bon::Builder;
-
-#[derive(Builder)]
-struct Projection {
- x: Option,
- y: Option,
-
- // Use an annotation for members of non-`Option` type
- #[builder(default)]
- z: u32,
-}
-
-// Both `x` and `y` will be set to `None`, `z` will be set to `0`
-Projection::builder().build();
-
-Projection::builder()
- // Pass the value without wrapping it with `Some()`
- .x(10)
- // Or use a `maybe_`-prefixed setter that accepts `Option`
- .maybe_y(Some(20))
- // The APIs generated for `#[builder(default)]` and `Option` are equivalent.
- // `z` will be set to `0` when `build()` is called.
- .maybe_z(None)
- .build();
-```
-
-See [optional members](./optional-members) page for details.
-
-## `Into` conversions
-
-If you have members of type `String`, or `PathBuf`, and you need to set them to a hard-coded string literal, then you have to write `.to_owned()` or `.to_string()` or `.into()`.
-
-**Example:**
-
-```rust
-use bon::Builder;
-use std::path::PathBuf;
-
-#[derive(Builder)] // [!code focus]
-struct Project { // [!code focus]
- name: String, // [!code focus]
- description: String, // [!code focus]
- path: PathBuf, // [!code focus]
-} // [!code focus]
-
-Project::builder()
- .name("Bon".to_owned()) // [!code focus]
- .description("Awesome crate 🐱".to_string()) // [!code focus]
- .path("/path/to/bon".into()) // [!code focus]
- .build();
-```
-
-However, you can ask `bon` to generate setters that accept `impl Into` to remove the need for manual conversion.
-
-This can be configured with [`#[builder(into)]`](../reference/builder/member/into) for a single member or with [`#[builder(on({type}, into))]`](../reference/builder/top-level/on) for many members at once.
-
-```rust
-use bon::Builder;
-use std::path::PathBuf;
-
-// All setters for members of type `String` will accept `impl Into` // [!code highlight]
-#[derive(Builder)] // [!code highlight]
-#[builder(on(String, into))] // [!code highlight]
-struct Project {
- name: String,
- description: String,
-
- // The setter only for this member will accept `impl Into` // [!code highlight]
- #[builder(into)] // [!code highlight]
- path: PathBuf,
-}
-
-Project::builder()
- // &str is converted to `String` internally // [!code highlight]
- .name("Bon")
- .description("Awesome crate 🐱")
- // `&str` is converted to `PathBuf` internally // [!code highlight]
- .path("/path/to/your/heart")
- .build();
-```
-
-See the ["Into Conversions In-Depth"](./into-conversions-in-depth) page for more details and important caveats (!).
-
-## What's next?
-
-::: tip
-
-If you like the idea of this crate and want to say "thank you" or "keep up doing this" consider giving us a [star ⭐ on Github](https://github.com/elastio/bon). Any support and contribution are appreciated 🐱!
-
-:::
-
-This is just part of what's available in `bon`. You may consider reading the rest of the `Guide` section to harness the full power of `bon` and understand the decisions it makes.
-
-You can also consult the [API reference index](../reference/builder) that describes all available configuration attributes. This guide will cover most of them but not all. Check the short descriptions of available attributes to see if something might be of immediate interest to you.
-
-However, feel free to skip the docs and just use the `#[builder]` and `#[derive(Builder)]` in your code. They are designed to be intuitive, so they'll probably do the thing you want them to do already.
-
-If you can't figure something out, consult the docs and maybe use that search `🔍 Search` thing at the top to navigate. You may also create an issue or a discussion on the [Github repository](https://github.com/elastio/bon) for help or write us a message on [Discord](https://discord.gg/QcBYSamw4c) (see below).
-
-Click the "Next page" button at the bottom to proceed with the guide.
-
-## Socials
-
-
Profile of the maintainer. There are only posts about bon and Rust in general here.
-
-
-
-
-
-## Acknowledgments
-
-This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them.
-
-See [alternatives](./alternatives) for comparison.
+
diff --git a/website/src/guide/patterns/conditional-building.md b/website/src/guide/patterns/conditional-building.md
index bc22bb81..7373f4ad 100644
--- a/website/src/guide/patterns/conditional-building.md
+++ b/website/src/guide/patterns/conditional-building.md
@@ -1,6 +1,8 @@
# Conditional Building
-On this page, we'll review a case when you have multiple branches in your code that need to set different values for different builder members. Since builders generated by `bon` use the type-state pattern and setters consume `self`, it is a bit more complicated for conditional code to use them. But, not until you know the patterns described below 🐱. So let's learn how to write better conditional code 📚.
+On this page, we'll review a case when you have multiple branches in your code that need to set different values for different builder members.
+
+Since builders generated by `bon` use the typestate pattern and setters consume `self`, it is a bit more complicated for conditional code to use them. But, not until you know the patterns described below 🐱. So let's learn how to write better conditional code 📚.
The patterns described here aren't mutually exclusive. You can combine them as you see necessary to keep your code clean.
@@ -56,7 +58,7 @@ Once the `build()` method is called, we no longer have the context of how exactl
## Shared total builder
-In contrast to the [shared partial builder](#shared-partial-builder), here we are going to use the builder strictly _after_ the conditional code. The conditional code needs to create the variables that hold the component values for the builder beforehand.
+In contrast to the [shared partial builder](#shared-partial-builder), here we'll use the builder strictly _after_ the conditional code. The conditional code needs to create the variables that hold the component values for the builder beforehand.
**Example:**
@@ -102,11 +104,11 @@ let user = User::builder()
.build();
```
-In this case, we create a variable for each conditional member beforehand and initialize them separately, then we just pass the results to the builder. We benefit from the `maybe_` setters for [optional members](./optional-members) such that we can pass the `Option` values directly.
+In this case, we create a variable for each conditional member beforehand and initialize them separately, then we pass the results to the builder. We benefit from the `maybe_` setters for [optional members](../basics/optional-members) such that we can pass the `Option` values directly.
::: tip NOTE
-However, creating separate variables is not strictly required. You can inline the usage of the variables in such a simple code like here, where each branch of the `if` takes a single line. Anyhow, branches can be much bigger in real code.
+Creating separate variables is not strictly required. You can inline the usages of variables in such simple code as here, where each branch of the `if` takes a single line. Anyhow, branches can be much bigger in real code.
:::
diff --git a/website/src/guide/patterns/into-conversions-in-depth.md b/website/src/guide/patterns/into-conversions-in-depth.md
index f5168e14..003b73f7 100644
--- a/website/src/guide/patterns/into-conversions-in-depth.md
+++ b/website/src/guide/patterns/into-conversions-in-depth.md
@@ -7,7 +7,7 @@ outline: deep
## Preface
-This is the continuation of the ["Into conversions" section](./overview#into-conversions) from the general overview page. This page describes the important caveats of using `impl Into` that you should know before enabling them.
+This is the continuation of [Into Conversions](../basics/into-conversions) from the `Basics` section. This page describes important caveats of using `impl Into` that you should know before enabling them.
Make sure you are familiar with the standard [`From`](https://doc.rust-lang.org/stable/std/convert/trait.From.html) and [`Into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html) traits before you proceed. Reading their docs is pretty much enough. For example, you should know that every type that implements `From` automatically implements `Into`. Also, you should know that you can pass a value of type `T` at no cost directly to a function that accepts `impl Into` thanks to [this blanket](https://github.com/rust-lang/rust/blob/1a94d839be8b248b972b9e022cb940d56de72fa1/library/core/src/convert/mod.rs#L763-L771) impl in `std`.
diff --git a/website/src/guide/patterns/shared-configuration.md b/website/src/guide/patterns/shared-configuration.md
index c169d47a..cd59b58b 100644
--- a/website/src/guide/patterns/shared-configuration.md
+++ b/website/src/guide/patterns/shared-configuration.md
@@ -8,7 +8,9 @@ On this page, you'll learn how to share common configurations for builders to av
## Problem statement
-As an example, let's suppose you want to enable [`Into` conversions](./into-conversions-in-depth) for specific types across all your builders and maybe also override the name of the finishing function that consumes the builder from the default `build` to `finish`. The problem that you'll quickly run into is that you'll need to repeat the same configuration everywhere:
+As an example, let's suppose you want to enable [`Into` conversions](../patterns/into-conversions-in-depth) for specific types across all your builders and maybe also override the name of the finishing function that consumes the builder from the default `build` to `finish`.
+
+You'll quickly run into a problem, where you need to repeat the same configuration for every usage of the builder macro.
```rust
use bon::Builder;
@@ -33,25 +35,23 @@ struct MyLovelyStruct2 { /**/ }
::: tip
-This code uses the [`#[builder(on(...))]`](../reference/builder/top-level/on) attribute to configure the types of members for which `bon` should enable `Into` conversions.
+This code uses the [`#[builder(on(...))]`](../../reference/builder/top-level/on) attribute to configure the types of members for which `bon` should enable `Into` conversions.
:::
-The annoying thing here is that we need to copy all these configurations on every struct where we derive the builder.
-
## Solution
-### Structs
+To overcome this problem you can utilize the [`macro_rules_attribute`] crate. It allows you to declare an [`attribute_alias`](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/macro.attribute_alias.html) that defines all the shared configuration for your builders and makes it reusable.
-To overcome this problem we can utilize the [`macro_rules_attribute`] crate. It allows you to declare an [`attribute_alias`](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/macro.attribute_alias.html) that defines all the shared configuration for your builders and makes it reusable.
+Use this approach if you have a lot of structs/functions in your crate that need a builder.
-So with the [`macro_rules_attribute`] your code will look like this:
+### Structs
```rust
use macro_rules_attribute::{attribute_alias, apply};
// The alias can also be defined in a separate module.
-// Under the hood it creates a macro with `pub(crate)` visibility.
+// Under the hood, it creates a macro with `pub(crate)` visibility.
attribute_alias! {
#[apply(derive_builder!)] =
#[derive(::bon::Builder)]
@@ -69,13 +69,8 @@ struct MyLovelyStruct1 { /**/ }
struct MyLovelyStruct2 { /**/ }
```
-Use this approach if you have a lot of structs in your crate that need a builder. Adding [`macro_rules_attribute`] to your dependencies shouldn't have a noticeable impact on the compilation performance. This approach [was tested](https://github.com/ayrat555/frankenstein/blob/91ac379a52ed716e09632f78b984852c85f2adaa/src/macros.rs#L3-L14) on a crate with ~320 structs that derive a builder and compile time was the same as before adding the [`macro_rules_attribute`] crate.
-
### Free functions
-A similar approach works with `#[bon::builder]` on free functions.
-**Example:**
-
```rust
use macro_rules_attribute::{attribute_alias, apply};
@@ -100,6 +95,6 @@ fn my_lovely_fn2(/**/) { /**/ }
Unfortunately, this technique doesn't quite work with associated methods (functions inside impl blocks) due to the limitations of proc macro attribute expansion order. The `#[bon]` macro on top of the impl block is expanded first before the `#[apply(...)]` macro inside of the impl block, so `#[bon]` doesn't see the configuration expanded from the `#[apply(...)]`.
-There is a proposed solution to this problem in the issue [#elastio/bon#144](https://github.com/elastio/bon/issues/144). Add a 👍 to that issue if your use case needs a solution for this, and maybe leave a comment about your particular use case where you'd like to have this feature.
+There is a proposed solution to this problem in the issue [#144](https://github.com/elastio/bon/issues/144). Add a 👍 to that issue if your use case needs a solution for this. It would be even better if you left a comment describing your particular use case where you'd like to have this feature.
[`macro_rules_attribute`]: https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/
diff --git a/website/src/guide/positional-members.md b/website/src/guide/positional-members.md
deleted file mode 100644
index 18847842..00000000
--- a/website/src/guide/positional-members.md
+++ /dev/null
@@ -1,146 +0,0 @@
-# Positional Members
-
-While having the ability to use separate setters for the members gives you a ton of flexibility and extensibility described on the ["Compatibility"](./compatibility) page, sometimes you don't need all of that.
-
-Maybe you'd like to pick out some specific members and let the user pass their values as positional parameters to the starting function that creates the builder or to the finishing function that consumes it. This reduces the syntax a bit at the cost of some extensibility loss ⚖️, but it may be worth it!
-
-## Starting function
-
-As an example, suppose we have a `Treasure` struct with `x` and `y` coordinates and a `label` that describes the payload of the treasure. Since all treasures are located somewhere, they all have coordinates, and it would be cool to specify them in a single starting function call.
-
-To do that we can use the `#[builder(start_fn)]` attribute. There are two contexts where we can place it, and they both have a different meaning:
-
-- [Top-level `#[builder(start_fn = ...)]`](../reference/builder/top-level/start-fn) - configures name, visibility and docs of the starting function
-- [Member-level `#[builder(start_fn)]`](../reference/builder/member/start-fn) - configures the member to be a positional parameter on the starting function
-
-We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure `x` and `y` as positional parameters on the starting function as well.
-
-**Example:**
-
-```rust
-use bon::Builder;
-
-#[derive(Builder)]
-// Top-level attribute to give a better name for the starting function // [!code highlight]
-#[builder(start_fn = with_coordinates)] // [!code highlight]
-struct Treasure {
- // Member-level attributes to make members as // [!code highlight]
- // parameters of `with_coordinates()` // [!code highlight]
- #[builder(start_fn)] // [!code highlight]
- x: u32,
-
- #[builder(start_fn)] // [!code highlight]
- y: u32,
-
- label: Option,
-}
-
-let treasure = Treasure::with_coordinates(2, 9) // [!code highlight]
- .label("oats".to_owned())
- .build();
-
-assert_eq!(treasure.x, 2);
-assert_eq!(treasure.y, 9);
-assert_eq!(treasure.label.as_deref(), Some("oats"));
-```
-
-Here, the generated `with_coordinates` method has the following signature:
-
-```rust ignore
-impl Treasure {
- fn with_coordinates(x: u32, y: u32) -> TreasureBuilder { /**/ }
-}
-```
-
-## Finishing function
-
-Now let's say we need to know the person who claimed the `Treasure`. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process.
-
-We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder/top-level/finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder/member/finish-fn) attributes to do that.
-
-**Example:**
-
-```rust
-use bon::Builder;
-
-#[derive(Builder)]
-#[builder(
- start_fn = with_coordinates,
- finish_fn = claim // [!code highlight]
-)]
-struct Treasure {
- #[builder(start_fn)]
- x: u32,
-
- #[builder(start_fn)]
- y: u32,
-
- #[builder(finish_fn)] // [!code highlight]
- claimed_by_first_name: String, // [!code highlight]
-
- #[builder(finish_fn)] // [!code highlight]
- claimed_by_last_name: String, // [!code highlight]
-
- label: Option,
-}
-
-let treasure = Treasure::with_coordinates(2, 9)
- .label("oats".to_owned())
- .claim("Lyra".to_owned(), "Heartstrings".to_owned()); // [!code highlight]
-
-assert_eq!(treasure.x, 2);
-assert_eq!(treasure.y, 9);
-assert_eq!(treasure.label.as_deref(), Some("oats"));
-assert_eq!(treasure.claimed_by_first_name, "Lyra"); // [!code highlight]
-assert_eq!(treasure.claimed_by_last_name, "Heartstrings"); // [!code highlight]
-```
-
-## Into conversions
-
-You may also combine these attributes with [`#[builder(into)]`](../reference/builder/member/into) or [`#[builder(on(..., into))]`](../reference/builder/top-level/on) to reduce the number of `to_owned()` calls a bit.
-
-```rust
-use bon::Builder;
-
-#[derive(Builder)]
-#[builder(
- start_fn = with_coordinates,
- finish_fn = claim // [!code focus]
-)]
-struct Treasure {
- #[builder(start_fn)]
- x: u32,
-
- #[builder(start_fn)]
- y: u32,
-
- #[builder(finish_fn, into)] // [!code focus]
- claimed_by_first_name: String, // [!code focus]
-
- #[builder(finish_fn, into)] // [!code focus]
- claimed_by_last_name: String, // [!code focus]
-
- #[builder(into)] // [!code focus]
- label: Option, // [!code focus]
-}
-
-let treasure = Treasure::with_coordinates(2, 9)
- .label("oats") // [!code focus]
- .claim("Lyra", "Heartstrings"); // [!code focus]
-```
-
-However, keep in mind that positional members (ones annotated with `#[builder(start_fn/finish_fn)]`) are always required to pass. There is no special treatment of the `Option` type for such members.
-
-For example `#[builder(into)]` on a regular (named) member of the `Option` type generates two setters:
-- One that accepts `impl Into`.
-- The other that accepts `Option>`.
-
-For positional members, the story is completely different because there are no separate setters generated for them. There is just a single starting or finishing function. So if you enable an into conversion for a positional member of the `Option` type, it will be accepted as `impl Into