Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion website/src/guide/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

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

Code examples may use either `fn` or `struct` builder syntax interchangeably. Both follow the same principles unless stated otherwise.
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.

Now, let's begin with the first topic of [Optional Members](./basics/optional-members).
57 changes: 54 additions & 3 deletions website/src/guide/basics/custom-conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

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;
::: code-group

```rust [Struct]
struct Point {
x: u32,
y: u32,
}

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

```rust [Function]
struct Point {
x: u32,
y: u32,
}

#[bon::builder]
fn example(
#[builder(with = |x: u32, y: u32| Point { x, y })] // [!code highlight]
point: Point,
) -> Point {
point
}

let value = example()
.point(2, 3) // [!code highlight]
.call();

assert_eq!(value.x, 2);
assert_eq!(value.y, 3);
```

```rust [Method]
struct Point {
x: u32,
y: u32,
}

struct Example;

#[bon::bon]
impl Example {
#[builder]
fn example(
#[builder(with = |x: u32, y: u32| Point { x, y })] // [!code highlight]
point: Point,
) -> Point {
point
}
}

let value = Example::example()
.point(2, 3) // [!code highlight]
.call();

assert_eq!(value.x, 2);
assert_eq!(value.y, 3);
```

:::

You can make the setter fallible by passing a [fallible closure](../../reference/builder/member/with#fallible-closure).

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.
Expand Down
60 changes: 58 additions & 2 deletions website/src/guide/basics/derives-for-builders.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,35 @@ You can specify some extra derives on the generated builder struct itself via th

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.

```rust
use bon::builder;
::: code-group

```rust [Struct]
#[derive(bon::Builder)]
#[builder(derive(Debug))] // [!code highlight]
struct Example {
name: String,
is_admin: bool,
level: Option<u32>,
}

let builder = Example::builder().name("Bon".to_owned());

// This will output the current state of the builder to `stderr`
dbg!(&builder);

// You can also format the debug output to `String`:
assert_eq!(
format!("{builder:?}"),
// Only the fields that were set will be output
r#"ExampleBuilder { name: "Bon" }"#
);

// Finish building
builder.is_admin(true).build();
```

```rust [Function]
#[bon::builder(derive(Debug))] // [!code highlight]
fn example(
name: String,
is_admin: bool,
Expand All @@ -30,4 +55,35 @@ assert_eq!(
builder.is_admin(true).call();
```

```rust [Method]
struct Example;

#[bon::bon]
impl Example {
#[builder(derive(Debug))] // [!code highlight]
fn method(
name: String,
is_admin: bool,
level: Option<u32>,
) {}
}

let builder = Example::method().name("Bon".to_owned());

// This will output the current state of the builder to `stderr`
dbg!(&builder);

// You can also format the debug output to `String`:
assert_eq!(
format!("{builder:?}"),
// Only the fields that were set will be output
r#"ExampleMethodBuilder { name: "Bon" }"#
);

// Finish building
builder.is_admin(true).call();
```

:::

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).
53 changes: 41 additions & 12 deletions website/src/guide/basics/documenting.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@

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]
#[bon::builder]
fn greet(
/// Name of the person to greet.
///
Expand All @@ -33,26 +29,59 @@ This works because Rust compiler checks for invalid placement of `#[doc = ...]`

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
## 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;
::: code-group

#[derive(Builder)]
```rust [Struct]
#[derive(bon::Builder)]
#[builder(
builder_type(doc {
/// Custom docs on the builder struct itself
}),
start_fn(doc {
/// Custom docs on the starting function
finish_fn(doc {
/// Custom docs on the finishing function
}),
// ...
)]
struct Example {}
```

```rust [Function]
#[bon::builder(
builder_type(doc {
/// Custom docs on the builder struct itself
}),
finish_fn(doc {
/// Custom docs on the finishing function
}),
// ...
)]
fn example() {}
```

```rust [Method]
struct Example;

#[bon::bon]
impl Example {
#[builder(
builder_type(doc {
/// Custom docs on the builder struct itself
}),
finish_fn(doc {
/// Custom docs on the finishing function
}),
// ...
)]
fn example() {}
}
```

:::

You can document the following items this way:

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

## Positional members
## 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.
112 changes: 102 additions & 10 deletions website/src/guide/basics/into-conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,79 @@

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;
::: code-group

```rust [Struct]
use std::path::PathBuf;

#[derive(Builder)] // [!code focus]
struct Project { // [!code focus]
#[derive(bon::Builder)] // [!code focus]
struct Example { // [!code focus]
name: String, // [!code focus]
description: String, // [!code focus]
path: PathBuf, // [!code focus]
} // [!code focus]

Project::builder()
Example::builder()
.name("Bon".to_owned()) // [!code focus]
.description("Awesome crate 🐱".to_string()) // [!code focus]
.path("/path/to/bon".into()) // [!code focus]
.build();
```

```rust [Function]
use std::path::PathBuf;

#[bon::builder] // [!code focus]
fn example( // [!code focus]
name: String, // [!code focus]
description: String, // [!code focus]
path: PathBuf, // [!code focus]
) {} // [!code focus]

example()
.name("Bon".to_owned()) // [!code focus]
.description("Awesome crate 🐱".to_string()) // [!code focus]
.path("/path/to/bon".into()) // [!code focus]
.call();
```

```rust [Method]
use std::path::PathBuf;

struct Example;

#[bon::bon]
impl Example {
#[builder] // [!code focus]
fn example( // [!code focus]
name: String, // [!code focus]
description: String, // [!code focus]
path: PathBuf, // [!code focus]
) {} // [!code focus]
}

Example::example()
.name("Bon".to_owned()) // [!code focus]
.description("Awesome crate 🐱".to_string()) // [!code focus]
.path("/path/to/bon".into()) // [!code focus]
.call();
```

:::

However, you can ask `bon` to generate setters that accept `impl Into<T>` 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;
::: code-group

```rust [Struct]
use std::path::PathBuf;

// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
#[derive(Builder)] // [!code highlight]
#[derive(bon::Builder)] // [!code highlight]
#[builder(on(String, into))] // [!code highlight]
struct Project {
struct Example {
name: String,
description: String,

Expand All @@ -40,11 +83,60 @@ struct Project {
path: PathBuf,
}

Project::builder()
Example::builder()
.name("Bon") // [!code highlight]
.description("Awesome crate 🐱") // [!code highlight]
.path("/path/to/your/heart") // [!code highlight]
.build();
```

```rust [Function]
use std::path::PathBuf;

// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
#[bon::builder(on(String, into))] // [!code highlight]
fn example(
name: String,
description: String,

// The setter only for this member will accept `impl Into<PathBuf>` // [!code highlight]
#[builder(into)] // [!code highlight]
path: PathBuf,
) {}

example()
.name("Bon") // [!code highlight]
.description("Awesome crate 🐱") // [!code highlight]
.path("/path/to/your/heart") // [!code highlight]
.call();
```

```rust [Method]
use std::path::PathBuf;

struct Example;

#[bon::bon]
impl Example {
// All setters for members of type `String` will accept `impl Into<String>` // [!code highlight]
#[builder(on(String, into))] // [!code highlight]
fn example(
name: String,
description: String,

// The setter only for this member will accept `impl Into<PathBuf>` // [!code highlight]
#[builder(into)] // [!code highlight]
path: PathBuf,
) {}
}

Example::example()
.name("Bon") // [!code highlight]
.description("Awesome crate 🐱") // [!code highlight]
.path("/path/to/your/heart") // [!code highlight]
.call();
```

:::

`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.
Loading
Loading