From c0dccec3b9877d1707b8958cc82383fb2e849a95 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Tue, 29 Oct 2024 00:21:24 +0000 Subject: [PATCH 01/13] Update docs for the existing top-level configs --- .../src/builder/builder_gen/input_fn.rs | 13 ++-- bon/tests/integration/builder/attr_bon.rs | 24 +++++- website/.vitepress/config.mts | 12 +++ website/changelog.md | 46 ++++++++--- website/reference/builder.md | 2 +- .../reference/builder/member/overwritable.md | 3 + .../reference/builder/member/transparent.md | 3 + .../builder/top-level/builder-type.md | 58 +++++++++++--- website/reference/builder/top-level/derive.md | 63 ++++++++++++++- .../reference/builder/top-level/finish-fn.md | 45 ++++++++++- website/reference/builder/top-level/on.md | 78 +++++++++++++++++-- .../reference/builder/top-level/start-fn.md | 24 +++--- .../reference/builder/top-level/state-mod.md | 3 + 13 files changed, 317 insertions(+), 57 deletions(-) create mode 100644 website/reference/builder/member/overwritable.md create mode 100644 website/reference/builder/member/transparent.md create mode 100644 website/reference/builder/top-level/state-mod.md diff --git a/bon-macros/src/builder/builder_gen/input_fn.rs b/bon-macros/src/builder/builder_gen/input_fn.rs index 7820a303..18faa6d0 100644 --- a/bon-macros/src/builder/builder_gen/input_fn.rs +++ b/bon-macros/src/builder/builder_gen/input_fn.rs @@ -327,11 +327,14 @@ impl<'a> FnInputCtx<'a> { docs: finish_fn_docs, } = self.config.finish_fn; + let is_special_builder_method = self.impl_ctx.is_some() + && (self.fn_item.norm.sig.ident == "new" || self.fn_item.norm.sig.ident == "builder"); + let finish_fn_ident = finish_fn_ident .map(SpannedKey::into_value) .unwrap_or_else(|| { // For `builder` methods the `build` finisher is more conventional - if self.impl_ctx.is_some() && self.start_fn.ident == "builder" { + if is_special_builder_method { format_ident!("build") } else { format_ident!("call") @@ -381,16 +384,14 @@ impl<'a> FnInputCtx<'a> { let ty_prefix = self.self_ty_prefix.unwrap_or_default(); - // A special case for the starting function named `builder`. + // A special case for the `new` or `builder` method. // We don't insert the `Builder` suffix in this case because // this special case should be compatible with deriving // a builder from a struct. // // We can arrive inside of this branch only if the function under - // the macro is called `new` or `builder` without `start_fn` - // name override, or if the `start_fn = builder/start_fn(name = builder)` - // is specified in the macro invocation explicitly. - if self.impl_ctx.is_some() && self.start_fn.ident == "builder" { + // the macro is called `new` or `builder`. + if is_special_builder_method { return format_ident!("{ty_prefix}Builder"); } diff --git a/bon/tests/integration/builder/attr_bon.rs b/bon/tests/integration/builder/attr_bon.rs index 77651467..cf0b593d 100644 --- a/bon/tests/integration/builder/attr_bon.rs +++ b/bon/tests/integration/builder/attr_bon.rs @@ -1,5 +1,21 @@ use crate::prelude::*; +#[test] +fn new_method_special_case() { + struct Sut; + + #[bon] + impl Sut { + #[builder] + fn new() {} + } + + let _: SutBuilder = Sut::builder(); + let builder: SutBuilder = Sut::builder(); + + builder.build(); +} + #[test] fn builder_method_special_case() { struct Sut; @@ -17,7 +33,7 @@ fn builder_method_special_case() { } #[test] -fn builder_start_fn_special_case() { +fn builder_start_fn_is_not_special_case() { struct Sut; #[bon] @@ -26,10 +42,10 @@ fn builder_start_fn_special_case() { fn some_other_name() {} } - let _: SutBuilder = Sut::builder(); - let builder: SutBuilder = Sut::builder(); + let _: SutSomeOtherNameBuilder = Sut::builder(); + let builder: SutSomeOtherNameBuilder = Sut::builder(); - builder.build(); + builder.call(); Sut::some_other_name(); } diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index ac2fdd0f..0c03f7d8 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -218,6 +218,10 @@ export default defineConfig({ text: "start_fn", link: "/reference/builder/top-level/start-fn", }, + { + text: "state_mod", + link: "/reference/builder/top-level/state-mod", + } ], }, { @@ -240,6 +244,10 @@ export default defineConfig({ text: "name", link: "/reference/builder/member/name", }, + { + text: "overwritable", + link: "/reference/builder/member/overwritable", + }, { text: "skip", link: "/reference/builder/member/skip", @@ -248,6 +256,10 @@ export default defineConfig({ text: "start_fn", link: "/reference/builder/member/start-fn", }, + { + text: "transparent", + link: "/reference/builder/member/transparent", + }, ], }, ], diff --git a/website/changelog.md b/website/changelog.md index 5e867af2..ccc795ca 100644 --- a/website/changelog.md +++ b/website/changelog.md @@ -10,31 +10,55 @@ All the breaking changes are very unlikely to actually break your code that was ### Changed -- Reject unnecessary empty attributes e.g. `#[builder()]` or `#[builder]` with no parameters on a member ([#145](https://github.com/elastio/bon/pull/145)) -- Reject non-empty `#[bon(...)]` attribute. This attribute will accept some parameters in future releases ([#145](https://github.com/elastio/bon/pull/145)) -- Reject square brackets and curly braces delimiters for `builder_type`, `finish_fn`, `start_fn` and `on` attributes syntax. Only parentheses are accepted e.g. `#[builder(finish_fn(...))]` or `#[builder(on(...))]`. This no longer works: `#[builder(finish_fn[...])]` or `#[builder(on{...})]` ([#145](https://github.com/elastio/bon/pull/145)) -- `#[builder(derive(Clone, Debug))]` now generates impl blocks that follow the behavior of standard `Clone` and `Debug` derives in that it conservatively adds `Clone/Debug` trait bounds for all the generic types declared on the original item (struct or function). See the *Added* section for details on the way to override these bounds with `#[builder(derive(Clone/Debug(bounds(...))))]`. + +- ⚠️ **Breaking.** Reject unnecessary empty attributes e.g. `#[builder()]` or `#[builder]` with no parameters on a member ([#145](https://github.com/elastio/bon/pull/145)) + +- ⚠️ **Breaking.** Reject square brackets and curly braces delimiters for `builder_type`, `finish_fn`, `start_fn` and `on` attributes syntax. Only parentheses are accepted e.g. `#[builder(finish_fn(...))]` or `#[builder(on(...))]`. This no longer works: `#[builder(finish_fn[...])]` or `#[builder(on{...})]` ([#145](https://github.com/elastio/bon/pull/145)) + +- ⚠️ **Breaking.** Reject non-consecutive `on(...)` clauses. For example, the following now generates a compile error: `#[builder(on(String, into), finish_fn = build, on(Vec<_>, into))]`, because there is a `finish_fn = ...` between `on(...)` clauses. + +- ⚠️ **Breaking.** `#[builder(derive(Clone, Debug))]` now generates impl blocks that follow the behavior of standard `Clone` and `Debug` derives in that it conservatively adds `Clone/Debug` trait bounds for all the generic types declared on the original item (struct or function). Previously no additional bounds were required on `Clone` and `Debug` impls. See the *Added* section for details on the way to override these bounds with `#[builder(derive(Clone/Debug(bounds(...))))]`. + +- ⚠️ **Breaking.** The name of the builder struct generated for methods named `builder` changed from `TBuilderBuilder` to just `TBuilder` making methods name `builder` work the same as methods named `new`. ### Removed -- Removed support for `#[bon::builder]` proc-macro attribute on top of a `struct`. Use `#[derive(bon::Builder)]` for that instead. This syntax has been deprecated since `2.1` and it is now removed as part of a major version cleanup ([#145](https://github.com/elastio/bon/pull/145)) + +- ⚠️ **Breaking.** Remove support for `#[bon::builder]` proc-macro attribute on top of a `struct`. Use `#[derive(bon::Builder)]` for that instead. This syntax has been deprecated since `2.1` and it is now removed as part of a major version cleanup ([#145](https://github.com/elastio/bon/pull/145)) ### Added -- Add `#[builder(builder_type(vis = "..."))]` that allows overriding the visibility of the builder struct ([#145](https://github.com/elastio/bon/pull/145)) -- Add `#[builder(finish_fn(vis = "..."))]` that allows overriding the visibility of the finishing function ([#145](https://github.com/elastio/bon/pull/145)) -- Add `#[builder(with = closure)]` syntax to customize setters with a custom closure. If the closure returns a `Result<_, E>` the setters become fallible ([#145](https://github.com/elastio/bon/pull/145)) -- Add `#[builder(with = Some)]`, `#[builder(with = FromIterator::from_iter)]`, `#[builder(with = <_>::from_iter)]` support for two well-known functions that will probably be used frequently ([#157](https://github.com/elastio/bon/pull/157)) + +- Add `#[builder(builder_type(vis = "...", docs { ... }))]` that allows overriding the visibility and docs of the builder struct ([#145](https://github.com/elastio/bon/pull/145)) + +- Add `#[builder(finish_fn(vis = "...", docs { ... } ))]` that allows overriding the visibility and docs of the finishing function ([#145](https://github.com/elastio/bon/pull/145)) + +- Add `#[builder(start_fn(docs { ... }))]` that allows overriding the docs of the starting function ([#145](https://github.com/elastio/bon/pull/145)) + +- Add `#[builder(with = closure)]` syntax to customize setters with a closure. If the closure returns a `Result<_, E>` the setters become fallible ([#145](https://github.com/elastio/bon/pull/145)) + +- Add `#[builder(with = Some)]`, `#[builder(with = FromIterator::from_iter)]`, `#[builder(with = <_>::from_iter)]` syntax support for two well-known functions that will probably be used frequently ([#157](https://github.com/elastio/bon/pull/157)) + - Add `#[builder(transparent)]` for `Option` fields to opt out from their special handling which makes `bon` treat them as regular required fields. It's also available at the top-level via `#[builder(on(_, transparent))]` ([#145](https://github.com/elastio/bon/pull/145), [#155](https://github.com/elastio/bon/pull/155)) + - Add `#[builder(state_mod)]` to configure the builder's type state API module name, visibility and docs ([#145](https://github.com/elastio/bon/pull/145)) -- Add `#[builder(overwritable)]` and `#[builder(on(..., overwritable)]` to make it possible to call setters multiple times for the same member ([#145](https://github.com/elastio/bon/pull/145)) + +- 🔬 **Experimental.** Add `#[builder(overwritable)]` and `#[builder(on(..., overwritable)]` to make it possible to call setters multiple times for the same member. This attribute is available under the cargo feature `"experimental-overwritable"` (stabilization is tracked in [#149](https://github.com/elastio/bon/issues/149), let us know if you need this attribute!). ([#145](https://github.com/elastio/bon/pull/145)) + - Add `#[builder(setters)]` to fine-tune the setters names, visibility and docs ([#145](https://github.com/elastio/bon/pull/145)) + - Add `#[builder(derive(Clone/Debug(bounds(...))]` to allow overriding trait bounds on the `Clone/Debug` impl block of the builder ([#145](https://github.com/elastio/bon/pull/145)) + - Improve rustdoc output ([#145](https://github.com/elastio/bon/pull/145)) + - Add info that the member is required or optional. + - For members with defaults values show the default value in the docs. + - For optional members provide a link to a companion setter. The docs for `{member}(T)` setter mention the `maybe_{member}(Option)` setter and vice versa. + - Remove `__` prefixes for generic types and lifetimes from internal symbols. Instead, the prefixes added only if the macro detects a name collision. -- Add inheritance of `#[allow()]` and `#[expect()]` lint attributes to all generated items. This is useful to suppress any lints coming from the generated code. Although, lints coming from the generated code are generally considered defects in `bon` and should be reported via a Github issue but this provides an easy temporary workaround the problem ([#145](https://github.com/elastio/bon/pull/145)) + +- Add inheritance of `#[allow()]` and `#[expect()]` lint attributes to all generated items. This is useful to suppress any lints coming from the generated code. Although, lints coming from the generated code are generally considered defects in `bon` and should be reported via a Github issue but this provides an easy temporary workaround for the problem ([#145](https://github.com/elastio/bon/pull/145)) ### Fixed diff --git a/website/reference/builder.md b/website/reference/builder.md index 2974d47a..cf8d2d8f 100644 --- a/website/reference/builder.md +++ b/website/reference/builder.md @@ -59,7 +59,7 @@ Click on the name of the attribute to view detailed docs. | Attribute | Short description | -- | -- | -| [`builder_type`](./builder/top-level/builder-type) | Overrides the name, visibility and docs of the generated builder +| [`builder_type`](./builder/top-level/builder-type) | Overrides the name, visibility and docs of the builder struct | [`derive`](./builder/top-level/derive) | Generates additional derives on the builder struct itself | [`finish_fn`](./builder/top-level/finish-fn) | Overrides the name, visibility and docs of the finishing function | [`on`](./builder/top-level/on) | Applies the given builder attributes to all members matching a type pattern diff --git a/website/reference/builder/member/overwritable.md b/website/reference/builder/member/overwritable.md new file mode 100644 index 00000000..b6ace458 --- /dev/null +++ b/website/reference/builder/member/overwritable.md @@ -0,0 +1,3 @@ +# 🔬 `overwritable` + +This is an **experimental** attribute available under the cargo feature `"experimental-overwritable"`. diff --git a/website/reference/builder/member/transparent.md b/website/reference/builder/member/transparent.md new file mode 100644 index 00000000..bd583b9c --- /dev/null +++ b/website/reference/builder/member/transparent.md @@ -0,0 +1,3 @@ +# `transparent` + +TODO: add docs diff --git a/website/reference/builder/top-level/builder-type.md b/website/reference/builder/top-level/builder-type.md index bffd9dc2..8962560f 100644 --- a/website/reference/builder/top-level/builder-type.md +++ b/website/reference/builder/top-level/builder-type.md @@ -1,23 +1,55 @@ +--- +outline: deep +--- + # `builder_type` **Applies to:** -Overrides the name of the generated builder struct. +Overrides the name, visibility and docs of the builder struct. + +**Short syntax** configures just the *name*. + +```attr +#[builder(builder_type = CustomName)] +``` + +**Long syntax** provides more flexibility. All keys are optional. + +```attr +#[builder( + builder_type( + name = CustomName, + vis = "pub(crate)", + doc { + /// Custom docs + } + ) +)] +``` -The default naming pattern is the following: +## `name` -| Underlying item | Naming pattern | -| -------------------------- | --------------------------------------------- | -| Struct | `{StructName}Builder` | -| `StructName::new()` method | `{StructName}Builder` | -| Free function | `{PascalCaseFunctionName}Builder` | -| Associated method | `{SelfTypeName}{PascalCaseMethodName}Builder` | +The default name for the builder struct is chosen according to the following rules: -The attribute expects the desired builder type identifier as its input. +| Syntax | Default +| -----------------------------------|------------------------ +| `struct T` | `{T}Builder` +| Associated `fn` `T::new/builder()` | `{T}Builder` +| Associated `fn` `T::fn_name()` | `{T}{FnName}Builder` +| Free `fn` `fn_name()` | `{FnName}Builder` -### `vis` +## `vis` -**Example:** +The visibility must be enclosed with quotes. Use `""` or [`"pub(self)"`](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself) for private visibility. + +The default visibility is the same as the visibility of the underlying `struct` or `fn`. + +## `doc` + +Simple documentation is generated by default. The syntax of this attribute expects a block with doc comments. + +## Examples ::: code-group @@ -58,7 +90,9 @@ let builder: MyBuilder = Brush::builder(); ::: -You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name annotated with `#[builder]`: +--- + +You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name both annotated with `#[derive(Builder)]` and `#[builder]`: ::: code-group diff --git a/website/reference/builder/top-level/derive.md b/website/reference/builder/top-level/derive.md index c216651f..66acbc5a 100644 --- a/website/reference/builder/top-level/derive.md +++ b/website/reference/builder/top-level/derive.md @@ -2,10 +2,9 @@ **Applies to:** -*⚠️ Do not confuse this with `derive(bon::Builder)`⚠️* - -Generates additional derives on the builder struct itself. The syntax is similar to the regular `#[derive(...)]` attribute, but it must be wrapped in `#[builder(derive(...))]`. Expects one or more of the supported derives separated by commas. +*⚠️ Do not confuse this with `#[derive(bon::Builder)]`⚠️* +Generates additional derives on the builder struct itself. The syntax is similar to the regular `#[derive(...)]` attribute, but it must be wrapped in `#[builder(derive(...))]`. Expects one or more of the supported derives separated by a comma. The following derives are supported: `Clone`, `Debug`. @@ -120,6 +119,64 @@ assert_eq!( ::: +## Generic types handling + +If the underlying `struct` or `fn` contains generic type parameters, then the generated impl block will include a `where` bound requiring the respective trait (`Clone` or `Debug`) to be implemented by all of them. This follows the behavior of the [standard `derive` macros](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable). + +This works fine in most of the cases, but sometimes the generated bounds may be overly restrictive. To fix that, you can manually specify the bounds using the syntax `#[builder(derive(Trait(bounds(...))))]`, where `...` is a comma-separated list of `where` bounds. + +See the example of this problem, and how it can be fixed (click on the tab `Fixed` in the code snippet): + +::: code-group + +```rust [Overly restrictive] +use bon::Builder; +use std::rc::Rc; + +#[derive(Builder)] +#[builder(derive(Clone))] +struct Example { + x: Rc, + y: U, +} + +struct NonCloneable; + +let builder = Example::<_, ()>::builder().x(Rc::new(NonCloneable)); + +// `Rc` can be cloned even if `T` is not `Clone`, but this code // [!code error] +// doesn't compile, because the `Clone` impl for `ExampleBuilder` // [!code error] +// conservatively requires `T: Clone` // [!code error] +builder.clone(); // [!code error] +``` + +```rust [Fixed] +use bon::Builder; +use std::rc::Rc; + +#[derive(Builder)] +// Only a bound `U: Clone` is needed in this case // [!code highlight] +#[builder(derive(Clone(bounds(U: Clone))))] // [!code highlight] +struct Example { + x: Rc, + y: U, +} + +struct NonCloneable; + +let builder = Example::<_, ()>::builder().x(Rc::new(NonCloneable)); + +// Now this works, because there is no bound `T: Clone` // [!code highlight] +builder.clone(); +``` +::: + + +::: tip +If you'd like to know why this attribute is this dumb and doesn't just add a `where Rc: Clone` bound instead, then check this article about the ["Implied bounds and perfect derive"](https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/) by Niko Matsakis 📖. + +::: + ## Compile errors _Requires_ that all members of the builder including the receiver (if this is a builder for an associated method) implement the target trait. For example, this doesn't compile because not all members implement `Clone`: diff --git a/website/reference/builder/top-level/finish-fn.md b/website/reference/builder/top-level/finish-fn.md index 2fc27f78..94ddf67a 100644 --- a/website/reference/builder/top-level/finish-fn.md +++ b/website/reference/builder/top-level/finish-fn.md @@ -2,9 +2,50 @@ **Applies to:** -This attribute allows overriding the name of the generated builder's method that finishes the building process. +Overrides the name, visibility and docs of the builder's method that finishes the building process, i.e. returns the resulting object (in case of `#[derive(Builder)]` on a `struct`) or invokes the underlying function (in case of `#[builder]` on an `fn`). It is commonly referred to as the "finishing function". -**Example:** +**Short syntax** configures just the *name*. + +```attr +#[builder(finish_fn = custom_name)] +``` + +**Long syntax** provides more flexibility. All keys are optional. + +```attr +#[builder( + finish_fn( + name = custom_name, + vis = "pub(crate)", + doc { + /// Custom docs + } + ) +)] +``` + +## `name` + +The default name for the starting function is chosen according to the following rules: + +| Syntax | Default +|--------------------------------|---------- +| `struct T` | `builder` +| Associated `fn` `T::new()` | `builder` +| Associated `fn` `T::fn_name()` | `{fn_name}` +| Free `fn fn_name()` | `{fn_name}` + +## `vis` + +The visibility must be enclosed with quotes. Use `""` or [`"pub(self)"`](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself) for private visibility. + +The default visibility is the same as the visibility of the [`builder_type`](./builder-type#vis), which in turn, defaults to the visibility of the underlying `struct` or `fn`. + +## `doc` + +Simple documentation is generated by default. The syntax of this attribute expects a block with doc comments. + +## Examples ::: code-group diff --git a/website/reference/builder/top-level/on.md b/website/reference/builder/top-level/on.md index 1afd13d0..666f9396 100644 --- a/website/reference/builder/top-level/on.md +++ b/website/reference/builder/top-level/on.md @@ -1,3 +1,7 @@ +--- +outline: deep +--- + # `on` **Applies to:** @@ -79,23 +83,37 @@ Example::example() This attribute must be of form `on(type_pattern, attributes)`. -- `type_pattern` - type that will be compared with the types of the members. The types are compared textually. For example, `String` doesn't match `std::string::String`. You can use `_` to mark parts of the type to ignore when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. +## Type pattern + +`type_pattern` is a type that will be compared with the types of the members. The types are compared textually. For example, `String` doesn't match `std::string::String` because, internally, they are compared just like strings `"String" == "std::string::String"`. -- `attributes` - for now, the only attribute supported in the `attributes` position is [`into`](../member/into). It sets `#[builder(into)]` for members that match the `type_pattern`. +You can use `_` to mark parts of the type to ignore when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. -If you want to apply the `attributes` to all members, you can use the `_` type pattern that matches any type. For example, `#[builder(on(_, into))]`. +If you want to apply the attributes to all members, you can use the `_` type pattern that matches any type. For example, `#[builder(on(_, into))]`. For optional members the underlying type is matched ignoring the `Option` wrapper. -**Example:** +## Attributes -```rust +There are several attributes supported in the `attributes` position listed below. A single `on(...)` clause can contain several of these separated by a comma e.g. `on(_, into, transparent)`. + +- [`into`](../member/into); +- [`transparent`](../member/transparent) - currently, this attribute can only be used with the `_` type pattern as the first `on(...)` clause; +- [`ovewritable`](../member/overwritable) - 🔬 **experimental**, this attribute is available under the cargo feature `"experimental-overwritable"` (see the issue [#149](https://github.com/elastio/bon/issues/149)); + + +## Examples + +::: code-group + +```rust [into] use bon::Builder; #[derive(Builder)] -#[builder(on(String, into))] +#[builder(on(String, into))] // [!code highlight] struct Example { name: String, + description: Option, #[builder(default)] @@ -111,7 +129,49 @@ Example::builder() .build(); ``` -You can specify `on(...)` multiple times. +```rust [transparent] +use bon::Builder; + +#[derive(Builder)] +#[builder(on(_, transparent))] // [!code highlight] +struct Example { + name: String, + level: Option, + description: Option, +} + +Example::builder() + .name("regular required member".to_owned()) + .level(Some(99)) + .description(Some("transparent `Option`".to_owned())) + .build(); +``` + +```rust [overwritable] +use bon::Builder; + +#[derive(Builder)] +#[builder(on(_, overwritable))] // [!code highlight] +struct Example { + x: u32, + y: Option, +} + +Example::builder() + // Now we can call setters for the same member multiple times + .x(2) + .x(99) + // Same also works for optional members + .y(1) + .maybe_y(None) + .y(2) + .build(); +``` + + +::: + +You can specify `on(...)` multiple times. All `on(...)` clauses must be consecutive (no other attributes between them are allowed). **Example:** @@ -135,3 +195,7 @@ Example::builder() .level(100) .build(); ``` + +## Future releases + +There is an issue [#152](https://github.com/elastio/bon/issues/152) about adding support for [`default`](../member/default.md), [`with`](../member/with) and other non-boolean attributes to the `on(...)` clause. We'll be glad if you take a look at the design proposed in that issue and put a 👍 if you like/want this feature or leave comment if you have some more feedback. diff --git a/website/reference/builder/top-level/start-fn.md b/website/reference/builder/top-level/start-fn.md index 94ee4274..b9066186 100644 --- a/website/reference/builder/top-level/start-fn.md +++ b/website/reference/builder/top-level/start-fn.md @@ -26,28 +26,28 @@ When this attribute is used with `fn` syntax, it additionally [exposes the origi )] ``` -### `name` +## `name` -The default name for the starting function depends on the syntax used. +The default name for the starting function is chosen according to the following rules: -| Syntax | Default -| -- | -- | -| `struct` | `builder` -| associated `fn` called `new` | `builder` -| associated `fn` *not* called `new` | `{function_name}` -| free `fn` | `{function_name}` +| Syntax | Default +|--------------------------------|---------- +| `struct T` | `builder` +| Associated `fn` `T::new()` | `builder` +| Associated `fn` `T::fn_name()` | `{fn_name}` +| Free `fn fn_name()` | `{fn_name}` -### `vis` +## `vis` The visibility must be enclosed with quotes. Use `""` or [`"pub(self)"`](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself) for private visibility. The default visibility is the same as the visibility of the [`builder_type`](./builder-type#vis), which in turn, defaults to the visibility of the underlying `struct` or `fn`. -### `doc` +## `doc` Simple documentation is generated by default. The syntax of this attribute expects a block with doc comments. -## Example +## Examples ::: code-group @@ -83,6 +83,8 @@ User::init() // [!code highlight] ::: +See examples with `fn` syntax in the section below. + ## Exposing original function diff --git a/website/reference/builder/top-level/state-mod.md b/website/reference/builder/top-level/state-mod.md new file mode 100644 index 00000000..8490ab72 --- /dev/null +++ b/website/reference/builder/top-level/state-mod.md @@ -0,0 +1,3 @@ +# `state_mod` + +TODO: add docs From 553deb793d5c08d841180fc92b8a1a6afc9b309a Mon Sep 17 00:00:00 2001 From: Veetaha Date: Tue, 29 Oct 2024 20:20:43 +0000 Subject: [PATCH 02/13] Add the rest of breaking changes into the changelog --- website/.vitepress/config.mts | 13 +++++++- website/changelog.md | 33 +++++++++++++++---- website/reference/bon/crate.md | 3 ++ .../reference/builder/member/overwritable.md | 2 +- website/reference/builder/top-level/crate.md | 3 ++ 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 website/reference/bon/crate.md create mode 100644 website/reference/builder/top-level/crate.md diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 0c03f7d8..a1131da6 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -202,6 +202,10 @@ export default defineConfig({ text: "builder_type", link: "/reference/builder/top-level/builder-type", }, + { + text: "crate", + link: "/reference/builder/top-level/crate", + }, { text: "derive", link: "/reference/builder/top-level/derive", @@ -245,7 +249,7 @@ export default defineConfig({ link: "/reference/builder/member/name", }, { - text: "overwritable", + text: "overwritable 🔬", link: "/reference/builder/member/overwritable", }, { @@ -267,6 +271,13 @@ export default defineConfig({ { text: "#[bon]", link: "/reference/bon", + collapsed: true, + items: [ + { + text: "crate", + link: "/reference/bon/crate", + } + ] }, { text: "Other items on docs.rs", diff --git a/website/changelog.md b/website/changelog.md index ccc795ca..47999f8d 100644 --- a/website/changelog.md +++ b/website/changelog.md @@ -10,23 +10,42 @@ All the breaking changes are very unlikely to actually break your code that was ### Changed - - ⚠️ **Breaking.** Reject unnecessary empty attributes e.g. `#[builder()]` or `#[builder]` with no parameters on a member ([#145](https://github.com/elastio/bon/pull/145)) - ⚠️ **Breaking.** Reject square brackets and curly braces delimiters for `builder_type`, `finish_fn`, `start_fn` and `on` attributes syntax. Only parentheses are accepted e.g. `#[builder(finish_fn(...))]` or `#[builder(on(...))]`. This no longer works: `#[builder(finish_fn[...])]` or `#[builder(on{...})]` ([#145](https://github.com/elastio/bon/pull/145)) -- ⚠️ **Breaking.** Reject non-consecutive `on(...)` clauses. For example, the following now generates a compile error: `#[builder(on(String, into), finish_fn = build, on(Vec<_>, into))]`, because there is a `finish_fn = ...` between `on(...)` clauses. +- ⚠️ **Breaking.** Reject non-consecutive `on(...)` clauses. For example, the following now generates a compile error: `#[builder(on(String, into), finish_fn = build, on(Vec<_>, into))]`, because there is a `finish_fn = ...` between `on(...)` clauses. ([#155](https://github.com/elastio/bon/pull/155)) + +- ⚠️ **Breaking.** `#[builder(derive(Clone, Debug))]` now generates impl blocks that follow the behaviour of standard `Clone` and `Debug` derives in that it conservatively adds `Clone/Debug` trait bounds for all the generic types declared on the original item (struct or function). Previously no additional bounds were required on `Clone` and `Debug` impls. See the *Added* section for details on the way to override these bounds with `#[builder(derive(Clone/Debug(bounds(...))))]`. + +- ⚠️ **Breaking.** The name of the builder struct generated for methods named `builder` changed from `TBuilderBuilder` to just `TBuilder` making methods name `builder` work the same as methods named `new`. ([#145](https://github.com/elastio/bon/pull/145)) -- ⚠️ **Breaking.** `#[builder(derive(Clone, Debug))]` now generates impl blocks that follow the behavior of standard `Clone` and `Debug` derives in that it conservatively adds `Clone/Debug` trait bounds for all the generic types declared on the original item (struct or function). Previously no additional bounds were required on `Clone` and `Debug` impls. See the *Added* section for details on the way to override these bounds with `#[builder(derive(Clone/Debug(bounds(...))))]`. +- ⚠️ **Breaking.** The type of the builder is now dependent on the order of the setters' invocation. This should not break anything unless you had code like the following that will stop compiling: + ```rust + let builder = if condition { + Foo::builder().a(1).b(2) + } else { + Foo::builder().b(1).a(2) + }; -- ⚠️ **Breaking.** The name of the builder struct generated for methods named `builder` changed from `TBuilderBuilder` to just `TBuilder` making methods name `builder` work the same as methods named `new`. + builder.build(); + ``` + + This is because the types of the builders returned from the branches are the following: + - `FooBuilder>` (`if` branch) + - `FooBuilder>` (`else` branch) + + We believe such code should generally be very rare and even if it breaks, it's easy to fix it by reordering the setter method calls. This compromise was accepted as a design tradeoff such that the builder's type signature becomes simpler, the generated documentation becomes much less noisy, it removes an annoying special case for the builder of just one member, and it improves the type-checking performance considerably compared to the previous approach that used tuples to represent the type state. ([#145](https://github.com/elastio/bon/pull/145)) ### Removed - ⚠️ **Breaking.** Remove support for `#[bon::builder]` proc-macro attribute on top of a `struct`. Use `#[derive(bon::Builder)]` for that instead. This syntax has been deprecated since `2.1` and it is now removed as part of a major version cleanup ([#145](https://github.com/elastio/bon/pull/145)) +- ⚠️ **Breaking.** Remove `#[builder(expose_positional_fn = positional_fn_name)]` attribute. Use `#[builder(start_fn = builder_fn_name)]` instead, since this attribute works additively keeping the function with positional arguments under the attribute unchanged. + ### Added +- ⚠️ **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 = "...", docs { ... }))]` that allows overriding the visibility and docs of the builder struct ([#145](https://github.com/elastio/bon/pull/145)) @@ -52,13 +71,13 @@ All the breaking changes are very unlikely to actually break your code that was - Add info that the member is required or optional. - - For members with defaults values show the default value in the docs. + - For members with default values show the default value in the docs. - For optional members provide a link to a companion setter. The docs for `{member}(T)` setter mention the `maybe_{member}(Option)` setter and vice versa. - Remove `__` prefixes for generic types and lifetimes from internal symbols. Instead, the prefixes added only if the macro detects a name collision. -- Add inheritance of `#[allow()]` and `#[expect()]` lint attributes to all generated items. This is useful to suppress any lints coming from the generated code. Although, lints coming from the generated code are generally considered defects in `bon` and should be reported via a Github issue but this provides an easy temporary workaround for the problem ([#145](https://github.com/elastio/bon/pull/145)) +- Add inheritance of `#[allow()]` and `#[expect()]` lint attributes to all generated items. This is useful to suppress any lints coming from the generated code. Although, lints coming from the generated code are generally considered defects in `bon` and should be reported via a Github issue, but this provides an easy temporary workaround for the problem ([#145](https://github.com/elastio/bon/pull/145)) ### Fixed @@ -67,7 +86,7 @@ All the breaking changes are very unlikely to actually break your code that was ### Other -- Add graceful internal panic handling. If some `bon` macro panics due to an internal bug, the macro will try to still generate a fallback for IDEs to still provide intellisense ([#145](https://github.com/elastio/bon/pull/145)) +- Add graceful internal panic handling. If some `bon` macro panics due to an internal bug, the macro will try to generate a fallback for IDEs to still provide intellisense ([#145](https://github.com/elastio/bon/pull/145)) - Switch from `elastio.github.io/bon` to a custom domain `bon-rs.com` ([#158](https://github.com/elastio/bon/pull/158)) - Add anonymous stats with [`umami`](https://umami.is/) for the docs website ([#158](https://github.com/elastio/bon/pull/158)) diff --git a/website/reference/bon/crate.md b/website/reference/bon/crate.md new file mode 100644 index 00000000..564e0c7b --- /dev/null +++ b/website/reference/bon/crate.md @@ -0,0 +1,3 @@ +# `crate` + +TODO: add docs diff --git a/website/reference/builder/member/overwritable.md b/website/reference/builder/member/overwritable.md index b6ace458..47c7b9e2 100644 --- a/website/reference/builder/member/overwritable.md +++ b/website/reference/builder/member/overwritable.md @@ -1,3 +1,3 @@ -# 🔬 `overwritable` +# `overwritable` 🔬 This is an **experimental** attribute available under the cargo feature `"experimental-overwritable"`. diff --git a/website/reference/builder/top-level/crate.md b/website/reference/builder/top-level/crate.md new file mode 100644 index 00000000..564e0c7b --- /dev/null +++ b/website/reference/builder/top-level/crate.md @@ -0,0 +1,3 @@ +# `crate` + +TODO: add docs From 45ee2a16b68fbf8935666fcb4fb98d4d54034248 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Tue, 29 Oct 2024 20:28:01 +0000 Subject: [PATCH 03/13] Add `#[builder/bon(crate)]` to the changelog --- website/changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/changelog.md b/website/changelog.md index 47999f8d..00b98090 100644 --- a/website/changelog.md +++ b/website/changelog.md @@ -41,7 +41,7 @@ All the breaking changes are very unlikely to actually break your code that was - ⚠️ **Breaking.** Remove support for `#[bon::builder]` proc-macro attribute on top of a `struct`. Use `#[derive(bon::Builder)]` for that instead. This syntax has been deprecated since `2.1` and it is now removed as part of a major version cleanup ([#145](https://github.com/elastio/bon/pull/145)) -- ⚠️ **Breaking.** Remove `#[builder(expose_positional_fn = positional_fn_name)]` attribute. Use `#[builder(start_fn = builder_fn_name)]` instead, since this attribute works additively keeping the function with positional arguments under the attribute unchanged. +- ⚠️ **Breaking.** Remove `#[builder(expose_positional_fn = positional_fn_name)]` attribute. Use `#[builder(start_fn = builder_fn_name)]` instead, since this attribute works additively keeping the function with positional arguments under the attribute unchanged. ([#153](https://github.com/elastio/bon/pull/153)) ### Added @@ -59,6 +59,8 @@ All the breaking changes are very unlikely to actually break your code that was - Add `#[builder(transparent)]` for `Option` fields to opt out from their special handling which makes `bon` treat them as regular required fields. It's also available at the top-level via `#[builder(on(_, transparent))]` ([#145](https://github.com/elastio/bon/pull/145), [#155](https://github.com/elastio/bon/pull/155)) +- Add `#[builder(crate = path::to::bon)]` and `#[bon(crate = path::to::bon)]` to allow overriding the path to `bon` crate used in the generated code, which is useful for the cases when `bon` macros are wrapped by other macros ([#153](https://github.com/elastio/bon/pull/153)) + - Add `#[builder(state_mod)]` to configure the builder's type state API module name, visibility and docs ([#145](https://github.com/elastio/bon/pull/145)) - 🔬 **Experimental.** Add `#[builder(overwritable)]` and `#[builder(on(..., overwritable)]` to make it possible to call setters multiple times for the same member. This attribute is available under the cargo feature `"experimental-overwritable"` (stabilization is tracked in [#149](https://github.com/elastio/bon/issues/149), let us know if you need this attribute!). ([#145](https://github.com/elastio/bon/pull/145)) From 3d07787d7f3c63f0b29d7f3d023fdc580ebaaafc Mon Sep 17 00:00:00 2001 From: Veetaha Date: Wed, 30 Oct 2024 02:14:50 +0000 Subject: [PATCH 04/13] Fix highlighting with RA and update vitepress --- bon-macros/src/builder/item_fn.rs | 12 +++-- bon-macros/src/builder/item_impl.rs | 11 ++++- website/package-lock.json | 76 ++++++++++++++--------------- website/package.json | 2 +- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/bon-macros/src/builder/item_fn.rs b/bon-macros/src/builder/item_fn.rs index dc49b557..14b7cce6 100644 --- a/bon-macros/src/builder/item_fn.rs +++ b/bon-macros/src/builder/item_fn.rs @@ -34,13 +34,15 @@ pub(crate) fn generate( } = ctx.into_builder_gen_ctx()?.output()?; Ok(quote! { - #start_fn - #other_items - - // Keep original function at the end. It seems like rust-analyzer + // Keep original function at the top. It seems like rust-analyzer // does better job of highlighting syntax when it is here. Assuming - // this is because rust-analyzer prefers the last occurrence of the + // this is because rust-analyzer prefers the first occurrence of the // span when highlighting. + // + // See this issue for details: https://github.com/rust-lang/rust-analyzer/issues/18438 #adapted_fn + + #start_fn + #other_items }) } diff --git a/bon-macros/src/builder/item_impl.rs b/bon-macros/src/builder/item_impl.rs index 4831db4c..7740f663 100644 --- a/bon-macros/src/builder/item_impl.rs +++ b/bon-macros/src/builder/item_impl.rs @@ -159,7 +159,7 @@ pub(crate) fn generate( let new_impl_items = outputs.iter().flat_map(|(adapted_fn, output)| { let start_fn = &output.start_fn; - [syn::parse_quote!(#start_fn), syn::parse_quote!(#adapted_fn)] + [syn::parse_quote!(#adapted_fn), syn::parse_quote!(#start_fn)] }); norm_selfful_impl_block.items = other_items; @@ -168,8 +168,15 @@ pub(crate) fn generate( let other_items = outputs.iter().map(|(_, output)| &output.other_items); Ok(quote! { - #(#other_items)* + // Keep the original impl block at the top. It seems like rust-analyzer + // does better job of highlighting syntax when it is here. Assuming + // this is because rust-analyzer prefers the first occurrence of the + // span when highlighting. + // + // See this issue for details: https://github.com/rust-lang/rust-analyzer/issues/18438 #norm_selfful_impl_block + + #(#other_items)* }) } diff --git a/website/package-lock.json b/website/package-lock.json index 040b5e68..2d03cda3 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -21,7 +21,7 @@ "smol-toml": "^1.3.0", "ts-node": "^10.9.2", "ts-pattern": "^5.5.0", - "vitepress": "^1.4.1" + "vitepress": "^1.4.2" } }, "node_modules/@algolia/autocomplete-core": { @@ -1115,53 +1115,53 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.1.tgz", - "integrity": "sha512-bqAhT/Ri5ixV4oYsvJNH8UJjpjbINWlWyXY6tBTsP4OmD6XnFv43nRJ+lTdxd2rmG5pgam/x+zGR6kLRXrpEKA==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.2.tgz", + "integrity": "sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==", "dev": true, "dependencies": { - "@shikijs/engine-javascript": "1.22.1", - "@shikijs/engine-oniguruma": "1.22.1", - "@shikijs/types": "1.22.1", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.3" } }, "node_modules/@shikijs/engine-javascript": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.1.tgz", - "integrity": "sha512-540pyoy0LWe4jj2BVbgELwOFu1uFvRI7lg4hdsExrSXA9x7gqfzZ/Nnh4RfX86aDAgJ647gx4TCmRwACbnQSvw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz", + "integrity": "sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==", "dev": true, "dependencies": { - "@shikijs/types": "1.22.1", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "oniguruma-to-js": "0.4.3" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.1.tgz", - "integrity": "sha512-L+1Vmd+a2kk8HtogUFymQS6BjUfJnzcWoUp1BUgxoDiklbKSMvrsMuLZGevTOP1m0rEjgnC5MsDmsr8lX1lC+Q==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz", + "integrity": "sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==", "dev": true, "dependencies": { - "@shikijs/types": "1.22.1", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0" } }, "node_modules/@shikijs/transformers": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.22.1.tgz", - "integrity": "sha512-KvG49YFV6gV116sC4L3Sn1Rp6HXsioMKBBG373j088rw849440hm8s2r+/dgjsGLvT4p+QB7newev+5a3ARM6w==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.22.2.tgz", + "integrity": "sha512-8f78OiBa6pZDoZ53lYTmuvpFPlWtevn23bzG+azpPVvZg7ITax57o/K3TC91eYL3OMJOO0onPbgnQyZjRos8XQ==", "dev": true, "dependencies": { - "shiki": "1.22.1" + "shiki": "1.22.2" } }, "node_modules/@shikijs/types": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.1.tgz", - "integrity": "sha512-+45f8mu/Hxqs6Kyhfm98Nld5n7Q7lwhjU8UtdQwrOPs7BnM4VAb929O3IQ2ce+4D7SlNFlZGd8CnKRSnwbQreQ==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.2.tgz", + "integrity": "sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==", "dev": true, "dependencies": { "@shikijs/vscode-textmate": "^9.3.0", @@ -2646,15 +2646,15 @@ "peer": true }, "node_modules/shiki": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.1.tgz", - "integrity": "sha512-PbJ6XxrWLMwB2rm3qdjIHNm3zq4SfFnOx0B3rEoi4AN8AUngsdyZ1tRe5slMPtn6jQkbUURLNZPpLR7Do3k78g==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.2.tgz", + "integrity": "sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==", "dev": true, "dependencies": { - "@shikijs/core": "1.22.1", - "@shikijs/engine-javascript": "1.22.1", - "@shikijs/engine-oniguruma": "1.22.1", - "@shikijs/types": "1.22.1", + "@shikijs/core": "1.22.2", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4" } @@ -3019,27 +3019,27 @@ } }, "node_modules/vitepress": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.4.1.tgz", - "integrity": "sha512-C2rQ7PMlDVqgsaHOa0uJtgGGWaGv74QMaGL62lxKbtFkYtosJB5HAfZ8+pEbfzzvLemYaYwaiQdFLBlexK2sFw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.4.2.tgz", + "integrity": "sha512-10v92Lqx0N4r7YC3cQLBvu+gRS2rHviE7vgdKiwlupUGfSWkyiQDqYccxM5iPStDGSi1Brnec1lf+lmhaQcZXw==", "dev": true, "dependencies": { "@docsearch/css": "^3.6.2", "@docsearch/js": "^3.6.2", - "@shikijs/core": "^1.22.0", - "@shikijs/transformers": "^1.22.0", - "@shikijs/types": "^1.22.0", + "@shikijs/core": "^1.22.2", + "@shikijs/transformers": "^1.22.2", + "@shikijs/types": "^1.22.2", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.1.4", - "@vue/devtools-api": "^7.4.6", + "@vue/devtools-api": "^7.5.4", "@vue/shared": "^3.5.12", "@vueuse/core": "^11.1.0", "@vueuse/integrations": "^11.1.0", "focus-trap": "^7.6.0", "mark.js": "8.11.1", "minisearch": "^7.1.0", - "shiki": "^1.22.0", - "vite": "^5.4.8", + "shiki": "^1.22.2", + "vite": "^5.4.10", "vue": "^3.5.12" }, "bin": { diff --git a/website/package.json b/website/package.json index 4c762196..5ac9560d 100644 --- a/website/package.json +++ b/website/package.json @@ -20,7 +20,7 @@ "smol-toml": "^1.3.0", "ts-node": "^10.9.2", "ts-pattern": "^5.5.0", - "vitepress": "^1.4.1" + "vitepress": "^1.4.2" }, "dependencies": { "vue": "^3.5.12" From fa42a6f46c7ea0de38c3e9df176b1831ef8ab035 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Wed, 30 Oct 2024 03:04:41 +0000 Subject: [PATCH 05/13] Fixes from grammarly --- website/.vitepress/config.mts | 2 +- website/guide/patterns/conditional-building.md | 2 +- website/reference/builder/top-level/derive.md | 6 +++--- website/reference/builder/top-level/on.md | 12 +++++------- website/reference/builder/top-level/start-fn.md | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index a1131da6..6b828d97 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -75,6 +75,7 @@ export default defineConfig({ ], ], + // https://vitepress.dev/reference/default-theme-config themeConfig: { logo: "/bon-logo-thumb.png", @@ -95,7 +96,6 @@ export default defineConfig({ provider: "local", }, - // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Guide", link: "/guide/overview" }, { text: "Reference", link: "/reference/builder" }, diff --git a/website/guide/patterns/conditional-building.md b/website/guide/patterns/conditional-building.md index d7b373a0..282785f2 100644 --- a/website/guide/patterns/conditional-building.md +++ b/website/guide/patterns/conditional-building.md @@ -1,6 +1,6 @@ # 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 the builders generated by `bon` use the type-state pattern and the 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 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 📚. The patterns described here aren't mutually exclusive. You can combine them as you see necessary to keep your code clean. diff --git a/website/reference/builder/top-level/derive.md b/website/reference/builder/top-level/derive.md index 66acbc5a..90b204c7 100644 --- a/website/reference/builder/top-level/derive.md +++ b/website/reference/builder/top-level/derive.md @@ -4,7 +4,7 @@ *⚠️ Do not confuse this with `#[derive(bon::Builder)]`⚠️* -Generates additional derives on the builder struct itself. The syntax is similar to the regular `#[derive(...)]` attribute, but it must be wrapped in `#[builder(derive(...))]`. Expects one or more of the supported derives separated by a comma. +Generates additional derives for the builder struct itself. The syntax is similar to the regular `#[derive(...)]` attribute, but it must be wrapped in `#[builder(derive(...))]`. Expects one or more of the supported derives separated by a comma. The following derives are supported: `Clone`, `Debug`. @@ -121,9 +121,9 @@ assert_eq!( ## Generic types handling -If the underlying `struct` or `fn` contains generic type parameters, then the generated impl block will include a `where` bound requiring the respective trait (`Clone` or `Debug`) to be implemented by all of them. This follows the behavior of the [standard `derive` macros](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable). +If the underlying `struct` or `fn` contains generic type parameters, then the generated impl block will include a `where` bound requiring the respective trait (`Clone` or `Debug`) to be implemented by all of them. This follows the behaviour of the [standard `derive` macros](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable). -This works fine in most of the cases, but sometimes the generated bounds may be overly restrictive. To fix that, you can manually specify the bounds using the syntax `#[builder(derive(Trait(bounds(...))))]`, where `...` is a comma-separated list of `where` bounds. +This works fine in most cases, but sometimes the generated bounds may be overly restrictive. To fix that, you can manually specify the bounds using the syntax `#[builder(derive(Trait(bounds(...))))]`, where `...` is a comma-separated list of `where` bounds. See the example of this problem, and how it can be fixed (click on the tab `Fixed` in the code snippet): diff --git a/website/reference/builder/top-level/on.md b/website/reference/builder/top-level/on.md index 666f9396..fda45cae 100644 --- a/website/reference/builder/top-level/on.md +++ b/website/reference/builder/top-level/on.md @@ -6,7 +6,7 @@ outline: deep **Applies to:** -Applies the given builder attributes to all members that match a type pattern. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: +Applies the given builder attributes to all members that match a type pattern. This attribute must be of the form `on(type_pattern, attributes)`. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: ::: code-group @@ -81,17 +81,15 @@ Example::example() ::: -This attribute must be of form `on(type_pattern, attributes)`. - ## Type pattern `type_pattern` is a type that will be compared with the types of the members. The types are compared textually. For example, `String` doesn't match `std::string::String` because, internally, they are compared just like strings `"String" == "std::string::String"`. -You can use `_` to mark parts of the type to ignore when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. +You can use `_` to mark parts of the type that should be ignored when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. If you want to apply the attributes to all members, you can use the `_` type pattern that matches any type. For example, `#[builder(on(_, into))]`. -For optional members the underlying type is matched ignoring the `Option` wrapper. +For optional members, the underlying type is matched ignoring the `Option` wrapper. ## Attributes @@ -99,7 +97,7 @@ There are several attributes supported in the `attributes` position listed below - [`into`](../member/into); - [`transparent`](../member/transparent) - currently, this attribute can only be used with the `_` type pattern as the first `on(...)` clause; -- [`ovewritable`](../member/overwritable) - 🔬 **experimental**, this attribute is available under the cargo feature `"experimental-overwritable"` (see the issue [#149](https://github.com/elastio/bon/issues/149)); +- [`overwritable](../member/overwritable) - 🔬 **experimental**, this attribute is available under the cargo feature `"experimental-overwritable"` (see the issue [#149](https://github.com/elastio/bon/issues/149)); ## Examples @@ -198,4 +196,4 @@ Example::builder() ## Future releases -There is an issue [#152](https://github.com/elastio/bon/issues/152) about adding support for [`default`](../member/default.md), [`with`](../member/with) and other non-boolean attributes to the `on(...)` clause. We'll be glad if you take a look at the design proposed in that issue and put a 👍 if you like/want this feature or leave comment if you have some more feedback. +There is an issue [#152](https://github.com/elastio/bon/issues/152) about adding support for [`default`](../member/default.md), [`with`](../member/with) and other non-boolean attributes to the `on(...)` clause. We'll be glad if you take a look at the design proposed in that issue and put a 👍 if you like/want this feature or leave a comment if you have some more feedback. diff --git a/website/reference/builder/top-level/start-fn.md b/website/reference/builder/top-level/start-fn.md index b9066186..d980c635 100644 --- a/website/reference/builder/top-level/start-fn.md +++ b/website/reference/builder/top-level/start-fn.md @@ -88,7 +88,7 @@ See examples with `fn` syntax in the section below. ## Exposing original function -By default, when you place `#[builder]` on a function, that original function is modified by the macro to make it hidden such that only builder syntax remains available. Specifically, the macro does the following by default: +By default, when you place `#[builder]` on a function, that original function is modified by the macro to make it hidden, so that only builder syntax remains available. Specifically, the macro does the following by default: - Prepends the prefix `__orig_` to the original function's name. - Changes the visibility of the original function to private. From 40581cc62acdeddce45ddc872eb9aa412dc1be6a Mon Sep 17 00:00:00 2001 From: Veetaha Date: Wed, 30 Oct 2024 04:21:54 +0000 Subject: [PATCH 06/13] Document the `crate` attribute --- website/.vitepress/config.mts | 25 +++-- website/reference/bon.md | 2 +- website/reference/bon/crate.md | 3 - website/reference/builder.md | 64 ++++++------ .../reference/builder/member/overwritable.md | 2 + website/reference/builder/member/setters.md | 3 + .../reference/builder/member/transparent.md | 2 +- website/reference/builder/member/with.md | 3 + website/reference/builder/top-level/crate.md | 97 ++++++++++++++++++- website/reference/builder/top-level/on.md | 2 +- website/reference/builder/typestate-api.md | 1 + 11 files changed, 155 insertions(+), 49 deletions(-) delete mode 100644 website/reference/bon/crate.md create mode 100644 website/reference/builder/member/setters.md create mode 100644 website/reference/builder/member/with.md create mode 100644 website/reference/builder/typestate-api.md diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 6b828d97..fa94bd25 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -46,7 +46,7 @@ export default defineConfig({ markdown: { languageAlias: { - 'attr': 'js', + attr: "js", }, theme: { @@ -197,6 +197,7 @@ export default defineConfig({ { text: "Top-level", link: "/reference/builder#top-level-attributes", + collapsed: true, items: [ { text: "builder_type", @@ -225,12 +226,13 @@ export default defineConfig({ { text: "state_mod", link: "/reference/builder/top-level/state-mod", - } + }, ], }, { text: "Member", link: "/reference/builder#member-attributes", + collapsed: true, items: [ { text: "default", @@ -252,6 +254,10 @@ export default defineConfig({ text: "overwritable 🔬", link: "/reference/builder/member/overwritable", }, + { + text: "setters", + link: "/reference/builder/member/setters", + }, { text: "skip", link: "/reference/builder/member/skip", @@ -264,20 +270,21 @@ export default defineConfig({ text: "transparent", link: "/reference/builder/member/transparent", }, + { + text: "with", + link: "/reference/builder/member/with", + }, ], }, + { + text: "Typestate API", + link: "/reference/builder/typestate-api", + }, ], }, { text: "#[bon]", link: "/reference/bon", - collapsed: true, - items: [ - { - text: "crate", - link: "/reference/bon/crate", - } - ] }, { text: "Other items on docs.rs", diff --git a/website/reference/bon.md b/website/reference/bon.md index 1b24b699..08be8819 100644 --- a/website/reference/bon.md +++ b/website/reference/bon.md @@ -5,6 +5,6 @@ where you want to define methods with the [`builder`] macro. It provides the necessary context to the [`builder`] macros on top of the functions inside of the `impl` block. You'll get compile errors without that context. -For the examples of the usage of this macro and the reason why it's needed see this paragraph in [the overview](../guide/overview#builder-for-an-associated-method). +For the examples of the usage of this macro and the reason why it's needed, see this paragraph in [the overview](../guide/overview#builder-for-an-associated-method). [`builder`]: ./builder diff --git a/website/reference/bon/crate.md b/website/reference/bon/crate.md deleted file mode 100644 index 564e0c7b..00000000 --- a/website/reference/bon/crate.md +++ /dev/null @@ -1,3 +0,0 @@ -# `crate` - -TODO: add docs diff --git a/website/reference/builder.md b/website/reference/builder.md index cf8d2d8f..8ab98fb8 100644 --- a/website/reference/builder.md +++ b/website/reference/builder.md @@ -4,12 +4,39 @@ outline: [2, 3] # `#[derive(Builder)]` / `#[builder]` -You can generate a builder using three different kinds of syntax (struct, free function, associated method). They all share two common groups of attributes. +Generate builders from structs via `#[derive(Builder)]`, free functions via `#[builder]`, and associated methods via `#[bon]` + `#[builder]`. They all use the same attributes syntax. -- [Top-level attributes](#top-level-attributes) - placed on a `struct` or `fn` declaration. -- [Member attributes](#member-attributes) - placed on a `struct` field or `fn` argument. +## Top-level attributes -See examples. Make sure to click through the tabs: +These attributes are placed on top of a `struct` or `fn` declaration. + +| Attribute | Short description +| -- | -- | +| [`builder_type`](./builder/top-level/builder-type) | Overrides the name, visibility and docs of the builder struct +| [`crate`](./builder/top-level/crate) | Overrides the path to `bon` crate referenced in the generated code +| [`derive`](./builder/top-level/derive) | Generates additional derives for the builder struct itself +| [`finish_fn`](./builder/top-level/finish-fn) | Overrides the name, visibility and docs of the finishing function +| [`on`](./builder/top-level/on) | Applies the given builder attributes to all members matching a type pattern +| [`start_fn`](./builder/top-level/start-fn) | Overrides the name, visibility and docs of the starting function + +## Member attributes + +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 +| [`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 generated setters to accept `impl Into` +| [`name`](./builder/member/name) | Overrides the name of the member used in the builder's API +| [`overwritable` 🔬](./builder/member/overwritable) | ??????? TODO: ADD DOCS ?????? +| [`setters`](./builder/member/setters) | ??????? TODO: ADD DOCS ?????? +| [`skip`](./builder/member/skip) | Skips generating setters for the member +| [`start_fn`](./builder/member/start-fn) | Makes the member a positional argument on the starting function +| [`transparent`](./builder/member/transparent) | ??????? TODO: ADD DOCS ?????? +| [`with`](./builder/member/with) | ??????? TODO: ADD DOCS ?????? + +## Examples :::code-group @@ -48,32 +75,3 @@ impl Example { ) { } } ``` - ---- - -Most of the attributes apply both to `struct` and `fn` syntaxes, but there are exceptions. The **"Applies to"** clause at the top of the detailed docs clarifies this for every attribute. - -## Top-level attributes - -Click on the name of the attribute to view detailed docs. - -| Attribute | Short description -| -- | -- | -| [`builder_type`](./builder/top-level/builder-type) | Overrides the name, visibility and docs of the builder struct -| [`derive`](./builder/top-level/derive) | Generates additional derives on the builder struct itself -| [`finish_fn`](./builder/top-level/finish-fn) | Overrides the name, visibility and docs of the finishing function -| [`on`](./builder/top-level/on) | Applies the given builder attributes to all members matching a type pattern -| [`start_fn`](./builder/top-level/start-fn) | Overrides the name, visibility and docs of the starting function - -## Member attributes - -Click on the name of the attribute to view detailed docs. - -| Attribute | Short description -| -- | -- | -| [`default`](./builder/member/default) | Makes the member optional with a default value -| [`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 generated setters to accept `impl Into` -| [`name`](./builder/member/name) | Overrides the name of the member used in the builder's API -| [`skip`](./builder/member/skip) | Skips generating setters for the member -| [`start_fn`](./builder/member/start-fn) | Makes the member a positional argument on the starting function diff --git a/website/reference/builder/member/overwritable.md b/website/reference/builder/member/overwritable.md index 47c7b9e2..6e63bd95 100644 --- a/website/reference/builder/member/overwritable.md +++ b/website/reference/builder/member/overwritable.md @@ -1,3 +1,5 @@ # `overwritable` 🔬 This is an **experimental** attribute available under the cargo feature `"experimental-overwritable"`. + +TODO: add docs (update the short descriptions on the parent page) diff --git a/website/reference/builder/member/setters.md b/website/reference/builder/member/setters.md new file mode 100644 index 00000000..90be032a --- /dev/null +++ b/website/reference/builder/member/setters.md @@ -0,0 +1,3 @@ +# `setters` + +TODO: add docs (update the short descriptions on the parent page) diff --git a/website/reference/builder/member/transparent.md b/website/reference/builder/member/transparent.md index bd583b9c..5f30adcd 100644 --- a/website/reference/builder/member/transparent.md +++ b/website/reference/builder/member/transparent.md @@ -1,3 +1,3 @@ # `transparent` -TODO: add docs +TODO: add docs (update the short descriptions on the parent page) diff --git a/website/reference/builder/member/with.md b/website/reference/builder/member/with.md new file mode 100644 index 00000000..2f81d26d --- /dev/null +++ b/website/reference/builder/member/with.md @@ -0,0 +1,3 @@ +# `with` + +TODO: add docs (update the short descriptions on the parent page) diff --git a/website/reference/builder/top-level/crate.md b/website/reference/builder/top-level/crate.md index 564e0c7b..03934161 100644 --- a/website/reference/builder/top-level/crate.md +++ b/website/reference/builder/top-level/crate.md @@ -1,3 +1,98 @@ # `crate` -TODO: add docs +**Applies to:** (*) + +Overrides the path to `bon` crate referenced in the generated code, which is useful in cases when `bon` macros are wrapped by other macros. + +(*) `#[builder(crate)]` attribute isn't supported for associated methods. Instead, you should use the `#[bon(crate)]` attribute on top of the impl block. + + +## Examples + +Suppose this code lives in the crate `my_lib`: + +::: code-group + +```rust [Struct] +// Reexport `bon`, so it can be referenced from the macro-generated code +#[doc(hidden)] +pub use bon; + +#[macro_export] +macro_rules! gen_builder { + ($struct_item:item) => { + #[derive($crate::bon::Builder)] + #[builder(crate = $crate::bon)] + $struct_item + } +} +``` + +```rust [Free function] +// Reexport `bon`, so it can be referenced from the macro-generated code +#[doc(hidden)] +pub use bon; + +#[macro_export] +macro_rules! gen_builder { + ($fn_item:item) => { + #[$crate::bon::builder(crate = $crate::bon)] + $fn_item + } +} +``` + +```rust [Associated method] +// Reexport `bon`, so it can be referenced from the macro-generated code +#[doc(hidden)] +pub use bon; + +#[macro_export] +macro_rules! gen_builder { + ($impl_item:item) => { + #[$crate::bon::bon(crate = $crate::bon)] + $impl_item + } +} +``` + +::: + +When you use this macro wrapper in the other crate, it will work fine: + +::: code-group + +```rust ignore [Struct] +my_lib::gen_builder! { + struct Example {} +} +``` + +```rust ignore [Free function] +my_lib::gen_builder! { + fn example() {} +} +``` + +```rust ignore [Associated method] +struct Example; + +my_lib::gen_builder! { + impl Example { + #[builder] + fn example() {} + } +} +``` + +::: + +Without the `crate` attribute this code wouldn't compile because the generated code would try to access symbols from `::bon` instead of from the `my_crate::bon` reexport. + + +## Compile errors + +The macro disallows relative paths. Only th following is accepted: +- Absolute path with a leading colon like `::foo::bar` +- Path with `crate` prefix like `crate::foo::bar` +- Path with `$crate` prefix like `$crate::foo::bar` diff --git a/website/reference/builder/top-level/on.md b/website/reference/builder/top-level/on.md index fda45cae..ef665066 100644 --- a/website/reference/builder/top-level/on.md +++ b/website/reference/builder/top-level/on.md @@ -6,7 +6,7 @@ outline: deep **Applies to:** -Applies the given builder attributes to all members that match a type pattern. This attribute must be of the form `on(type_pattern, attributes)`. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: +Applies the given builder attributes to all members that match a type pattern. The syntax of this attribute is `on(type_pattern, attributes)`. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: ::: code-group diff --git a/website/reference/builder/typestate-api.md b/website/reference/builder/typestate-api.md new file mode 100644 index 00000000..dc923668 --- /dev/null +++ b/website/reference/builder/typestate-api.md @@ -0,0 +1 @@ +TODO: describe the generated typestate API module From f23c71bc0886208213e1dc22376b3553a4e3a171 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Wed, 30 Oct 2024 19:21:33 +0000 Subject: [PATCH 07/13] Add more tests for compatibility with positional args --- .../integration/builder/attr_transparent.rs | 48 +++++++++++++++---- .../integration/ui/compile_fail/attr_with.rs | 18 +++++++ .../ui/compile_fail/attr_with.stderr | 18 +++++++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/bon/tests/integration/builder/attr_transparent.rs b/bon/tests/integration/builder/attr_transparent.rs index aae84f53..6e84dcbf 100644 --- a/bon/tests/integration/builder/attr_transparent.rs +++ b/bon/tests/integration/builder/attr_transparent.rs @@ -138,6 +138,9 @@ mod attr_on { #[builder(on(_, transparent))] #[allow(dead_code)] struct Sut { + #[builder(start_fn)] + start_fn: u32, + regular: Option, generic: Option, @@ -152,7 +155,7 @@ mod attr_on { } assert_debug_eq( - Sut::builder() + Sut::builder(11) .regular(Some(1)) .generic(Some(false)) .with_into(2) @@ -160,6 +163,7 @@ mod attr_on { .build(), expect![[r#" Sut { + start_fn: 11, regular: Some( 1, ), @@ -183,23 +187,31 @@ mod attr_on { fn test_free_fn() { #[builder(on(_, transparent))] fn sut( + #[builder(start_fn)] start_fn: u32, regular: Option, generic: Option, #[builder(into)] with_into: Option, #[builder(default = Some(99))] with_default: Option, #[builder(default = Some(10))] with_default_2: Option, ) -> impl fmt::Debug { - (regular, generic, with_into, with_default, with_default_2) + ( + start_fn, + regular, + generic, + with_into, + with_default, + with_default_2, + ) } assert_debug_eq( - sut() + sut(11) .regular(Some(1)) .generic(Some(false)) .with_into(2) .maybe_with_default_2(Some(Some(3))) .call(), - expect!["(Some(1), Some(false), Some(2), Some(99), Some(3))"], + expect!["(11, Some(1), Some(false), Some(2), Some(99), Some(3))"], ); } @@ -211,18 +223,27 @@ mod attr_on { impl Sut { #[builder(on(_, transparent))] fn sut( + #[builder(start_fn)] start_fn: u32, regular: Option, generic: Option, #[builder(into)] with_into: Option, #[builder(default = Some(99))] with_default: Option, #[builder(default = Some(10))] with_default_2: Option, ) -> impl fmt::Debug { - (regular, generic, with_into, with_default, with_default_2) + ( + start_fn, + regular, + generic, + with_into, + with_default, + with_default_2, + ) } #[builder(on(_, transparent))] fn with_self( &self, + #[builder(start_fn)] start_fn: u32, regular: Option, generic: Option, #[builder(into)] with_into: Option, @@ -230,28 +251,35 @@ mod attr_on { #[builder(default = Some(10))] with_default_2: Option, ) -> impl fmt::Debug { let _ = self; - (regular, generic, with_into, with_default, with_default_2) + ( + start_fn, + regular, + generic, + with_into, + with_default, + with_default_2, + ) } } assert_debug_eq( - Sut::sut() + Sut::sut(11) .regular(Some(1)) .generic(Some(false)) .with_into(2) .maybe_with_default_2(Some(Some(3))) .call(), - expect!["(Some(1), Some(false), Some(2), Some(99), Some(3))"], + expect!["(11, Some(1), Some(false), Some(2), Some(99), Some(3))"], ); assert_debug_eq( - Sut.with_self() + Sut.with_self(11) .regular(Some(1)) .generic(Some(false)) .with_into(2) .maybe_with_default_2(Some(Some(3))) .call(), - expect!["(Some(1), Some(false), Some(2), Some(99), Some(3))"], + expect!["(11, Some(1), Some(false), Some(2), Some(99), Some(3))"], ); } } diff --git a/bon/tests/integration/ui/compile_fail/attr_with.rs b/bon/tests/integration/ui/compile_fail/attr_with.rs index 07fa7409..d0bb4026 100644 --- a/bon/tests/integration/ui/compile_fail/attr_with.rs +++ b/bon/tests/integration/ui/compile_fail/attr_with.rs @@ -104,4 +104,22 @@ struct NonCollectionWithFromIter2 { value: Option, } +#[derive(Builder)] +struct IncompatibleWithStartFn { + #[builder(with = |x: u32| x + 1, start_fn)] + value: u32, +} + +#[derive(Builder)] +struct IncompatibleWithFinishFn { + #[builder(with = |x: u32| x + 1, finish_fn)] + value: u32, +} + +#[derive(Builder)] +struct IncompatibleWithInto { + #[builder(with = |x: u32| x + 1, into)] + value: u32, +} + fn main() {} diff --git a/bon/tests/integration/ui/compile_fail/attr_with.stderr b/bon/tests/integration/ui/compile_fail/attr_with.stderr index 41ce2a12..af42735b 100644 --- a/bon/tests/integration/ui/compile_fail/attr_with.stderr +++ b/bon/tests/integration/ui/compile_fail/attr_with.stderr @@ -183,6 +183,24 @@ error: the underlying type of this member is not a known collection type; only a 104 | value: Option, | ^^^ +error: `start_fn` attribute can't be specified together with `with` + --> tests/integration/ui/compile_fail/attr_with.rs:109:38 + | +109 | #[builder(with = |x: u32| x + 1, start_fn)] + | ^^^^^^^^ + +error: `finish_fn` attribute can't be specified together with `with` + --> tests/integration/ui/compile_fail/attr_with.rs:115:38 + | +115 | #[builder(with = |x: u32| x + 1, finish_fn)] + | ^^^^^^^^^ + +error: `with` attribute can't be specified together with `into` + --> tests/integration/ui/compile_fail/attr_with.rs:121:15 + | +121 | #[builder(with = |x: u32| x + 1, into)] + | ^^^^ + error[E0308]: mismatched types --> tests/integration/ui/compile_fail/attr_with.rs:54:12 | From d1510f482f805ba4f841763ea157a4c591f1d769 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Wed, 30 Oct 2024 22:20:05 +0000 Subject: [PATCH 08/13] Move stuff --- README.md | 2 +- website/.vitepress/config.mts | 63 ++++++++------ .../reference/builder/member/overwritable.md | 5 -- website/{ => src}/blog.md | 0 .../blog/bon-builder-generator-v2-release.md | 0 .../blog/bon-builder-v2-1-release.md | 0 .../blog/bon-builder-v2-2-release.md | 0 .../blog/bon-builder-v2-3-release.md | 0 ...-to-do-named-function-arguments-in-rust.md | 0 ...e-weird-of-function-local-types-in-rust.md | 0 website/{ => src}/changelog.md | 2 +- website/src/guide/builder-extensions.md | 1 + website/{ => src}/guide/documenting.md | 0 website/{ => src}/guide/inspecting.md | 0 .../{ => src}/guide/internal/contributing.md | 0 .../{guide => src/guide/misc}/alternatives.md | 0 .../{guide => src/guide/misc}/benchmarks.md | 0 .../guide/misc}/compatibility.md | 0 .../{guide => src/guide/misc}/limitations.md | 0 .../guide/misc}/troubleshooting.md | 0 website/{ => src}/guide/optional-members.md | 74 ++++++++++------- website/{ => src}/guide/overview.md | 18 ++-- .../guide/patterns/conditional-building.md | 0 .../guide/patterns/fallible-builders.md | 0 .../patterns/into-conversions-in-depth.md | 0 .../guide/patterns/shared-configuration.md | 0 website/{ => src}/guide/positional-members.md | 8 +- website/{ => src}/index.md | 0 website/{ => src}/public/CNAME | 0 .../public/bon-2-1-compile-times.png | Bin website/{ => src}/public/bon-home.png | Bin website/{ => src}/public/bon-logo-medium.png | Bin website/{ => src}/public/bon-logo-orig.png | Bin website/{ => src}/public/bon-logo-thumb.png | Bin website/{ => src}/public/completions-demo.mp4 | Bin .../public/expand-macro-recursively.mp4 | Bin website/{ => src}/reference/bon.md | 0 website/{ => src}/reference/builder.md | 2 +- .../reference/builder/member/default.md | 0 .../reference/builder/member/finish-fn.md | 2 +- .../reference/builder/member/into.md | 8 +- .../reference/builder/member/name.md | 0 .../reference/builder/member/overwritable.md | 78 ++++++++++++++++++ .../reference/builder/member/setters.md | 4 + .../reference/builder/member/skip.md | 0 .../reference/builder/member/start-fn.md | 2 +- .../reference/builder/member/transparent.md | 0 .../reference/builder/member/with.md | 0 .../builder/top-level/builder-type.md | 0 .../reference/builder/top-level/crate.md | 0 .../reference/builder/top-level/derive.md | 0 .../reference/builder/top-level/finish-fn.md | 0 .../reference/builder/top-level/on.md | 0 .../reference/builder/top-level/start-fn.md | 0 .../reference/builder/top-level/state-mod.md | 0 .../reference/builder/typestate-api.md | 0 website/{ => src}/v1/config.mts | 0 website/{ => src}/v1/guide/alternatives.md | 0 website/{ => src}/v1/guide/benchmarks.md | 0 website/{ => src}/v1/guide/compatibility.md | 0 website/{ => src}/v1/guide/documenting.md | 0 .../{ => src}/v1/guide/into-conversions.md | 0 website/{ => src}/v1/guide/limitations.md | 0 .../{ => src}/v1/guide/optional-members.md | 0 website/{ => src}/v1/guide/overview.md | 0 website/{ => src}/v1/guide/troubleshooting.md | 0 website/{ => src}/v1/reference/bon.md | 0 website/{ => src}/v1/reference/builder.md | 0 website/{ => src}/v2/config.mts | 0 website/{ => src}/v2/guide/alternatives.md | 0 website/{ => src}/v2/guide/benchmarks.md | 0 website/{ => src}/v2/guide/compatibility.md | 0 website/{ => src}/v2/guide/documenting.md | 0 website/{ => src}/v2/guide/inspecting.md | 0 .../v2/guide/internal/contributing.md | 0 website/{ => src}/v2/guide/limitations.md | 0 .../{ => src}/v2/guide/optional-members.md | 0 website/{ => src}/v2/guide/overview.md | 0 .../v2/guide/patterns/conditional-building.md | 0 .../v2/guide/patterns/fallible-builders.md | 0 .../patterns/into-conversions-in-depth.md | 0 .../v2/guide/patterns/shared-configuration.md | 0 .../{ => src}/v2/guide/positional-members.md | 0 website/{ => src}/v2/guide/troubleshooting.md | 0 website/{ => src}/v2/reference/bon.md | 0 website/{ => src}/v2/reference/builder.md | 0 86 files changed, 192 insertions(+), 77 deletions(-) delete mode 100644 website/reference/builder/member/overwritable.md rename website/{ => src}/blog.md (100%) rename website/{ => src}/blog/bon-builder-generator-v2-release.md (100%) rename website/{ => src}/blog/bon-builder-v2-1-release.md (100%) rename website/{ => src}/blog/bon-builder-v2-2-release.md (100%) rename website/{ => src}/blog/bon-builder-v2-3-release.md (100%) rename website/{ => src}/blog/how-to-do-named-function-arguments-in-rust.md (100%) rename website/{ => src}/blog/the-weird-of-function-local-types-in-rust.md (100%) rename website/{ => src}/changelog.md (98%) create mode 100644 website/src/guide/builder-extensions.md rename website/{ => src}/guide/documenting.md (100%) rename website/{ => src}/guide/inspecting.md (100%) rename website/{ => src}/guide/internal/contributing.md (100%) rename website/{guide => src/guide/misc}/alternatives.md (100%) rename website/{guide => src/guide/misc}/benchmarks.md (100%) rename website/{guide => src/guide/misc}/compatibility.md (100%) rename website/{guide => src/guide/misc}/limitations.md (100%) rename website/{guide => src/guide/misc}/troubleshooting.md (100%) rename website/{ => src}/guide/optional-members.md (53%) rename website/{ => src}/guide/overview.md (92%) rename website/{ => src}/guide/patterns/conditional-building.md (100%) rename website/{ => src}/guide/patterns/fallible-builders.md (100%) rename website/{ => src}/guide/patterns/into-conversions-in-depth.md (100%) rename website/{ => src}/guide/patterns/shared-configuration.md (100%) rename website/{ => src}/guide/positional-members.md (94%) rename website/{ => src}/index.md (100%) rename website/{ => src}/public/CNAME (100%) rename website/{ => src}/public/bon-2-1-compile-times.png (100%) rename website/{ => src}/public/bon-home.png (100%) rename website/{ => src}/public/bon-logo-medium.png (100%) rename website/{ => src}/public/bon-logo-orig.png (100%) rename website/{ => src}/public/bon-logo-thumb.png (100%) rename website/{ => src}/public/completions-demo.mp4 (100%) rename website/{ => src}/public/expand-macro-recursively.mp4 (100%) rename website/{ => src}/reference/bon.md (100%) rename website/{ => src}/reference/builder.md (96%) rename website/{ => src}/reference/builder/member/default.md (100%) rename website/{ => src}/reference/builder/member/finish-fn.md (95%) rename website/{ => src}/reference/builder/member/into.md (97%) rename website/{ => src}/reference/builder/member/name.md (100%) create mode 100644 website/src/reference/builder/member/overwritable.md rename website/{ => src}/reference/builder/member/setters.md (72%) rename website/{ => src}/reference/builder/member/skip.md (100%) rename website/{ => src}/reference/builder/member/start-fn.md (94%) rename website/{ => src}/reference/builder/member/transparent.md (100%) rename website/{ => src}/reference/builder/member/with.md (100%) rename website/{ => src}/reference/builder/top-level/builder-type.md (100%) rename website/{ => src}/reference/builder/top-level/crate.md (100%) rename website/{ => src}/reference/builder/top-level/derive.md (100%) rename website/{ => src}/reference/builder/top-level/finish-fn.md (100%) rename website/{ => src}/reference/builder/top-level/on.md (100%) rename website/{ => src}/reference/builder/top-level/start-fn.md (100%) rename website/{ => src}/reference/builder/top-level/state-mod.md (100%) rename website/{ => src}/reference/builder/typestate-api.md (100%) rename website/{ => src}/v1/config.mts (100%) rename website/{ => src}/v1/guide/alternatives.md (100%) rename website/{ => src}/v1/guide/benchmarks.md (100%) rename website/{ => src}/v1/guide/compatibility.md (100%) rename website/{ => src}/v1/guide/documenting.md (100%) rename website/{ => src}/v1/guide/into-conversions.md (100%) rename website/{ => src}/v1/guide/limitations.md (100%) rename website/{ => src}/v1/guide/optional-members.md (100%) rename website/{ => src}/v1/guide/overview.md (100%) rename website/{ => src}/v1/guide/troubleshooting.md (100%) rename website/{ => src}/v1/reference/bon.md (100%) rename website/{ => src}/v1/reference/builder.md (100%) rename website/{ => src}/v2/config.mts (100%) rename website/{ => src}/v2/guide/alternatives.md (100%) rename website/{ => src}/v2/guide/benchmarks.md (100%) rename website/{ => src}/v2/guide/compatibility.md (100%) rename website/{ => src}/v2/guide/documenting.md (100%) rename website/{ => src}/v2/guide/inspecting.md (100%) rename website/{ => src}/v2/guide/internal/contributing.md (100%) rename website/{ => src}/v2/guide/limitations.md (100%) rename website/{ => src}/v2/guide/optional-members.md (100%) rename website/{ => src}/v2/guide/overview.md (100%) rename website/{ => src}/v2/guide/patterns/conditional-building.md (100%) rename website/{ => src}/v2/guide/patterns/fallible-builders.md (100%) rename website/{ => src}/v2/guide/patterns/into-conversions-in-depth.md (100%) rename website/{ => src}/v2/guide/patterns/shared-configuration.md (100%) rename website/{ => src}/v2/guide/positional-members.md (100%) rename website/{ => src}/v2/guide/troubleshooting.md (100%) rename website/{ => src}/v2/reference/bon.md (100%) rename website/{ => src}/v2/reference/builder.md (100%) diff --git a/README.md b/README.md index 799b6108..3e3903bd 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ assert_eq!(user.level, Some(24)); assert!(user.is_admin); ``` -See [the guide](https://bon-rs.com/guide/overview) for the rest. +See [the guide](https://bon-rs.com/guide/overview) for more. --- diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index fa94bd25..ef077702 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -1,7 +1,7 @@ import { defineConfig } from "vitepress"; import { abbr } from "@mdit/plugin-abbr"; -import * as v1 from "../v1/config.mjs"; -import * as v2 from "../v2/config.mjs"; +import * as v1 from "../src/v1/config.mjs"; +import * as v2 from "../src/v2/config.mjs"; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -75,6 +75,12 @@ export default defineConfig({ ], ], + srcDir: "src", + + rewrites: { + "guide/:subdir/:page": "guide/:page", + }, + // https://vitepress.dev/reference/default-theme-config themeConfig: { logo: "/bon-logo-thumb.png", @@ -124,10 +130,6 @@ export default defineConfig({ text: "Optional Members", link: "/guide/optional-members", }, - { - text: "Compatibility", - link: "/guide/compatibility", - }, { text: "Positional Members", link: "/guide/positional-members", @@ -141,41 +143,54 @@ export default defineConfig({ link: "/guide/documenting", }, { - text: "Limitations", - link: "/guide/limitations", + text: "Builder extensions", + link: "/guide/builder-extensions", + } + ] + }, + { + text: "Patterns", + items: [ + { + text: "Conditional Building", + link: "/guide/conditional-building", }, { - text: "Benchmarks", - link: "/guide/benchmarks", + text: "Fallible Builders", + link: "/guide/fallible-builders", }, { - text: "Alternatives", - link: "/guide/alternatives", + text: "Into Conversions In-Depth", + link: "/guide/into-conversions-in-depth", }, { - text: "Troubleshooting", - link: "/guide/troubleshooting", + text: "Shared Configuration", + link: "/guide/shared-configuration", }, ], }, { - text: "Patterns", + text: "Misc", items: [ { - text: "Conditional Building", - link: "/guide/patterns/conditional-building", + text: "Compatibility", + link: "/guide/compatibility", }, { - text: "Fallible Builders", - link: "/guide/patterns/fallible-builders", + text: "Limitations", + link: "/guide/limitations", }, { - text: "Into Conversions In-Depth", - link: "/guide/patterns/into-conversions-in-depth", + text: "Benchmarks", + link: "/guide/benchmarks", }, { - text: "Shared Configuration", - link: "/guide/patterns/shared-configuration", + text: "Alternatives", + link: "/guide/alternatives", + }, + { + text: "Troubleshooting", + link: "/guide/troubleshooting", }, ], }, @@ -184,7 +199,7 @@ export default defineConfig({ items: [ { text: "Contributing", - link: "/guide/internal/contributing", + link: "/guide/contributing", }, ], }, diff --git a/website/reference/builder/member/overwritable.md b/website/reference/builder/member/overwritable.md deleted file mode 100644 index 6e63bd95..00000000 --- a/website/reference/builder/member/overwritable.md +++ /dev/null @@ -1,5 +0,0 @@ -# `overwritable` 🔬 - -This is an **experimental** attribute available under the cargo feature `"experimental-overwritable"`. - -TODO: add docs (update the short descriptions on the parent page) diff --git a/website/blog.md b/website/src/blog.md similarity index 100% rename from website/blog.md rename to website/src/blog.md diff --git a/website/blog/bon-builder-generator-v2-release.md b/website/src/blog/bon-builder-generator-v2-release.md similarity index 100% rename from website/blog/bon-builder-generator-v2-release.md rename to website/src/blog/bon-builder-generator-v2-release.md diff --git a/website/blog/bon-builder-v2-1-release.md b/website/src/blog/bon-builder-v2-1-release.md similarity index 100% rename from website/blog/bon-builder-v2-1-release.md rename to website/src/blog/bon-builder-v2-1-release.md diff --git a/website/blog/bon-builder-v2-2-release.md b/website/src/blog/bon-builder-v2-2-release.md similarity index 100% rename from website/blog/bon-builder-v2-2-release.md rename to website/src/blog/bon-builder-v2-2-release.md diff --git a/website/blog/bon-builder-v2-3-release.md b/website/src/blog/bon-builder-v2-3-release.md similarity index 100% rename from website/blog/bon-builder-v2-3-release.md rename to website/src/blog/bon-builder-v2-3-release.md diff --git a/website/blog/how-to-do-named-function-arguments-in-rust.md b/website/src/blog/how-to-do-named-function-arguments-in-rust.md similarity index 100% rename from website/blog/how-to-do-named-function-arguments-in-rust.md rename to website/src/blog/how-to-do-named-function-arguments-in-rust.md diff --git a/website/blog/the-weird-of-function-local-types-in-rust.md b/website/src/blog/the-weird-of-function-local-types-in-rust.md similarity index 100% rename from website/blog/the-weird-of-function-local-types-in-rust.md rename to website/src/blog/the-weird-of-function-local-types-in-rust.md diff --git a/website/changelog.md b/website/src/changelog.md similarity index 98% rename from website/changelog.md rename to website/src/changelog.md index 00b98090..adaf6a0b 100644 --- a/website/changelog.md +++ b/website/src/changelog.md @@ -63,7 +63,7 @@ All the breaking changes are very unlikely to actually break your code that was - Add `#[builder(state_mod)]` to configure the builder's type state API module name, visibility and docs ([#145](https://github.com/elastio/bon/pull/145)) -- 🔬 **Experimental.** Add `#[builder(overwritable)]` and `#[builder(on(..., overwritable)]` to make it possible to call setters multiple times for the same member. This attribute is available under the cargo feature `"experimental-overwritable"` (stabilization is tracked in [#149](https://github.com/elastio/bon/issues/149), let us know if you need this attribute!). ([#145](https://github.com/elastio/bon/pull/145)) +- 🔬 **Experimental.** Add `#[builder(overwritable)]` and `#[builder(on(..., overwritable)]` to make it possible to call setters multiple times for the same member. This attribute is available under the cargo feature `"experimental-overwritable"`. The fate of this feature depends on your feedback in the tracking issue [#149](https://github.com/elastio/bon/issues/149). Please, let us know if you have a use case for this attribute! ([#145](https://github.com/elastio/bon/pull/145)) - Add `#[builder(setters)]` to fine-tune the setters names, visibility and docs ([#145](https://github.com/elastio/bon/pull/145)) diff --git a/website/src/guide/builder-extensions.md b/website/src/guide/builder-extensions.md new file mode 100644 index 00000000..cb735d44 --- /dev/null +++ b/website/src/guide/builder-extensions.md @@ -0,0 +1 @@ +# Builder Extension diff --git a/website/guide/documenting.md b/website/src/guide/documenting.md similarity index 100% rename from website/guide/documenting.md rename to website/src/guide/documenting.md diff --git a/website/guide/inspecting.md b/website/src/guide/inspecting.md similarity index 100% rename from website/guide/inspecting.md rename to website/src/guide/inspecting.md diff --git a/website/guide/internal/contributing.md b/website/src/guide/internal/contributing.md similarity index 100% rename from website/guide/internal/contributing.md rename to website/src/guide/internal/contributing.md diff --git a/website/guide/alternatives.md b/website/src/guide/misc/alternatives.md similarity index 100% rename from website/guide/alternatives.md rename to website/src/guide/misc/alternatives.md diff --git a/website/guide/benchmarks.md b/website/src/guide/misc/benchmarks.md similarity index 100% rename from website/guide/benchmarks.md rename to website/src/guide/misc/benchmarks.md diff --git a/website/guide/compatibility.md b/website/src/guide/misc/compatibility.md similarity index 100% rename from website/guide/compatibility.md rename to website/src/guide/misc/compatibility.md diff --git a/website/guide/limitations.md b/website/src/guide/misc/limitations.md similarity index 100% rename from website/guide/limitations.md rename to website/src/guide/misc/limitations.md diff --git a/website/guide/troubleshooting.md b/website/src/guide/misc/troubleshooting.md similarity index 100% rename from website/guide/troubleshooting.md rename to website/src/guide/misc/troubleshooting.md diff --git a/website/guide/optional-members.md b/website/src/guide/optional-members.md similarity index 53% rename from website/guide/optional-members.md rename to website/src/guide/optional-members.md index 3693c6e3..c2921363 100644 --- a/website/guide/optional-members.md +++ b/website/src/guide/optional-members.md @@ -1,48 +1,62 @@ +--- +outline: deep +--- + # Optional Members ## `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. -**Example:** - ```rust -use bon::builder; - -#[builder] +#[bon::builder] fn example(level: Option) {} // We can call it without specifying the `level` example().call(); ``` -The generated builder has two setters for each optional member. One setter accepts `T` and the other accepts `Option`. The following setters will be generated in the example above (simplified): +You can use [`#[builder(transparent)]`](../reference/builder/member/transparent) to opt-out from this. + +### Setters pair + +The builder provides a **pair** of setters for each optional member: + +| Name | Input | Description | Configuration attribute +|------------------|-------------|-------------------------------|------------------ +| `{member}` | `T` | Accepts a non-`None` value. | [`some_fn`] +| `maybe_{member}` | `Option` | Accepts an `Option` directly. | [`option_fn`] + +[`some_fn`]: ../reference/builder/member/setters#some-fn +[`option_fn`]: ../reference/builder/member/setters#option-fn + + +::: details See how the setters look like in the generated code ```rust ignore -impl ExampleBuilder { - // Accepts the underlying value. Wraps it in `Some()` internally - fn level(value: u32) -> NextBuilderState { /* */ } +// [GENERATED CODE (simplified)] +impl ExampleBuilder { + fn level(self, value: u32) -> ExampleBuilder> { + self.maybe_level(Some(value)) // Yes, it's this simple! + } - // Accepts the `Option` directly. - fn maybe_level(value: Option) -> NextBuilderState { /* */ } + fn maybe_level(self, value: Option) -> ExampleBuilder> { /* */ } } ``` -::: tip +::: Thanks to this design, changing the member from required to optional [preserves compatibility](./compatibility#making-a-required-member-optional). -::: - ---- +### Examples -If you need to pass a simple literal value, then the syntax is very short +Pass a non-`None` value via the `{member}(T)` setter: ```rust ignore example().level(42).call(); ``` -If you already have an `Option` variable somewhere or you need to dynamically decide if the value should be `Some` or `None`, then you can use the `maybe_` variant of the setter. +Pass an `Option` value directly via the `maybe_{member}(Option)` setter: ```rust ignore let value = if some_condition { @@ -54,6 +68,8 @@ let value = if some_condition { 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. @@ -64,12 +80,10 @@ Switching between `#[builder(default)]` and `Option` is [compatible](./compat ::: -**Example:** +### Examples ```rust -use bon::builder; - -#[builder] +#[bon::builder] fn example( // This uses the `Default` trait // [!code highlight] #[builder(default)] // [!code highlight] @@ -84,23 +98,21 @@ fn example( // Here, the default values will be used `a = 0` and `b = 4` // [!code highlight] let result = example().call(); + assert_eq!(result, 4); +``` -// The same couple of setters `{member}(T)` and `maybe_{member}(Option)` // [!code highlight] -// are generated just like it works with members of `Option` type // [!code highlight] -let result = example() - .a(3) - .b(5) - .call(); -assert_eq!(result, 8); +The same [pair of optional setters](#setters-pair) is generated for members with default values. +```rust ignore let result = example() - .maybe_a(Some(3)) + // Pass a non-None value + .a(3) + // Pass an `Option` value directly .maybe_b(Some(5)) .call(); -assert_eq!(result, 8); ``` ## Conditional building -Now that you know how optional members work you can check out the ["Conditional building" patterns](./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" design patterns](./patterns/conditional-building) or continue studying other features of `bon` by following the "Next page" link at the bottom. diff --git a/website/guide/overview.md b/website/src/guide/overview.md similarity index 92% rename from website/guide/overview.md rename to website/src/guide/overview.md index affa9157..6337f653 100644 --- a/website/guide/overview.md +++ b/website/src/guide/overview.md @@ -1,5 +1,5 @@