diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03ecb476..96e47c41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,6 +140,7 @@ jobs: cargo +${{ matrix.toolchain }} clippy --all-features --all-targets --locked \ -- \ --allow edition-2024-expr-fragment-specifier \ + --allow if_let_rescope \ --allow impl-trait-overcaptures cargo-miri: @@ -149,7 +150,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: nightly-2024-10-14 + toolchain: nightly-2024-10-30 components: miri - run: | @@ -159,6 +160,7 @@ jobs: RUSTFLAGS: >- --deny warnings --allow edition-2024-expr-fragment-specifier + --allow if_let_rescope --allow impl-trait-overcaptures cargo-doc: 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/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-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/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/bon/tests/integration/builder/attr_default.rs b/bon/tests/integration/builder/attr_default.rs index c3db2621..e9efee5c 100644 --- a/bon/tests/integration/builder/attr_default.rs +++ b/bon/tests/integration/builder/attr_default.rs @@ -65,6 +65,7 @@ fn struct_alloc() { ); #[bon] + #[allow(clippy::items_after_statements)] impl Sut { #[builder] fn assoc( @@ -174,6 +175,7 @@ fn struct_no_std() { ); #[bon] + #[allow(clippy::items_after_statements)] impl Sut { #[builder] fn assoc(self, #[builder(default)] arg1: u32, #[builder(default = 43)] arg2: u32) -> Self { 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/builder/init_order.rs b/bon/tests/integration/builder/init_order.rs index 2dd26a6e..fe142cb0 100644 --- a/bon/tests/integration/builder/init_order.rs +++ b/bon/tests/integration/builder/init_order.rs @@ -48,6 +48,7 @@ fn struct_init_order() { ); #[bon] + #[allow(clippy::items_after_statements)] impl Sut { #[builder] fn sut( diff --git a/bon/tests/integration/builder/raw_idents.rs b/bon/tests/integration/builder/raw_idents.rs index 4cc96c51..01f530e6 100644 --- a/bon/tests/integration/builder/raw_idents.rs +++ b/bon/tests/integration/builder/raw_idents.rs @@ -31,15 +31,19 @@ fn struct_case() { #[test] #[allow(non_camel_case_types)] fn fn_case() { - #[builder] - fn r#type(r#type: u32, #[builder(name = r#while)] other: u32) { - let _ = (r#type, other); - } + { + #[builder] + fn r#type(r#type: u32, #[builder(name = r#while)] other: u32) { + let _ = (r#type, other); + } - r#type().r#type(42).r#while(100).call(); + r#type().r#type(42).r#while(100).call(); + } - #[builder(builder_type = r#type, state_mod = r#mod)] - fn sut() {} + { + #[builder(builder_type = r#type, state_mod = r#mod)] + fn sut() {} - let _: r#type = sut(); + let _: r#type = sut(); + } } 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 | diff --git a/e2e-tests/build.rs b/e2e-tests/build.rs index 5ab5ef69..8f3ef86b 100644 --- a/e2e-tests/build.rs +++ b/e2e-tests/build.rs @@ -22,7 +22,8 @@ fn main() { .filter(|entry| { entry.file_type().is_file() && entry.path().extension() == Some("md".as_ref()) - && !entry.path().starts_with("../website/v1") + && !entry.path().starts_with("../website/src/v1") + && !entry.path().starts_with("../website/src/v2") }) .map(DirEntry::into_path) .sorted_unstable() diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index ac2fdd0f..c6972e62 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({ @@ -46,7 +46,7 @@ export default defineConfig({ markdown: { languageAlias: { - 'attr': 'js', + attr: "js", }, theme: { @@ -75,6 +75,13 @@ export default defineConfig({ ], ], + srcDir: "src", + + rewrites: { + "guide/:subdir/:page": "guide/:page", + }, + + // https://vitepress.dev/reference/default-theme-config themeConfig: { logo: "/bon-logo-thumb.png", @@ -95,7 +102,6 @@ export default defineConfig({ provider: "local", }, - // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Guide", link: "/guide/overview" }, { text: "Reference", link: "/reference/builder" }, @@ -125,8 +131,8 @@ export default defineConfig({ link: "/guide/optional-members", }, { - text: "Compatibility", - link: "/guide/compatibility", + text: "Customizing Setters", + link: "/guide/customizing-setters", }, { text: "Positional Members", @@ -141,41 +147,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 +203,7 @@ export default defineConfig({ items: [ { text: "Contributing", - link: "/guide/internal/contributing", + link: "/guide/contributing", }, ], }, @@ -195,13 +214,18 @@ export default defineConfig({ link: "/reference/builder", items: [ { - text: "Top-level", + text: "Top-Level", link: "/reference/builder#top-level-attributes", + collapsed: true, items: [ { 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", @@ -218,11 +242,16 @@ export default defineConfig({ text: "start_fn", link: "/reference/builder/top-level/start-fn", }, + { + text: "state_mod", + link: "/reference/builder/top-level/state-mod", + }, ], }, { text: "Member", link: "/reference/builder#member-attributes", + collapsed: true, items: [ { text: "default", @@ -240,6 +269,14 @@ export default defineConfig({ text: "name", link: "/reference/builder/member/name", }, + { + text: "overwritable 🔬", + link: "/reference/builder/member/overwritable", + }, + { + text: "setters", + link: "/reference/builder/member/setters", + }, { text: "skip", link: "/reference/builder/member/skip", @@ -248,8 +285,20 @@ export default defineConfig({ text: "start_fn", link: "/reference/builder/member/start-fn", }, + { + text: "transparent", + link: "/reference/builder/member/transparent", + }, + { + text: "with", + link: "/reference/builder/member/with", + }, ], }, + { + text: "Typestate API", + link: "/reference/builder/typestate-api", + }, ], }, { 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" diff --git a/website/reference/builder.md b/website/reference/builder.md deleted file mode 100644 index 2974d47a..00000000 --- a/website/reference/builder.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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. - -- [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. - -See examples. Make sure to click through the tabs: - -:::code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(finish_fn = finish)] // <-- this is a top-level attribute // [!code highlight] -struct Example { - #[builder(default)] // <-- this is a member attribute // [!code highlight] - field: u32 -} -``` - -```rust [Free function] -use bon::builder; - -#[builder(finish_fn = finish)] // <-- this is a top-level attribute // [!code highlight] -fn example( - #[builder(default)] // <-- this is a member attribute // [!code highlight] - arg: u32 -) { } -``` - -```rust [Associated method] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder(finish_fn = finish)] // <-- this is a top-level attribute // [!code highlight] - fn example( - #[builder(default)] // <-- this is a member attribute // [!code highlight] - arg: u32 - ) { } -} -``` - ---- - -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 generated builder -| [`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/top-level/finish-fn.md b/website/reference/builder/top-level/finish-fn.md deleted file mode 100644 index 2fc27f78..00000000 --- a/website/reference/builder/top-level/finish-fn.md +++ /dev/null @@ -1,63 +0,0 @@ -# `finish_fn` - -**Applies to:** - -This attribute allows overriding the name of the generated builder's method that finishes the building process. - -**Example:** - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(finish_fn = assemble)] // [!code highlight] -struct Article { - id: u32 -} - -let article = Article::builder() - .id(42) - .assemble(); // [!code highlight] - -assert_eq!(article.id, 42); -``` - -```rust [Free function] -use bon::builder; - -#[builder(finish_fn = send)] // [!code highlight] -fn get_article(id: u32) -> String { - format!("Some article with id {id}") -} - -let response = get_article() - .id(42) - .send(); // [!code highlight] - -assert_eq!(response, "Some article with id 42"); -``` - -```rust [Associated method] -use bon::bon; - -struct ArticlesClient; - -#[bon] -impl ArticlesClient { - #[builder(finish_fn = send)] // [!code highlight] - fn get_article(&self, id: u32) -> String { - format!("Some article with id {id}") - } -} - -let response = ArticlesClient - .get_article() - .id(42) - .send(); // [!code highlight] - -assert_eq!(response, "Some article with id 42"); -``` - -::: diff --git a/website/reference/builder/top-level/on.md b/website/reference/builder/top-level/on.md deleted file mode 100644 index 1afd13d0..00000000 --- a/website/reference/builder/top-level/on.md +++ /dev/null @@ -1,137 +0,0 @@ -# `on` - -**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: - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(on(String, into))] -struct Example { - id: String, - name: String, - level: u32, -} - -Example::builder() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .build(); -``` - -```rust [Free function] -use bon::builder; - -#[builder(on(String, into))] -fn example( - id: String, - name: String, - level: u32, -) {} - -example() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .call(); -``` - -```rust [Associated method] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder(on(String, into))] - fn example( - id: String, - name: String, - level: u32, - ) {} -} - -Example::example() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .call(); -``` - -::: - -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. - -- `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`. - -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:** - -```rust -use bon::Builder; - -#[derive(Builder)] -#[builder(on(String, into))] -struct Example { - name: String, - description: Option, - - #[builder(default)] - alias: String -} - -Example::builder() - .name("Bon") - // These members also match the `String` type pattern, - // so `#[builder(into)]` was applied to them - .description("accepts an `impl Into` here") - .alias("builder") - .build(); -``` - -You can specify `on(...)` multiple times. - -**Example:** - -```rust -use bon::Builder; -use std::path::PathBuf; - -#[derive(Builder)] -#[builder(on(String, into), on(PathBuf, into))] -struct Example { - name: String, - path: PathBuf, - level: u32, -} - -Example::builder() - .name("accepts `impl Into`") - .path("accepts/impl/into/PathBuf") - // This member doesn't match either `String` or `PathBuf`, - // and thus #[builder(into)] was not applied to it - .level(100) - .build(); -``` diff --git a/website/blog.md b/website/src/blog.md similarity index 82% rename from website/blog.md rename to website/src/blog.md index 2ef2e114..45d6e8de 100644 --- a/website/blog.md +++ b/website/src/blog.md @@ -8,9 +8,9 @@ layout: home diff --git a/website/guide/patterns/conditional-building.md b/website/src/v2/guide/patterns/conditional-building.md similarity index 100% rename from website/guide/patterns/conditional-building.md rename to website/src/v2/guide/patterns/conditional-building.md diff --git a/website/v2/guide/patterns/fallible-builders.md b/website/src/v2/guide/patterns/fallible-builders.md similarity index 100% rename from website/v2/guide/patterns/fallible-builders.md rename to website/src/v2/guide/patterns/fallible-builders.md diff --git a/website/guide/patterns/into-conversions-in-depth.md b/website/src/v2/guide/patterns/into-conversions-in-depth.md similarity index 100% rename from website/guide/patterns/into-conversions-in-depth.md rename to website/src/v2/guide/patterns/into-conversions-in-depth.md diff --git a/website/v2/guide/patterns/shared-configuration.md b/website/src/v2/guide/patterns/shared-configuration.md similarity index 100% rename from website/v2/guide/patterns/shared-configuration.md rename to website/src/v2/guide/patterns/shared-configuration.md diff --git a/website/v2/guide/positional-members.md b/website/src/v2/guide/positional-members.md similarity index 100% rename from website/v2/guide/positional-members.md rename to website/src/v2/guide/positional-members.md diff --git a/website/v2/guide/troubleshooting.md b/website/src/v2/guide/troubleshooting.md similarity index 100% rename from website/v2/guide/troubleshooting.md rename to website/src/v2/guide/troubleshooting.md diff --git a/website/v1/reference/bon.md b/website/src/v2/reference/bon.md similarity index 100% rename from website/v1/reference/bon.md rename to website/src/v2/reference/bon.md diff --git a/website/v2/reference/builder.md b/website/src/v2/reference/builder.md similarity index 100% rename from website/v2/reference/builder.md rename to website/src/v2/reference/builder.md