Skip to content

Commit

Permalink
Fix doctests, add reference API docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Veetaha committed Nov 17, 2024
1 parent 487c208 commit 79f1041
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions website/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ export default defineConfig({
text: "default",
link: "/reference/builder/member/default",
},
{
text: "field",
link: "/reference/builder/member/field",
},
{
text: "finish_fn",
link: "/reference/builder/member/finish_fn",
Expand Down
3 changes: 2 additions & 1 deletion website/doctests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ workspace = true

[dev-dependencies]
anyhow = "1.0"
bon = { path = "../../bon", features = ["experimental-overwritable"] }
bon = { path = "../../bon", features = ["experimental-overwritable", "implied-bounds"] }
buildstructor = "0.5"
macro_rules_attribute = "0.2"
typed-builder = "0.20"

[build-dependencies]
itertools = "0.13"
Expand Down
2 changes: 1 addition & 1 deletion website/doctests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,5 @@ fn is_hidden(entry: &DirEntry) -> bool {
entry
.file_name()
.to_str()
.is_some_and(|s| s == "node_modules" || s.starts_with('.'))
.is_some_and(|s| s == "node_modules" || (s != ".." && s.starts_with('.')))
}
1 change: 1 addition & 0 deletions website/src/reference/builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ These attributes are placed on a `struct` field or `fn` argument.
| Attribute | Short description |
| -------------------------------------------------- | ---------------------------------------------------------------- |
| [`default`](./builder/member/default) | Makes the member optional with a default value |
| [`field`](./builder/member/field) | Defines a private field on the builder without setters |
| [`finish_fn`](./builder/member/finish_fn) | Makes the member a positional argument on the finishing function |
| [`into`](./builder/member/into) | Changes the signature of the setters to accept `impl Into<T>` |
| [`name`](./builder/member/name) | Overrides the name of the member used in the builder's API |
Expand Down
4 changes: 1 addition & 3 deletions website/src/reference/builder/member/default.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

**Applies to:** <Badge type="warning" text="struct fields"/> <Badge type="warning" text="function arguments"/> <Badge type="warning" text="method arguments"/>

Makes the member optional and assigns a default value to it. The default value is lazily computed inside of the finishing function based on the form of this attribute.

## Syntax
Makes the member optional and assigns a default value to it. The default value is lazily computed inside of the finishing function.

| Form | How default value is computed |
| ---------------------------------- | ----------------------------- |
Expand Down
138 changes: 138 additions & 0 deletions website/src/reference/builder/member/field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# `field`

**Applies to:** <Badge type="warning" text="struct fields"/> <Badge type="warning" text="function arguments"/> <Badge type="warning" text="method arguments"/>

Defines a private field on the builder without setters. This field will be available in [Custom Methods](../../../guide/typestate-api/custom-methods).

The value for the member will be computed inside of the starting function.

| Form | How value for the member is computed |
| -------------------------------- | ------------------------------------ |
| `#[builder(field)]` | `Default::default()` |
| `#[builder(field = expression)]` | `expression` |

## Difference with [`#[builder(skip)]`](./skip)

`#[builder(field)]` attribute is similar to [`#[builder(skip)]`](./skip), but the main difference is that `#[builder(field)]` computes the value in the starting function and stores it in a private field in the builder. This lets you access that field to manage additional custom state during the building process.

On the other hand, [`#[builder(skip)]`](./skip) computes the value in the finishing function and doesn't store it in the builder.

## Example

Let's define a `Vec` field with a custom method to push values into it during building.

::: code-group

```rust [Struct]
use bon::Builder;

#[derive(Builder)]
struct Example {
#[builder(field)] // [!code highlight]
coefs: Vec<u32>,
}

// Add a custom method that uses the field // [!code highlight]
impl<S: example_builder::State> ExampleBuilder<S> {
fn coef(mut self, value: u32) -> Self {
self.coefs.push(value);
self
}
}

let example = Example::builder()
.coef(2)
.coef(99)
.build();

assert_eq!(example.coefs, [2, 99]);
```

```rust [Function]
use bon::builder;

#[builder]
fn example(
#[builder(field)] // [!code highlight]
coefs: Vec<u32>,
) -> Vec<u32> {
coefs
}

// Add a custom method that uses the field // [!code highlight]
impl<S: example_builder::State> ExampleBuilder<S> {
fn coef(mut self, value: u32) -> Self {
self.coefs.push(value);
self
}
}

let example = example()
.coef(2)
.coef(99)
.call();

assert_eq!(example, [2, 99]);
```

```rust [Method]
use bon::bon;

struct Example;

#[bon]
impl Example {
#[builder]
fn method(
#[builder(field)] // [!code highlight]
coefs: Vec<u32>,
) -> Vec<u32> {
coefs
}
}

// Add a custom method that uses the field // [!code highlight]
impl<S: example_method_builder::State> ExampleMethodBuilder<S> {
fn coef(mut self, value: u32) -> Self {
self.coefs.push(value);
self
}
}

let example = Example::method()
.coef(2)
.coef(99)
.call();

assert_eq!(example, [2, 99]);
```

:::

## Evaluation context

You can use values of other member marked with [`#[builder(start_fn)]`](./start_fn) or [`#[builder(field)]`](./field) by referencing their names in the `field` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `field` expression.

```rust
use bon::Builder;

#[derive(Builder)]
struct Example {
#[builder(start_fn)]
x1: u32,

// Note that here we don't have access to `x3`
// because it's declared (and thus initialized) later
#[builder(field = 2 * x1)]
x2: u32,

#[builder(field = x2 + x1)]
x3: u32,
}

let example = Example::builder(3).build();

assert_eq!(example.x1, 3);
assert_eq!(example.x2, 6);
assert_eq!(example.x3, 9);
```
2 changes: 1 addition & 1 deletion website/src/reference/builder/member/skip.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Skips generating setters for the member. This hides the member from the generated builder API, so the caller can't set its value.

The value for the member will be computed based on the form of the attribute:
The value for the member will be computed inside of the finishing function.

| Form | How value for the member is computed |
| ------------------------------- | ------------------------------------ |
Expand Down

0 comments on commit 79f1041

Please sign in to comment.