Skip to content

Commit 9abdd3d

Browse files
authored
Add all 3 variations of builder syntax to the Basics guide pages (#315)
1 parent 48d56aa commit 9abdd3d

7 files changed

Lines changed: 513 additions & 41 deletions

File tree

website/src/guide/basics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
Welcome to the Bon Guide Book 🍬! It's meant to walk you through `bon` so you don't get lost while exploring it 🐈.
44

5-
Code examples may use either `fn` or `struct` builder syntax interchangeably. Both follow the same principles unless stated otherwise.
5+
Some code examples have tabs for "Struct", "Function" and "Method" syntax. Some examples may not have them, but it doesn't necessarily mean the described feature works only with one kind of syntax.
66

77
Now, let's begin with the first topic of [Optional Members](./basics/optional-members).

website/src/guide/basics/custom-conversions.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
You can pass a custom closure to `#[builder(with)]`. It will define the signature of the setter and perform a conversion.
66

7-
```rust
8-
use bon::Builder;
7+
::: code-group
98

9+
```rust [Struct]
1010
struct Point {
1111
x: u32,
1212
y: u32,
1313
}
1414

15-
#[derive(Builder)]
15+
#[derive(bon::Builder)]
1616
struct Example {
1717
#[builder(with = |x: u32, y: u32| Point { x, y })] // [!code highlight]
1818
point: Point,
@@ -26,6 +26,57 @@ assert_eq!(value.point.x, 2);
2626
assert_eq!(value.point.y, 3);
2727
```
2828

29+
```rust [Function]
30+
struct Point {
31+
x: u32,
32+
y: u32,
33+
}
34+
35+
#[bon::builder]
36+
fn example(
37+
#[builder(with = |x: u32, y: u32| Point { x, y })] // [!code highlight]
38+
point: Point,
39+
) -> Point {
40+
point
41+
}
42+
43+
let value = example()
44+
.point(2, 3) // [!code highlight]
45+
.call();
46+
47+
assert_eq!(value.x, 2);
48+
assert_eq!(value.y, 3);
49+
```
50+
51+
```rust [Method]
52+
struct Point {
53+
x: u32,
54+
y: u32,
55+
}
56+
57+
struct Example;
58+
59+
#[bon::bon]
60+
impl Example {
61+
#[builder]
62+
fn example(
63+
#[builder(with = |x: u32, y: u32| Point { x, y })] // [!code highlight]
64+
point: Point,
65+
) -> Point {
66+
point
67+
}
68+
}
69+
70+
let value = Example::example()
71+
.point(2, 3) // [!code highlight]
72+
.call();
73+
74+
assert_eq!(value.x, 2);
75+
assert_eq!(value.y, 3);
76+
```
77+
78+
:::
79+
2980
You can make the setter fallible by passing a [fallible closure](../../reference/builder/member/with#fallible-closure).
3081

3182
You can pass one of [well-known functions](../../reference/builder/member/with#well-known-functions) instead of a closure to `#[builder(with)]`. If any of them fit your use, this will save you some characters to type.

website/src/guide/basics/derives-for-builders.md

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,35 @@ You can specify some extra derives on the generated builder struct itself via th
44

55
For example, if you want to inspect the values set in the builder for debugging purposes, you can derive the `Debug` trait for your builder.
66

7-
```rust
8-
use bon::builder;
7+
::: code-group
98

9+
```rust [Struct]
10+
#[derive(bon::Builder)]
1011
#[builder(derive(Debug))] // [!code highlight]
12+
struct Example {
13+
name: String,
14+
is_admin: bool,
15+
level: Option<u32>,
16+
}
17+
18+
let builder = Example::builder().name("Bon".to_owned());
19+
20+
// This will output the current state of the builder to `stderr`
21+
dbg!(&builder);
22+
23+
// You can also format the debug output to `String`:
24+
assert_eq!(
25+
format!("{builder:?}"),
26+
// Only the fields that were set will be output
27+
r#"ExampleBuilder { name: "Bon" }"#
28+
);
29+
30+
// Finish building
31+
builder.is_admin(true).build();
32+
```
33+
34+
```rust [Function]
35+
#[bon::builder(derive(Debug))] // [!code highlight]
1136
fn example(
1237
name: String,
1338
is_admin: bool,
@@ -30,4 +55,35 @@ assert_eq!(
3055
builder.is_admin(true).call();
3156
```
3257

58+
```rust [Method]
59+
struct Example;
60+
61+
#[bon::bon]
62+
impl Example {
63+
#[builder(derive(Debug))] // [!code highlight]
64+
fn method(
65+
name: String,
66+
is_admin: bool,
67+
level: Option<u32>,
68+
) {}
69+
}
70+
71+
let builder = Example::method().name("Bon".to_owned());
72+
73+
// This will output the current state of the builder to `stderr`
74+
dbg!(&builder);
75+
76+
// You can also format the debug output to `String`:
77+
assert_eq!(
78+
format!("{builder:?}"),
79+
// Only the fields that were set will be output
80+
r#"ExampleMethodBuilder { name: "Bon" }"#
81+
);
82+
83+
// Finish building
84+
builder.is_admin(true).call();
85+
```
86+
87+
:::
88+
3389
You can also derive the `Clone` and `Into` traits for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../../reference/builder/top-level/derive).

website/src/guide/basics/documenting.md

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22

33
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.
44

5-
**Example:**
6-
75
````rust
8-
use bon::builder;
9-
106
/// Function that returns a greeting special-tailored for a given person
11-
#[builder]
7+
#[bon::builder]
128
fn greet(
139
/// Name of the person to greet.
1410
///
@@ -33,26 +29,59 @@ This works because Rust compiler checks for invalid placement of `#[doc = ...]`
3329

3430
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.
3531

36-
## Custom `doc` attributes
32+
## Custom `doc` Attributes
3733

3834
You can override documentation on other items generated by builder macros. There are multiple attributes accepting a `doc { ... }` block.
3935

40-
```rust
41-
use bon::Builder;
36+
::: code-group
4237

43-
#[derive(Builder)]
38+
```rust [Struct]
39+
#[derive(bon::Builder)]
4440
#[builder(
4541
builder_type(doc {
4642
/// Custom docs on the builder struct itself
4743
}),
48-
start_fn(doc {
49-
/// Custom docs on the starting function
44+
finish_fn(doc {
45+
/// Custom docs on the finishing function
5046
}),
5147
// ...
5248
)]
5349
struct Example {}
5450
```
5551

52+
```rust [Function]
53+
#[bon::builder(
54+
builder_type(doc {
55+
/// Custom docs on the builder struct itself
56+
}),
57+
finish_fn(doc {
58+
/// Custom docs on the finishing function
59+
}),
60+
// ...
61+
)]
62+
fn example() {}
63+
```
64+
65+
```rust [Method]
66+
struct Example;
67+
68+
#[bon::bon]
69+
impl Example {
70+
#[builder(
71+
builder_type(doc {
72+
/// Custom docs on the builder struct itself
73+
}),
74+
finish_fn(doc {
75+
/// Custom docs on the finishing function
76+
}),
77+
// ...
78+
)]
79+
fn example() {}
80+
}
81+
```
82+
83+
:::
84+
5685
You can document the following items this way:
5786

5887
| Attribute | Documentation target |
@@ -64,6 +93,6 @@ You can document the following items this way:
6493
| [`setters`](../../reference/builder/member/setters#doc) | Custom docs for setters. Prevents copying them from the field/argument |
6594
| [`getter`](../../reference/builder/member/getter#doc) | Custom docs for a getter. Prevents copying them from the field/argument |
6695

67-
## Positional members
96+
## Positional Members
6897

6998
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.

website/src/guide/basics/into-conversions.md

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,79 @@
22

33
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()`.
44

5-
```rust
6-
use bon::Builder;
5+
::: code-group
6+
7+
```rust [Struct]
78
use std::path::PathBuf;
89

9-
#[derive(Builder)] // [!code focus]
10-
struct Project { // [!code focus]
10+
#[derive(bon::Builder)] // [!code focus]
11+
struct Example { // [!code focus]
1112
name: String, // [!code focus]
1213
description: String, // [!code focus]
1314
path: PathBuf, // [!code focus]
1415
} // [!code focus]
1516

16-
Project::builder()
17+
Example::builder()
1718
.name("Bon".to_owned()) // [!code focus]
1819
.description("Awesome crate 🐱".to_string()) // [!code focus]
1920
.path("/path/to/bon".into()) // [!code focus]
2021
.build();
2122
```
2223

24+
```rust [Function]
25+
use std::path::PathBuf;
26+
27+
#[bon::builder] // [!code focus]
28+
fn example( // [!code focus]
29+
name: String, // [!code focus]
30+
description: String, // [!code focus]
31+
path: PathBuf, // [!code focus]
32+
) {} // [!code focus]
33+
34+
example()
35+
.name("Bon".to_owned()) // [!code focus]
36+
.description("Awesome crate 🐱".to_string()) // [!code focus]
37+
.path("/path/to/bon".into()) // [!code focus]
38+
.call();
39+
```
40+
41+
```rust [Method]
42+
use std::path::PathBuf;
43+
44+
struct Example;
45+
46+
#[bon::bon]
47+
impl Example {
48+
#[builder] // [!code focus]
49+
fn example( // [!code focus]
50+
name: String, // [!code focus]
51+
description: String, // [!code focus]
52+
path: PathBuf, // [!code focus]
53+
) {} // [!code focus]
54+
}
55+
56+
Example::example()
57+
.name("Bon".to_owned()) // [!code focus]
58+
.description("Awesome crate 🐱".to_string()) // [!code focus]
59+
.path("/path/to/bon".into()) // [!code focus]
60+
.call();
61+
```
62+
63+
:::
64+
2365
However, you can ask `bon` to generate setters that accept `impl Into<T>` to remove the need for manual conversion.
2466

2567
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.
2668

27-
```rust
28-
use bon::Builder;
69+
::: code-group
70+
71+
```rust [Struct]
2972
use std::path::PathBuf;
3073

3174
// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
32-
#[derive(Builder)] // [!code highlight]
75+
#[derive(bon::Builder)] // [!code highlight]
3376
#[builder(on(String, into))] // [!code highlight]
34-
struct Project {
77+
struct Example {
3578
name: String,
3679
description: String,
3780

@@ -40,11 +83,60 @@ struct Project {
4083
path: PathBuf,
4184
}
4285

43-
Project::builder()
86+
Example::builder()
4487
.name("Bon") // [!code highlight]
4588
.description("Awesome crate 🐱") // [!code highlight]
4689
.path("/path/to/your/heart") // [!code highlight]
4790
.build();
4891
```
4992

93+
```rust [Function]
94+
use std::path::PathBuf;
95+
96+
// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
97+
#[bon::builder(on(String, into))] // [!code highlight]
98+
fn example(
99+
name: String,
100+
description: String,
101+
102+
// The setter only for this member will accept `impl Into<PathBuf>` // [!code highlight]
103+
#[builder(into)] // [!code highlight]
104+
path: PathBuf,
105+
) {}
106+
107+
example()
108+
.name("Bon") // [!code highlight]
109+
.description("Awesome crate 🐱") // [!code highlight]
110+
.path("/path/to/your/heart") // [!code highlight]
111+
.call();
112+
```
113+
114+
```rust [Method]
115+
use std::path::PathBuf;
116+
117+
struct Example;
118+
119+
#[bon::bon]
120+
impl Example {
121+
// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
122+
#[builder(on(String, into))] // [!code highlight]
123+
fn example(
124+
name: String,
125+
description: String,
126+
127+
// The setter only for this member will accept `impl Into<PathBuf>` // [!code highlight]
128+
#[builder(into)] // [!code highlight]
129+
path: PathBuf,
130+
) {}
131+
}
132+
133+
Example::example()
134+
.name("Bon") // [!code highlight]
135+
.description("Awesome crate 🐱") // [!code highlight]
136+
.path("/path/to/your/heart") // [!code highlight]
137+
.call();
138+
```
139+
140+
:::
141+
50142
`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.

0 commit comments

Comments
 (0)