diff --git a/Cargo.lock b/Cargo.lock index 3830ef5b..138c6a83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,16 @@ dependencies = [ "syn", ] +[[package]] +name = "bon-sandbox" +version = "2.3.0" +dependencies = [ + "bon", + "buildstructor", + "derive_builder", + "typed-builder", +] + [[package]] name = "borsh" version = "1.5.1" @@ -395,13 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] -name = "drop_bomb" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" - -[[package]] -name = "e2e-tests" +name = "doctests" version = "0.1.0" dependencies = [ "anyhow", @@ -413,6 +417,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "drop_bomb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" + [[package]] name = "either" version = "1.13.0" diff --git a/Cargo.toml b/Cargo.toml index b6e4bd1a..ed864851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ members = [ "bon", "bon-cli", "bon-macros", - "e2e-tests", + "bon-sandbox", + "website/doctests", ] resolver = "2" diff --git a/README.v3.md b/README.v3.md index 0f9cba6b..1117c9b2 100644 --- a/README.v3.md +++ b/README.v3.md @@ -206,6 +206,16 @@ This project was heavily inspired by such awesome crates as [`buildstructor`](ht See [alternatives](https://bon-rs.com/guide/alternatives) for comparison. +## Who's Using `bon`? + +Some notable users: + +- [`crates.io` backend](https://github.com/rust-lang/crates.io) +- [`ractor`](https://github.com/slawlor/ractor) +- [`comrak`](https://github.com/kivikakk/comrak) +- [`soldeer`](https://github.com/mario-eth/soldeer) (package manager endorsed by [`foundry`](https://github.com/foundry-rs/foundry)) +- [`tachyonfx`](https://github.com/junkdog/tachyonfx) + ## Getting Help If you can't figure something out, consult the docs and maybe use the `🔍 Search` bar on our [docs website](https://bon-rs.com). You may also create an issue or a discussion on the [Github repository](https://github.com/elastio/bon) for help or write us a message on [Discord](https://bon-rs.com/discord). diff --git a/benchmarks/compilation/codegen/src/main.rs b/benchmarks/compilation/codegen/src/main.rs index 084b4411..be862ad2 100644 --- a/benchmarks/compilation/codegen/src/main.rs +++ b/benchmarks/compilation/codegen/src/main.rs @@ -10,8 +10,8 @@ fn main() -> anyhow::Result<()> { let src_dir = bench_dir.join("src"); - let structs_number = 100; - let fields_number = 10; + let structs_number = 10; + let fields_number = 50; std::fs::write( src_dir.join(format!( @@ -51,10 +51,6 @@ fn structs_n_fields_n(structs_number: usize, fields_number: usize) -> TokenStrea ), derive(crate::Builder), )] - #[cfg_attr( - feature = "bon-overwritable", - builder(on(_, overwritable)), - )] pub struct #struct_name { #( #field_names: i32, )* } diff --git a/benchmarks/compilation/results.md b/benchmarks/compilation/results.md index 5af1cb01..5217a0e9 100644 --- a/benchmarks/compilation/results.md +++ b/benchmarks/compilation/results.md @@ -1,12 +1,10 @@ -| Command | Mean [s] | Min [s] | Max [s] | Relative | -| :--------------------------------------- | ------------: | ------: | ------: | -----------: | -| `structs_100_fields_10 bon` | 2.319 ± 0.021 | 2.278 | 2.350 | 22.80 ± 2.99 | -| `structs_100_fields_10 bon-overwritable` | 2.240 ± 0.021 | 2.203 | 2.274 | 22.03 ± 2.89 | -| `structs_100_fields_10 typed-builder` | 1.849 ± 0.011 | 1.838 | 1.878 | 18.18 ± 2.38 | -| `structs_100_fields_10 derive_builder` | 1.022 ± 0.016 | 0.995 | 1.043 | 10.05 ± 1.32 | -| `structs_100_fields_10 ` | 0.104 ± 0.012 | 0.088 | 0.124 | 1.02 ± 0.18 | -| `structs_10_fields_50 bon` | 2.063 ± 0.017 | 2.044 | 2.100 | 20.28 ± 2.66 | -| `structs_10_fields_50 bon-overwritable` | 2.029 ± 0.029 | 1.998 | 2.102 | 19.95 ± 2.62 | -| `structs_10_fields_50 typed-builder` | 2.076 ± 0.016 | 2.048 | 2.101 | 20.41 ± 2.67 | -| `structs_10_fields_50 derive_builder` | 0.432 ± 0.016 | 0.400 | 0.458 | 4.25 ± 0.58 | -| `structs_10_fields_50 ` | 0.102 ± 0.013 | 0.084 | 0.130 | 1.00 | +| Command | Mean [s] | Min [s] | Max [s] | Relative | +| :------------------------------------- | ------------: | ------: | ------: | -----------: | +| `structs_100_fields_10 bon` | 2.340 ± 0.019 | 2.315 | 2.364 | 20.86 ± 1.37 | +| `structs_100_fields_10 typed-builder` | 1.831 ± 0.015 | 1.806 | 1.859 | 16.32 ± 1.07 | +| `structs_100_fields_10 derive_builder` | 1.026 ± 0.013 | 1.009 | 1.048 | 9.14 ± 0.61 | +| `structs_100_fields_10 ` | 0.113 ± 0.010 | 0.092 | 0.125 | 1.01 ± 0.11 | +| `structs_10_fields_50 bon` | 2.096 ± 0.009 | 2.080 | 2.109 | 18.69 ± 1.22 | +| `structs_10_fields_50 typed-builder` | 2.088 ± 0.014 | 2.067 | 2.115 | 18.61 ± 1.22 | +| `structs_10_fields_50 derive_builder` | 0.449 ± 0.022 | 0.391 | 0.470 | 4.00 ± 0.33 | +| `structs_10_fields_50 ` | 0.112 ± 0.007 | 0.086 | 0.116 | 1.00 | diff --git a/benchmarks/compilation/run.sh b/benchmarks/compilation/run.sh index 74d5dc8d..2d72a764 100755 --- a/benchmarks/compilation/run.sh +++ b/benchmarks/compilation/run.sh @@ -4,7 +4,6 @@ set -euxo pipefail macros=( bon - bon-overwritable typed-builder derive_builder ) diff --git a/benchmarks/compilation/src/structs_100_fields_10.rs b/benchmarks/compilation/src/structs_100_fields_10.rs index a7e1992b..2f1f0c43 100644 --- a/benchmarks/compilation/src/structs_100_fields_10.rs +++ b/benchmarks/compilation/src/structs_100_fields_10.rs @@ -2,7 +2,6 @@ any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct1 { x1: i32, x2: i32, @@ -19,7 +18,6 @@ pub struct Struct1 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct2 { x1: i32, x2: i32, @@ -36,7 +34,6 @@ pub struct Struct2 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct3 { x1: i32, x2: i32, @@ -53,7 +50,6 @@ pub struct Struct3 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct4 { x1: i32, x2: i32, @@ -70,7 +66,6 @@ pub struct Struct4 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct5 { x1: i32, x2: i32, @@ -87,7 +82,6 @@ pub struct Struct5 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct6 { x1: i32, x2: i32, @@ -104,7 +98,6 @@ pub struct Struct6 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct7 { x1: i32, x2: i32, @@ -121,7 +114,6 @@ pub struct Struct7 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct8 { x1: i32, x2: i32, @@ -138,7 +130,6 @@ pub struct Struct8 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct9 { x1: i32, x2: i32, @@ -155,7 +146,6 @@ pub struct Struct9 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct10 { x1: i32, x2: i32, @@ -172,7 +162,6 @@ pub struct Struct10 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct11 { x1: i32, x2: i32, @@ -189,7 +178,6 @@ pub struct Struct11 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct12 { x1: i32, x2: i32, @@ -206,7 +194,6 @@ pub struct Struct12 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct13 { x1: i32, x2: i32, @@ -223,7 +210,6 @@ pub struct Struct13 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct14 { x1: i32, x2: i32, @@ -240,7 +226,6 @@ pub struct Struct14 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct15 { x1: i32, x2: i32, @@ -257,7 +242,6 @@ pub struct Struct15 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct16 { x1: i32, x2: i32, @@ -274,7 +258,6 @@ pub struct Struct16 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct17 { x1: i32, x2: i32, @@ -291,7 +274,6 @@ pub struct Struct17 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct18 { x1: i32, x2: i32, @@ -308,7 +290,6 @@ pub struct Struct18 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct19 { x1: i32, x2: i32, @@ -325,7 +306,6 @@ pub struct Struct19 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct20 { x1: i32, x2: i32, @@ -342,7 +322,6 @@ pub struct Struct20 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct21 { x1: i32, x2: i32, @@ -359,7 +338,6 @@ pub struct Struct21 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct22 { x1: i32, x2: i32, @@ -376,7 +354,6 @@ pub struct Struct22 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct23 { x1: i32, x2: i32, @@ -393,7 +370,6 @@ pub struct Struct23 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct24 { x1: i32, x2: i32, @@ -410,7 +386,6 @@ pub struct Struct24 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct25 { x1: i32, x2: i32, @@ -427,7 +402,6 @@ pub struct Struct25 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct26 { x1: i32, x2: i32, @@ -444,7 +418,6 @@ pub struct Struct26 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct27 { x1: i32, x2: i32, @@ -461,7 +434,6 @@ pub struct Struct27 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct28 { x1: i32, x2: i32, @@ -478,7 +450,6 @@ pub struct Struct28 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct29 { x1: i32, x2: i32, @@ -495,7 +466,6 @@ pub struct Struct29 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct30 { x1: i32, x2: i32, @@ -512,7 +482,6 @@ pub struct Struct30 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct31 { x1: i32, x2: i32, @@ -529,7 +498,6 @@ pub struct Struct31 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct32 { x1: i32, x2: i32, @@ -546,7 +514,6 @@ pub struct Struct32 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct33 { x1: i32, x2: i32, @@ -563,7 +530,6 @@ pub struct Struct33 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct34 { x1: i32, x2: i32, @@ -580,7 +546,6 @@ pub struct Struct34 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct35 { x1: i32, x2: i32, @@ -597,7 +562,6 @@ pub struct Struct35 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct36 { x1: i32, x2: i32, @@ -614,7 +578,6 @@ pub struct Struct36 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct37 { x1: i32, x2: i32, @@ -631,7 +594,6 @@ pub struct Struct37 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct38 { x1: i32, x2: i32, @@ -648,7 +610,6 @@ pub struct Struct38 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct39 { x1: i32, x2: i32, @@ -665,7 +626,6 @@ pub struct Struct39 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct40 { x1: i32, x2: i32, @@ -682,7 +642,6 @@ pub struct Struct40 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct41 { x1: i32, x2: i32, @@ -699,7 +658,6 @@ pub struct Struct41 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct42 { x1: i32, x2: i32, @@ -716,7 +674,6 @@ pub struct Struct42 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct43 { x1: i32, x2: i32, @@ -733,7 +690,6 @@ pub struct Struct43 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct44 { x1: i32, x2: i32, @@ -750,7 +706,6 @@ pub struct Struct44 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct45 { x1: i32, x2: i32, @@ -767,7 +722,6 @@ pub struct Struct45 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct46 { x1: i32, x2: i32, @@ -784,7 +738,6 @@ pub struct Struct46 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct47 { x1: i32, x2: i32, @@ -801,7 +754,6 @@ pub struct Struct47 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct48 { x1: i32, x2: i32, @@ -818,7 +770,6 @@ pub struct Struct48 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct49 { x1: i32, x2: i32, @@ -835,7 +786,6 @@ pub struct Struct49 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct50 { x1: i32, x2: i32, @@ -852,7 +802,6 @@ pub struct Struct50 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct51 { x1: i32, x2: i32, @@ -869,7 +818,6 @@ pub struct Struct51 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct52 { x1: i32, x2: i32, @@ -886,7 +834,6 @@ pub struct Struct52 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct53 { x1: i32, x2: i32, @@ -903,7 +850,6 @@ pub struct Struct53 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct54 { x1: i32, x2: i32, @@ -920,7 +866,6 @@ pub struct Struct54 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct55 { x1: i32, x2: i32, @@ -937,7 +882,6 @@ pub struct Struct55 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct56 { x1: i32, x2: i32, @@ -954,7 +898,6 @@ pub struct Struct56 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct57 { x1: i32, x2: i32, @@ -971,7 +914,6 @@ pub struct Struct57 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct58 { x1: i32, x2: i32, @@ -988,7 +930,6 @@ pub struct Struct58 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct59 { x1: i32, x2: i32, @@ -1005,7 +946,6 @@ pub struct Struct59 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct60 { x1: i32, x2: i32, @@ -1022,7 +962,6 @@ pub struct Struct60 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct61 { x1: i32, x2: i32, @@ -1039,7 +978,6 @@ pub struct Struct61 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct62 { x1: i32, x2: i32, @@ -1056,7 +994,6 @@ pub struct Struct62 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct63 { x1: i32, x2: i32, @@ -1073,7 +1010,6 @@ pub struct Struct63 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct64 { x1: i32, x2: i32, @@ -1090,7 +1026,6 @@ pub struct Struct64 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct65 { x1: i32, x2: i32, @@ -1107,7 +1042,6 @@ pub struct Struct65 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct66 { x1: i32, x2: i32, @@ -1124,7 +1058,6 @@ pub struct Struct66 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct67 { x1: i32, x2: i32, @@ -1141,7 +1074,6 @@ pub struct Struct67 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct68 { x1: i32, x2: i32, @@ -1158,7 +1090,6 @@ pub struct Struct68 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct69 { x1: i32, x2: i32, @@ -1175,7 +1106,6 @@ pub struct Struct69 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct70 { x1: i32, x2: i32, @@ -1192,7 +1122,6 @@ pub struct Struct70 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct71 { x1: i32, x2: i32, @@ -1209,7 +1138,6 @@ pub struct Struct71 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct72 { x1: i32, x2: i32, @@ -1226,7 +1154,6 @@ pub struct Struct72 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct73 { x1: i32, x2: i32, @@ -1243,7 +1170,6 @@ pub struct Struct73 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct74 { x1: i32, x2: i32, @@ -1260,7 +1186,6 @@ pub struct Struct74 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct75 { x1: i32, x2: i32, @@ -1277,7 +1202,6 @@ pub struct Struct75 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct76 { x1: i32, x2: i32, @@ -1294,7 +1218,6 @@ pub struct Struct76 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct77 { x1: i32, x2: i32, @@ -1311,7 +1234,6 @@ pub struct Struct77 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct78 { x1: i32, x2: i32, @@ -1328,7 +1250,6 @@ pub struct Struct78 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct79 { x1: i32, x2: i32, @@ -1345,7 +1266,6 @@ pub struct Struct79 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct80 { x1: i32, x2: i32, @@ -1362,7 +1282,6 @@ pub struct Struct80 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct81 { x1: i32, x2: i32, @@ -1379,7 +1298,6 @@ pub struct Struct81 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct82 { x1: i32, x2: i32, @@ -1396,7 +1314,6 @@ pub struct Struct82 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct83 { x1: i32, x2: i32, @@ -1413,7 +1330,6 @@ pub struct Struct83 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct84 { x1: i32, x2: i32, @@ -1430,7 +1346,6 @@ pub struct Struct84 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct85 { x1: i32, x2: i32, @@ -1447,7 +1362,6 @@ pub struct Struct85 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct86 { x1: i32, x2: i32, @@ -1464,7 +1378,6 @@ pub struct Struct86 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct87 { x1: i32, x2: i32, @@ -1481,7 +1394,6 @@ pub struct Struct87 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct88 { x1: i32, x2: i32, @@ -1498,7 +1410,6 @@ pub struct Struct88 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct89 { x1: i32, x2: i32, @@ -1515,7 +1426,6 @@ pub struct Struct89 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct90 { x1: i32, x2: i32, @@ -1532,7 +1442,6 @@ pub struct Struct90 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct91 { x1: i32, x2: i32, @@ -1549,7 +1458,6 @@ pub struct Struct91 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct92 { x1: i32, x2: i32, @@ -1566,7 +1474,6 @@ pub struct Struct92 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct93 { x1: i32, x2: i32, @@ -1583,7 +1490,6 @@ pub struct Struct93 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct94 { x1: i32, x2: i32, @@ -1600,7 +1506,6 @@ pub struct Struct94 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct95 { x1: i32, x2: i32, @@ -1617,7 +1522,6 @@ pub struct Struct95 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct96 { x1: i32, x2: i32, @@ -1634,7 +1538,6 @@ pub struct Struct96 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct97 { x1: i32, x2: i32, @@ -1651,7 +1554,6 @@ pub struct Struct97 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct98 { x1: i32, x2: i32, @@ -1668,7 +1570,6 @@ pub struct Struct98 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct99 { x1: i32, x2: i32, @@ -1685,7 +1586,6 @@ pub struct Struct99 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct100 { x1: i32, x2: i32, diff --git a/benchmarks/compilation/src/structs_10_fields_50.rs b/benchmarks/compilation/src/structs_10_fields_50.rs index 8f3edea1..e3026f62 100644 --- a/benchmarks/compilation/src/structs_10_fields_50.rs +++ b/benchmarks/compilation/src/structs_10_fields_50.rs @@ -2,7 +2,6 @@ any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct1 { x1: i32, x2: i32, @@ -59,7 +58,6 @@ pub struct Struct1 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct2 { x1: i32, x2: i32, @@ -116,7 +114,6 @@ pub struct Struct2 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct3 { x1: i32, x2: i32, @@ -173,7 +170,6 @@ pub struct Struct3 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct4 { x1: i32, x2: i32, @@ -230,7 +226,6 @@ pub struct Struct4 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct5 { x1: i32, x2: i32, @@ -287,7 +282,6 @@ pub struct Struct5 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct6 { x1: i32, x2: i32, @@ -344,7 +338,6 @@ pub struct Struct6 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct7 { x1: i32, x2: i32, @@ -401,7 +394,6 @@ pub struct Struct7 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct8 { x1: i32, x2: i32, @@ -458,7 +450,6 @@ pub struct Struct8 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct9 { x1: i32, x2: i32, @@ -515,7 +506,6 @@ pub struct Struct9 { any(feature = "bon", feature = "typed-builder", feature = "derive_builder",), derive(crate::Builder) )] -#[cfg_attr(feature = "bon-overwritable", builder(on(_, overwritable)))] pub struct Struct10 { x1: i32, x2: i32, diff --git a/bon-macros/src/builder/builder_gen/setters/mod.rs b/bon-macros/src/builder/builder_gen/setters/mod.rs index 1b4592ab..6d8a1dc9 100644 --- a/bon-macros/src/builder/builder_gen/setters/mod.rs +++ b/bon-macros/src/builder/builder_gen/setters/mod.rs @@ -499,9 +499,7 @@ impl SettersItems { if member.is_required() { let docs = common_docs.unwrap_or(&member.docs); - let header = "\ - | **Required** |\n\ - | -- |\n\n"; + let header = "_**Required.**_\n\n"; let docs = doc(header).chain(docs.iter().cloned()).collect(); @@ -560,8 +558,10 @@ impl SettersItems { .or(common_docs) .unwrap_or(&member.docs); + let setter_names = (&some_fn_name, &option_fn_name); + let some_fn_docs = { - let header = optional_setter_docs(default, &option_fn_name, "accepts an `Option`"); + let header = optional_setter_docs(default, setter_names); doc(&header).chain(some_fn_docs.iter().cloned()).collect() }; @@ -572,11 +572,7 @@ impl SettersItems { .unwrap_or(&member.docs); let option_fn_docs = { - let header = optional_setter_docs( - default, - &some_fn_name, - "wraps the value with `Some` internally", - ); + let header = optional_setter_docs(default, setter_names); doc(&header).chain(option_fn_docs.iter().cloned()).collect() }; @@ -611,24 +607,23 @@ impl SettersItems { fn optional_setter_docs( default: Option<&str>, - other_setter: &syn::Ident, - description: &str, + (some_fn, option_fn): (&syn::Ident, &syn::Ident), ) -> String { let default = default .map(|default| { if default.contains('\n') || default.len() > 80 { - format!("**Default:**\n````rust,ignore\n{default}\n````\n\n") + format!(" _**Default:**_\n````rust,ignore\n{default}\n````\n\n") } else { - format!("**Default:** ```{default}```.\n\n") + format!(" _**Default:**_ ```{default}```.\n\n") } }) .unwrap_or_default(); format!( - "| **Optional** |\n\ - | -- |\n\n\ - **See also** [`{other_setter}()`](Self::{other_setter}), which is a companion setter that {description}. - \n\n{default}", + "_**Optional** \ + ([Some](Self::{some_fn}()) / [Option](Self::{option_fn}()) setters).\ + _{default}\ + \n\n" ) } diff --git a/bon-macros/tests/snapshots/setters_docs_and_vis.rs b/bon-macros/tests/snapshots/setters_docs_and_vis.rs index 52763184..7480a35d 100644 --- a/bon-macros/tests/snapshots/setters_docs_and_vis.rs +++ b/bon-macros/tests/snapshots/setters_docs_and_vis.rs @@ -2,8 +2,7 @@ #[automatically_derived] #[allow(deprecated)] impl SutBuilder { - /**| **Required** | -| -- | + /**_**Required.**_ */ /// Docs on the required field setters. @@ -15,11 +14,7 @@ impl SutBuilder { where S::RequiredField: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`maybe_optional_field()`](Self::maybe_optional_field), which is a companion setter that accepts an `Option`. - + /**_**Optional** ([Some](Self::optional_field()) / [Option](Self::maybe_optional_field()) setters)._ */ /// Docs on the optional field setters. @@ -31,11 +26,7 @@ impl SutBuilder { where S::OptionalField: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`optional_field()`](Self::optional_field), which is a companion setter that wraps the value with `Some` internally. - + /**_**Optional** ([Some](Self::optional_field()) / [Option](Self::maybe_optional_field()) setters)._ */ /// Docs on the optional field setters. @@ -47,13 +38,9 @@ impl SutBuilder { where S::OptionalField: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`maybe_default_field()`](Self::maybe_default_field), which is a companion setter that accepts an `Option`. + /**_**Optional** ([Some](Self::default_field()) / [Option](Self::maybe_default_field()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**Default:** ```2 + 2 * 3```. */ /// Docs on the default field setters. @@ -65,14 +52,10 @@ impl SutBuilder { where S::DefaultField: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | + /**_**Optional** ([Some](Self::default_field()) / [Option](Self::maybe_default_field()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**See also** [`default_field()`](Self::default_field), which is a companion setter that wraps the value with `Some` internally. -**Default:** ```2 + 2 * 3```. - */ /// Docs on the default field setters. /// Multiline. @@ -83,11 +66,7 @@ impl SutBuilder { where S::DefaultField: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`maybe_optional_field_with_specific_overrides()`](Self::maybe_optional_field_with_specific_overrides), which is a companion setter that accepts an `Option`. - + /**_**Optional** ([Some](Self::optional_field_with_specific_overrides()) / [Option](Self::maybe_optional_field_with_specific_overrides()) setters)._ */ /// Docs on some_fn @@ -99,11 +78,7 @@ impl SutBuilder { where S::OptionalFieldWithSpecificOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`optional_field_with_specific_overrides()`](Self::optional_field_with_specific_overrides), which is a companion setter that wraps the value with `Some` internally. - + /**_**Optional** ([Some](Self::optional_field_with_specific_overrides()) / [Option](Self::maybe_optional_field_with_specific_overrides()) setters)._ */ /// Docs on option_fn @@ -115,13 +90,9 @@ impl SutBuilder { where S::OptionalFieldWithSpecificOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`maybe_default_field_with_specific_overrides()`](Self::maybe_default_field_with_specific_overrides), which is a companion setter that accepts an `Option`. + /**_**Optional** ([Some](Self::default_field_with_specific_overrides()) / [Option](Self::maybe_default_field_with_specific_overrides()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**Default:** ```2 + 2 * 3```. */ /// Docs on some_fn @@ -133,13 +104,9 @@ impl SutBuilder { where S::DefaultFieldWithSpecificOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`default_field_with_specific_overrides()`](Self::default_field_with_specific_overrides), which is a companion setter that wraps the value with `Some` internally. + /**_**Optional** ([Some](Self::default_field_with_specific_overrides()) / [Option](Self::maybe_default_field_with_specific_overrides()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**Default:** ```2 + 2 * 3```. */ /// Docs on option_fn @@ -151,11 +118,7 @@ impl SutBuilder { where S::DefaultFieldWithSpecificOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`maybe_optional_field_with_inherited_overrides()`](Self::maybe_optional_field_with_inherited_overrides), which is a companion setter that accepts an `Option`. - + /**_**Optional** ([Some](Self::optional_field_with_inherited_overrides()) / [Option](Self::maybe_optional_field_with_inherited_overrides()) setters)._ */ /// Common docs @@ -167,11 +130,7 @@ impl SutBuilder { where S::OptionalFieldWithInheritedOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`optional_field_with_inherited_overrides()`](Self::optional_field_with_inherited_overrides), which is a companion setter that wraps the value with `Some` internally. - + /**_**Optional** ([Some](Self::optional_field_with_inherited_overrides()) / [Option](Self::maybe_optional_field_with_inherited_overrides()) setters)._ */ /// Docs on option_fn @@ -183,14 +142,10 @@ impl SutBuilder { where S::OptionalFieldWithInheritedOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | + /**_**Optional** ([Some](Self::default_field_with_inherited_overrides()) / [Option](Self::maybe_default_field_with_inherited_overrides()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**See also** [`maybe_default_field_with_inherited_overrides()`](Self::maybe_default_field_with_inherited_overrides), which is a companion setter that accepts an `Option`. -**Default:** ```2 + 2 * 3```. - */ /// Common docs /// Multiline. @@ -201,13 +156,9 @@ impl SutBuilder { where S::DefaultFieldWithInheritedOverrides: sut_builder::IsUnset, {} - /**| **Optional** | -| -- | - -**See also** [`default_field_with_inherited_overrides()`](Self::default_field_with_inherited_overrides), which is a companion setter that wraps the value with `Some` internally. + /**_**Optional** ([Some](Self::default_field_with_inherited_overrides()) / [Option](Self::maybe_default_field_with_inherited_overrides()) setters)._ _**Default:**_ ```2 + 2 * 3```. -**Default:** ```2 + 2 * 3```. */ /// Docs on option_fn diff --git a/bon-sandbox/Cargo.toml b/bon-sandbox/Cargo.toml new file mode 100644 index 00000000..02c364b5 --- /dev/null +++ b/bon-sandbox/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "bon-sandbox" +version = "2.3.0" + +description = """ + Not a real crate! It's just a showcase of examples used by `bon`'s documentation + to demonstrate the rustdoc output for code generated by builder macros. + + Don't use this crate, it doesn't follow semver at all and serves no other purpose + other than linking to its docs as an example! +""" + +edition = "2021" +homepage = "https://bon-rs.com" +license = "MIT OR Apache-2.0" +repository = "https://github.com/elastio/bon" + +[package.metadata.docs.rs] +all-features = true + +# Generate clickable links in the source code view in the docs +rustdoc-args = ["--generate-link-to-definition"] + +# We don't need the docs to be built for every first-tier target. +# This crate is not platform-specific. +targets = ["x86_64-unknown-linux-gnu"] + +[lints] +workspace = true + +[dependencies] +bon = { path = "../bon", version = "=2.3.0", features = ["experimental-overwritable"] } +buildstructor = "0.5" +derive_builder = "0.20" +typed-builder = "0.20" diff --git a/bon-sandbox/src/attr_default.rs b/bon-sandbox/src/attr_default.rs new file mode 100644 index 00000000..a43d9cf5 --- /dev/null +++ b/bon-sandbox/src/attr_default.rs @@ -0,0 +1,30 @@ +#[derive(bon::Builder)] +#[builder(builder_type( + doc { + /// Showcases examples of `#[builder(default)]` usage and what docs are + /// generated for them. Click on any of `source` links to see the source code. + } +))] +pub struct Example { + #[builder(default = (2 + 2) * 10)] + small_custom_default: u32, + + #[builder(default = Vec::from([ + Point { x: 1, y: 2 }, + Point { x: 3, y: 4 }, + Point { x: 5, y: 6 }, + Point { x: 7, y: 8 }, + ]))] + big_custom_default: Vec, + + #[builder(default)] + standard_u32_default: u32, + + #[builder(default)] + standard_string_default: String, +} + +struct Point { + x: u32, + y: u32, +} diff --git a/e2e-tests/src/attr_with.rs b/bon-sandbox/src/attr_with.rs similarity index 100% rename from e2e-tests/src/attr_with.rs rename to bon-sandbox/src/attr_with.rs diff --git a/bon-sandbox/src/docs_comparison/functions.rs b/bon-sandbox/src/docs_comparison/functions.rs new file mode 100644 index 00000000..3fb3f4fd --- /dev/null +++ b/bon-sandbox/src/docs_comparison/functions.rs @@ -0,0 +1,21 @@ +/// Example docs generated with `bon` +pub mod bon { + /// Doc comment on `function` + #[bon::builder] + pub fn function( + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + #[builder(default = 2 + 2)] + x2: u32, + + /// Doc comment on `x3` + x3: Option, + + /// Doc comment on `x4` + #[builder(into)] + x4: String, + ) { + } +} diff --git a/bon-sandbox/src/docs_comparison/methods.rs b/bon-sandbox/src/docs_comparison/methods.rs new file mode 100644 index 00000000..1c68c7af --- /dev/null +++ b/bon-sandbox/src/docs_comparison/methods.rs @@ -0,0 +1,44 @@ +/// Example docs generated with `bon` +pub mod bon { + /// Doc comment on `Struct` + pub struct Struct; + + #[bon::bon] + impl Struct { + /// Doc comment on `method` + #[builder] + pub fn method( + &self, + + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + #[builder(default = 2 + 2)] + x2: u32, + + /// Doc comment on `x3` + x3: Option, + + /// Doc comment on `x4` + #[builder(into)] + x4: String, + ) { + } + } +} + +/// Example docs generated with `buildstructor` +#[expect(elided_lifetimes_in_paths)] +pub mod buildstructor { + + /// Doc comment on `Struct` + pub struct Struct; + + #[buildstructor::buildstructor] + impl Struct { + /// Doc comment on `method_orig` + #[builder(entry = "method")] + pub fn method_orig(&self, x1: u32, x2: u32, x3: Option, x4: String) {} + } +} diff --git a/bon-sandbox/src/docs_comparison/mod.rs b/bon-sandbox/src/docs_comparison/mod.rs new file mode 100644 index 00000000..bc480aad --- /dev/null +++ b/bon-sandbox/src/docs_comparison/mod.rs @@ -0,0 +1,27 @@ +//! This module contains examples of `rustdoc` output for builder macros from different +//! crates applied to different kinds of syntax. +//! +//! The module hierarchy here is `syntax` -> `builder_crate`. +//! +//! All builders were configured to produce roughly similar builder API. +//! +//! The notable exceptions are: +//! +//! - `buildstructor` doesn't support `#[builder(default)]` and `#[builder(into)]`-like annotations; +//! - `buildstructor` doesn't support doc comments on function arguments; +//! - `derive_builder` doesn't support typestate-based builders; +#![expect( + dead_code, + unused_variables, + clippy::needless_pass_by_value, + clippy::unused_self +)] + +/// Examples docs generated with builder macros applied to functions +pub mod functions; + +/// Examples docs generated with builder macros applied to structs +pub mod structs; + +/// Examples docs generated with builder macros applied to methods +pub mod methods; diff --git a/bon-sandbox/src/docs_comparison/structs.rs b/bon-sandbox/src/docs_comparison/structs.rs new file mode 100644 index 00000000..342876f9 --- /dev/null +++ b/bon-sandbox/src/docs_comparison/structs.rs @@ -0,0 +1,85 @@ +/// Example docs generated with `bon` +pub mod bon { + /// Doc comment on `Struct` + #[derive(bon::Builder)] + pub struct Struct { + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + #[builder(default = 2 + 2)] + x2: u32, + + /// Doc comment on `x3` + x3: Option, + + /// Doc comment on `x4` + #[builder(into)] + x4: String, + } +} + +/// Example docs generated with `buildstructor` +pub mod buildstructor { + /// Doc comment on `Struct` + #[derive(buildstructor::Builder)] + pub struct Struct { + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + x2: u32, + + /// Doc comment on `x3` + x3: Option, + + /// Doc comment on `x4` + x4: String, + } +} + +/// Example docs generated with `typed-builder` +pub mod typed_builder { + + /// Doc comment on `Struct` + #[derive(typed_builder::TypedBuilder)] + #[builder(doc)] + pub struct Struct { + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + #[builder(default = 2 + 2)] + x2: u32, + + /// Doc comment on `x3` + #[builder(default)] + x3: Option, + + /// Doc comment on `x4` + #[builder(setter(into))] + x4: String, + } +} + +/// Example docs generated with `derive_builder` +pub mod derive_builder { + /// Doc comment on `Struct` + #[derive(derive_builder::Builder)] + pub struct Struct { + /// Doc comment on `x1` + x1: u32, + + /// Doc comment on `x2` + #[builder(default = 2 + 2)] + x2: u32, + + /// Doc comment on `x3` + #[builder(default)] + x3: Option, + + /// Doc comment on `x4` + #[builder(setter(into))] + x4: String, + } +} diff --git a/e2e-tests/src/lib.rs b/bon-sandbox/src/functions.rs similarity index 57% rename from e2e-tests/src/lib.rs rename to bon-sandbox/src/functions.rs index f5f263a1..d19513bc 100644 --- a/e2e-tests/src/lib.rs +++ b/bon-sandbox/src/functions.rs @@ -1,68 +1,4 @@ -//! This crate is used only for testing of the public interface of the `bon` crate. -//! We don't need all the aggressive lints that we use for public crates. -#![allow(missing_debug_implementations, missing_docs)] - -pub mod attr_with; -pub mod macro_rules_wrapper_test; -pub mod missing_docs_test; -pub mod state_mod_pub; - -mod reexports; - -pub use reexports::{UnexportedBuilder, UnexportedStateMod, UnexportedStateModBuilder}; - -use bon::{bon, builder, Builder}; - -#[cfg(doctest)] -// We use a bunch of Vitepress-specific syntax in the doctests, for example to -// give a name to a code group in a fenced code block, which conflicts with this -// lint. -#[deny(rustdoc::invalid_codeblock_attributes)] -mod website_doctests { - include!(concat!(env!("OUT_DIR"), "/website_doctests.rs")); -} - -/// Some docs on the private builder -#[derive(Builder)] -#[builder(builder_type(vis = ""))] -pub struct PrivateBuilder { - _field: String, -} - -/// Docs on the [`Self`] struct -#[derive(Builder)] -#[builder( - builder_type( - doc { - /// Docs on [`GreeterOverriddenBuilder`] - /// the builder type - }, - name = GreeterOverriddenBuilder, - ), - start_fn( - doc { - /// Docs on - /// [`Self::start_fn_override`] - }, - name = start_fn_override, - ), - finish_fn( - doc { - /// Docs on - /// [`GreeterOverriddenBuilder::finish_fn_override()`] - }, - name = finish_fn_override, - ) -)] -pub struct Greeter { - /// Docs on - /// the `name` field - _name: String, - - /// Docs on - /// the `level` field - _level: usize, -} +use bon::{bon, builder}; pub struct Counter { val: usize, @@ -115,29 +51,19 @@ pub fn documented( _arg4: Vec, - #[builder(default = - Greeter::start_fn_override() - .name( - "Some intentionally big expression to test the fallback to \ - a code fence in the default value docs" - .to_owned() - ) - .level(42) - .finish_fn_override() - )] - _arg5: Greeter, + #[builder(default = vec![1, 2, 3])] _arg5: Vec, ) { eprintln!("Non-const"); } /// Function that returns a greeting special-tailored for a given person -#[builder(builder_type = Foo)] +#[builder(builder_type = GreeterBuilderCustom)] pub fn greet( /// Name of the person to greet. /// /// **Example:** /// ``` - /// e2e_tests::greet().name("John"); + /// bon_sandbox::functions::greet().name("John"); /// ``` name: &str, diff --git a/bon-sandbox/src/lib.rs b/bon-sandbox/src/lib.rs new file mode 100644 index 00000000..9cd816e8 --- /dev/null +++ b/bon-sandbox/src/lib.rs @@ -0,0 +1,20 @@ +//! Not a real crate! It's just a showcase of examples used by `bon`'s documentation +//! to demonstrate the rustdoc output for code generated by builder macros. +//! +//! Don't use this crate, it doesn't follow semver at all and serves no other purpose +//! other than linking to its docs as an example! +#![allow(missing_debug_implementations, missing_docs, dead_code)] + +pub mod attr_default; +pub mod attr_with; +pub mod docs_comparison; +pub mod functions; +pub mod macro_rules_wrapper_test; +pub mod missing_docs_test; +pub mod overrides; +pub mod private_builder; +pub mod state_mod; + +mod reexports; + +pub use reexports::{UnexportedBuilder, UnexportedStateMod, UnexportedStateModBuilder}; diff --git a/e2e-tests/src/macro_rules_wrapper_test.rs b/bon-sandbox/src/macro_rules_wrapper_test.rs similarity index 100% rename from e2e-tests/src/macro_rules_wrapper_test.rs rename to bon-sandbox/src/macro_rules_wrapper_test.rs diff --git a/e2e-tests/src/missing_docs_test.rs b/bon-sandbox/src/missing_docs_test.rs similarity index 100% rename from e2e-tests/src/missing_docs_test.rs rename to bon-sandbox/src/missing_docs_test.rs diff --git a/bon-sandbox/src/overrides.rs b/bon-sandbox/src/overrides.rs new file mode 100644 index 00000000..8186999b --- /dev/null +++ b/bon-sandbox/src/overrides.rs @@ -0,0 +1,34 @@ +/// Docs on the [`Self`] struct +#[derive(bon::Builder)] +#[builder( + builder_type( + doc { + /// Docs on [`GreeterOverriddenBuilder`] + /// the builder type + }, + name = GreeterOverriddenBuilder, + ), + start_fn( + doc { + /// Docs on + /// [`Self::start_fn_override`] + }, + name = start_fn_override, + ), + finish_fn( + doc { + /// Docs on + /// [`GreeterOverriddenBuilder::finish_fn_override()`] + }, + name = finish_fn_override, + ) +)] +pub struct Greeter { + /// Docs on + /// the `name` field + _name: String, + + /// Docs on + /// the `level` field + _level: usize, +} diff --git a/bon-sandbox/src/private_builder.rs b/bon-sandbox/src/private_builder.rs new file mode 100644 index 00000000..c6915217 --- /dev/null +++ b/bon-sandbox/src/private_builder.rs @@ -0,0 +1,6 @@ +/// Some docs on the private builder +#[derive(bon::Builder)] +#[builder(builder_type(vis = ""))] +pub struct PrivateBuilder { + _field: String, +} diff --git a/e2e-tests/src/reexports.rs b/bon-sandbox/src/reexports.rs similarity index 100% rename from e2e-tests/src/reexports.rs rename to bon-sandbox/src/reexports.rs diff --git a/bon/src/examples/comprehensive.rs b/bon-sandbox/src/state_mod/comprehensive.rs similarity index 57% rename from bon/src/examples/comprehensive.rs rename to bon-sandbox/src/state_mod/comprehensive.rs index 4333f590..c13181f1 100644 --- a/bon/src/examples/comprehensive.rs +++ b/bon-sandbox/src/state_mod/comprehensive.rs @@ -3,7 +3,7 @@ //! The preliminary reading of [Typestate API](https://bon-rs.com/guide/typestate-api) //! guide is recommended to understand how the pieces in this example fit together. //! -//! This module contains a struct [`Example`] that was annotated with [`#[derive(Builder)]`](crate::Builder). +//! This module contains a struct [`Example`] that was annotated with [`#[derive(Builder)]`](bon::Builder). //! The config [`#[builder(state_mod(vis = "pub"))]`](https://bon-rs.com/reference/builder/top-level/state_mod) //! was applied to make the generated builder's typestate API public and visible here in the docs. //! @@ -12,16 +12,28 @@ //! - [`example_builder`] - the builder's typestate API module /// Example struct with the `#[derive(Builder)]` annotation. -#[derive(crate::Builder)] -#[builder(crate = crate, state_mod(vis = "pub"))] +#[derive(bon::Builder)] +#[builder(state_mod(vis = "pub"))] pub struct Example { - /// Example required member - x1: u32, + required: u32, - /// Example optional member - x2: Option, + optional: Option, - /// Example member with a default value. - #[builder(default = 2 + 2)] - x3: u32, + #[builder(default)] + default: u32, + + #[builder(overwritable)] + overwritable_required: u32, + + #[builder(overwritable)] + overwritable_optional: Option, + + #[builder(overwritable, default = 2 * 2 + 3)] + overwritable_default: u32, + + #[builder(required)] + required_option: Option, + + #[builder(with = |x: &str| -> Result<_, std::num::ParseIntError> { x.parse() })] + with: u32, } diff --git a/bon/src/examples/minimal.rs b/bon-sandbox/src/state_mod/minimal.rs similarity index 76% rename from bon/src/examples/minimal.rs rename to bon-sandbox/src/state_mod/minimal.rs index 751c9fb9..253d102b 100644 --- a/bon/src/examples/minimal.rs +++ b/bon-sandbox/src/state_mod/minimal.rs @@ -1,13 +1,12 @@ //! Minimal example of the generated builder and its typestate API. //! -//! This documentation was generated as a showcase for the [Builder's Type Signature] -//! guide +//! This documentation was generated as a showcase for the [Builder's Type Signature] guide. //! //! [Builder's Type Signature]: https://bon-rs.com/guide/typestate-api/builders-type-signature /// Example struct with the `#[derive(Builder)]` annotation. -#[derive(crate::Builder)] -#[builder(crate = crate, state_mod(vis = "pub"))] +#[derive(bon::Builder)] +#[builder(state_mod(vis = "pub"))] pub struct Example { x1: u32, x2: u32, diff --git a/bon-sandbox/src/state_mod/mod.rs b/bon-sandbox/src/state_mod/mod.rs new file mode 100644 index 00000000..ec64ba4e --- /dev/null +++ b/bon-sandbox/src/state_mod/mod.rs @@ -0,0 +1,4 @@ +//! Examples of docs generated for the builder's typestate API module. + +pub mod comprehensive; +pub mod minimal; diff --git a/bon/src/examples/mod.rs b/bon/src/examples/mod.rs deleted file mode 100644 index 4192a67c..00000000 --- a/bon/src/examples/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod comprehensive; -pub mod minimal; diff --git a/bon/src/lib.rs b/bon/src/lib.rs index 9076d325..398a3402 100644 --- a/bon/src/lib.rs +++ b/bon/src/lib.rs @@ -26,7 +26,3 @@ mod collections; pub mod __; mod builder_state; - -/// Examples of generated builders' APIs. -#[cfg(doc)] -pub mod examples; diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml deleted file mode 100644 index 9b19bde9..00000000 --- a/e2e-tests/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "e2e-tests" -version = "0.1.0" - -publish = false - -description = """ - Crate for manual end-to-end testing the `bon` crate. For example it's used to check - the quality of the documentation produced by `cargo doc` for the macro-generated - APIs. - - We also use this crate to test the code examples in the documentation in the `website`. -""" - -edition = "2021" - -[lints] -workspace = true - -[dependencies] -bon = { path = "../bon", features = ["experimental-overwritable"] } - -[dev-dependencies] -anyhow = "1.0" -buildstructor = "0.5" -macro_rules_attribute = "0.2" - -[build-dependencies] -itertools = "0.13" -lazy-regex = "3.3" -walkdir = "2.5" diff --git a/e2e-tests/src/state_mod_pub.rs b/e2e-tests/src/state_mod_pub.rs deleted file mode 100644 index 21fc1db7..00000000 --- a/e2e-tests/src/state_mod_pub.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[derive(bon::Builder)] -#[builder(state_mod(vis = "pub"))] -#[allow(dead_code)] -pub struct PubStateMod { - required_arg: u32, - optional_arg: Option, - - #[builder(default)] - default_arg: u32, - - #[builder(overwritable)] - overwritable_required_arg: u32, - - #[builder(overwritable)] - overwritable_optional_arg: Option, - - #[builder(overwritable, default = 2 * 2 + 3)] - overwritable_default_arg: u32, - - #[builder(required)] - required_option_arg: Option, - - #[builder(with = |x: &str| -> Result<_, std::num::ParseIntError> { x.parse() })] - with_arg: u32, -} diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 4ee795a3..c467320f 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -81,7 +81,7 @@ export default defineConfig({ }, }, - srcExclude: ["README.md", "infra/**"], + srcExclude: ["README.md", "infra/**", "doctests/**"], head, diff --git a/website/.vitepress/theme/utils/versioning.ts b/website/.vitepress/theme/utils/versioning.ts index 5847de3d..3116d1aa 100644 --- a/website/.vitepress/theme/utils/versioning.ts +++ b/website/.vitepress/theme/utils/versioning.ts @@ -1,7 +1,7 @@ import { withBase } from "vitepress"; -export const latestVersion = "v3-rc"; -export const versions = ["v3-rc", "v2", "v1"]; +export const latestVersion = "v3"; +export const versions = ["v3", "v2", "v1"]; export const sectionRoots = { guide: "guide/overview", diff --git a/website/doctests/Cargo.toml b/website/doctests/Cargo.toml new file mode 100644 index 00000000..27447b5d --- /dev/null +++ b/website/doctests/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "doctests" +version = "0.1.0" + +publish = false + +description = "Crate for testing Rust code snippets in markdown files on the website." + +edition = "2021" + +[lints] +workspace = true + +[dev-dependencies] +anyhow = "1.0" +bon = { path = "../../bon", features = ["experimental-overwritable"] } +buildstructor = "0.5" +macro_rules_attribute = "0.2" + +[build-dependencies] +itertools = "0.13" +lazy-regex = "3.3" +walkdir = "2.5" diff --git a/e2e-tests/build.rs b/website/doctests/build.rs similarity index 93% rename from e2e-tests/build.rs rename to website/doctests/build.rs index 1e7a3144..9cb1aac8 100644 --- a/e2e-tests/build.rs +++ b/website/doctests/build.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use walkdir::DirEntry; fn main() { - let website = "../website"; + let website = ".."; println!("cargo::rerun-if-changed={website}"); @@ -22,8 +22,8 @@ fn main() { .filter(|entry| { entry.file_type().is_file() && entry.path().extension() == Some("md".as_ref()) - && !entry.path().starts_with("../website/src/v1") - && !entry.path().starts_with("../website/src/v2") + && !entry.path().starts_with("../src/v1") + && !entry.path().starts_with("../src/v2") }) .map(DirEntry::into_path) .sorted_unstable() diff --git a/website/doctests/src/lib.rs b/website/doctests/src/lib.rs new file mode 100644 index 00000000..f575fc23 --- /dev/null +++ b/website/doctests/src/lib.rs @@ -0,0 +1,10 @@ +//! Crate for testing Rust code snippets in markdown files on the website. + +#[cfg(doctest)] +// We use a bunch of Vitepress-specific syntax in the doctests, for example to +// give a name to a code group in a fenced code block, which conflicts with this +// lint. +#[deny(rustdoc::invalid_codeblock_attributes)] +mod website_doctests { + include!(concat!(env!("OUT_DIR"), "/website_doctests.rs")); +} diff --git a/website/src/changelog.md b/website/src/changelog.md index b804ef57..88ce1f3f 100644 --- a/website/src/changelog.md +++ b/website/src/changelog.md @@ -189,7 +189,7 @@ See the [blog post for this release](https://bon-rs.com/blog/bon-builder-v2-1-re ### Docs - Add a new section ["`None` literals inference"](https://bon-rs.com/guide/patterns/into-conversions-in-depth#none-literals-inference) to docs for "Into Conversions In-Depth" -- Fix the docs about the comparison of Into conversions on the ["Alternatives"](http://bon-rs.com/guide/alternatives) page that were not updated during the v2 release +- Fix the docs about the comparison of Into conversions on the ["Alternatives"](https://bon-rs.com/guide/alternatives) page that were not updated during the v2 release ### Fixed diff --git a/website/src/guide/alternatives.md b/website/src/guide/alternatives.md index 4c08ae50..48d76f17 100644 --- a/website/src/guide/alternatives.md +++ b/website/src/guide/alternatives.md @@ -20,7 +20,7 @@ There are several other existing alternative crates for generating builders. `bo | ---------------------------------------- | ---------------------------------- | ------------------------- | ----------------------- | --------------------------------------------- | | Builder for structs | ✅ | ✅ | ✅ | ✅ | | Builder for functions | ✅ | | | -| Builder for associated methods | ✅ | ✅ | | +| Builder for methods | ✅ | ✅ | | | Panic safe | ✅ | ✅ | ✅ | `build()` returns `Result` | | `Option` makes members optional | ✅ | ✅ | | | | `T` -> `Option` is non-breaking | ✅ [docs][bon-req-to-opt] | ✅ | via attr `strip_option` | via attr [`strip_option`][db-so] | @@ -33,6 +33,7 @@ There are several other existing alternative crates for generating builders. `bo | Builder for `fn` hides original `fn` | ✅ | | | | Special setters for collections | [(see below)][collections] | ✅ | | ✅ | | Builder by `&self`/`&mut self` | | | | ✅ | +| [Generates nice docs][gen-docs-cmp] | ✅ | | | ✅ | @@ -86,7 +87,7 @@ Line::builder().x1(1).y1(2).x2(3).y2(4).build(); // [!code error] There are two problems with `#[derive(Builder)]` syntax in this case: 1. This refactoring becomes a breaking change to `Line`'s builder API 😢. -2. The private utility `Point` type leaks through the builder API via `point1`, `point2` setters 😭. +2. The private utility `Point` type leaks through the builder API via `point1`, and `point2` setters 😭. The fundamental problem is that the builder's API is _coupled_ ⛓️ with your struct's internal representation. It's literally `derive`d from the fields of your struct. @@ -143,7 +144,7 @@ And you know what, our old friend `new` doesn't feel offended for being abandone Moreover, it offers you a completely new dimension of flexibility: -- Need some validation? Just make the `new()` method return a `Result`. The generated `build()` method will then become fallible. +- Need some validation? Just change the `new()` method to return a `Result`. The generated `build()` method will then become fallible. - Need to do an `async` operation in the constructor? Just make your constructor `async` and your `build()` will return a `Future`. - Need some adrenaline 💉? Just add `unsafe`, and... you get the idea 😉. @@ -151,7 +152,35 @@ Moreover, it offers you a completely new dimension of flexibility: The chances of hitting a wall with function builders are close to zero, and even if you ever do, you still have access to the [Typestate API](./typestate-api) in `bon` for even more flexibility 💪. -## Special setter methods for collections +## Generated Docs Comparison + +Here is a table that compares the `rustdoc` output for builders generated by different crates based on different syntax. + +| Underlying syntax | `bon` | `buildstructor` | `typed-builder` | `derive_builder` | +| ----------------- | -------------------- | ----------------- | ----------------- | ----------------- | +| Struct | [Link][struct-bon] | [Link][struct-bs] | [Link][struct-tb] | [Link][struct-db] | +| Function | [Link][function-bon] | +| Method | [Link][method-bon] | [Link][method-bs] | + +[struct-bon]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/structs/bon/ +[struct-bs]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/structs/buildstructor/ +[struct-tb]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/structs/typed_builder/ +[struct-db]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/structs/derive_builder/ +[function-bon]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/functions/bon/ +[method-bon]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/methods/bon/ +[method-bs]: https://docs.rs/bon-sandbox/latest/bon_sandbox/docs_comparison/methods/buildstructor/ + +All builders were configured to produce roughly similar builder APIs. The notable exceptions are: + +- `buildstructor` doesn't support `#[builder(default)]` and `#[builder(into)]`-like annotations; +- `buildstructor` doesn't support doc comments on function arguments; +- `derive_builder` doesn't support typestate-based builders; + +Docs generated by `typed-builder` and `buildstructor` suffer from the problem of noisy generics. This problem significantly worsens with the number of fields/arguments in structs/functions. `bon` solves this problem by using a trait-based design for its [typestate](./typestate-api). + +`bon` also includes the default values assigned via [`#[builder(default)]`](../reference/builder/member/default) in the docs ([more examples here](https://docs.rs/bon-sandbox/latest/bon_sandbox/attr_default/struct.ExampleBuilder.html)). + +## Special Setter Methods for Collections Other builder crates provide a way to generate methods to build collections one element at a time. For example, `buildstructor` even generates such methods by default: @@ -176,9 +205,9 @@ Why is there an explicit `main()` function in this code snippet 🤔? It's a lon ::: -This feature isn't available today in `bon`, but it's planned for the future. However, it won't be enabled by default, but rather be opt-in like it is in `derive_builder`. +This feature isn't available today in `bon`, but it's planned for the future. However, it won't be enabled by default; rather, it will be opt-in like it is in `derive_builder`. -The problem with this feature is that a setter that pushes an element into a collection like that may confuse the reader in case if only one element is pushed. This may hide the fact that the member is actually a collection called `friends` in the plural. However, this feature is still useful to provide backwards compatibility when changing the type of a member from `T` or `Option` to `Collection`. +The problem with this feature is that a setter that pushes an element into a collection like that may confuse the reader if only one element is pushed. This may hide the fact that the member is actually a collection called `friends` in the plural. However, this feature is still useful to provide backwards compatibility when changing the type of a member from `T` or `Option` to `Collection`. Alternatively, `bon` provides a separate solution. `bon` exposes the following macros that provide convenient syntax to create collections. @@ -215,6 +244,7 @@ Another difference is that fields of collection types are considered required by [map]: https://docs.rs/bon/latest/bon/macro.map.html [set]: https://docs.rs/bon/latest/bon/macro.set.html [collections]: #special-setter-methods-for-collections +[gen-docs-cmp]: #generated-docs-comparison diff --git a/website/src/guide/benchmarks/compilation.md b/website/src/guide/benchmarks/compilation.md index e69de29b..a12fa6ba 100644 --- a/website/src/guide/benchmarks/compilation.md +++ b/website/src/guide/benchmarks/compilation.md @@ -0,0 +1,77 @@ +# Compilation Benchmarks + +This page compares the compilation performance with `bon` and alternative builder crates. + +--- + +For any proc macro, two things are contributing to compile times: + +1. Compiling the proc macro crate into a dynamic library. +2. Compiling the code generated by the proc macro. + +The benchmarks here intentionally include only `2` in measurements. + +::: details See the reasoning behind this + +The proc macro crate dynamic library is compiled only once and remains in the build cache. For example, once you add `bon` as a dependency, it'll be compiled only once and never recompiled during incremental rebuilds. The cost of that build is rather small and constant. So this isn't of a big interest to the scope of these benchmarks. + +::: + +## Measurements + +The benchmarks measure the time it takes to run `cargo build` against a crate that uses `#[derive(Builder)]` for the given number of structs with the given number of fields in each of them. + +| Builder crate | 10 structs with 50 fields | 100 structs with 10 fields | +| ---------------- | ------------------------- | -------------------------- | +| `bon` | `2.096s` | `2.340s` | +| `typed-builder` | `2.088s` | `1.831s` | +| `derive_builder` | `0.449s` | `1.026s` | +| no macros | `0.112s` | `0.113s` | + +## Summary + +Builder macros do add noticeable overhead to the compilation performance due to the number of additional structs and methods they generate. + +`bon` has more compile time overhead than other crates, which is likely still reasonable, but let's understand why and what we get in return from this. + +### Why does `bon` lose to `typed-builder` in compilation perf? + +`bon`'s generated code is a bit more complex than `typed-builder`'s, because `bon` generates additional traits and structs to expose a nice and stable [typestate API](../typestate-api). + +This is also done to reduce the builder's type signature size and reduce generic type noise in the generated builders' docs. See [docs comparison](../alternatives#generated-docs-comparison) (the difference is drastic). + +### Can `bon` improve compile times? + +Definitely, but it comes at a cost 🪙. `bon` prioritizes the convenience of the generated builder API, documentation cleanness, and higher level of compile-time checks by default. + +If you'd like to improve your compile times, consider disabling compile-time checks for overwrites of optional members in setters with [`#[builder(overwritable)]`](../../reference/builder/member/overwritable). However, don't rush with that, think carefully about the tradeoff you would make. + +### Why `derive_builder` is fastest to compile? + +The main contributor to compile times of code generated by `bon` and `typed-builder` is their usage of generics to represent the typestate. Builders generated by `derive_builder` don't use any generics. + +The main downside of `derive_builder`'s approach is that its finishing `build()` method always returns a `Result`, because it needs to validate, that all required fields were set **at runtime**. + +### Is this all worth compilation time overhead? + +It depends on your use case. + +If you are considering builders in your crate's public API, then it's definitely worth it. One of the main focus areas of `bon` is breaking change prevention (i.e. [Compatibility](../basics/compatibility)) and builder API ergonomics. This way you can provide stable, evolvable and convenient API for your users. + +If you are considering builders in your private modules, then the main feature of `bon` you'd be interested in is [optional members](../basics/optional-members) and nicer syntax. + +The general rule is that builders make more sense for pervasive structs and functions that you have to construct/call frequently. Don't blindly add builders to every other struct or function. If that struct or function is used only in a single place in your code, then using a builder for it may be unnecessary. + +## Hardware + +The benchmarks were run on a dedicated root server `AX51-NVMe` on [Hetzner](https://www.hetzner.com/). + +- OS: Ubuntu 22.04.4 (Linux 5.15.0-76-generic) +- CPU: AMD Ryzen 7 3700X 8-Core Processor (x86_64) +- RAM: 62.8 GiB + +## References + +The source code of the benchmarks is [available here][benchmarks-source]. + +[benchmarks-source]: https://github.com/elastio/bon/tree/master/benchmarks/compilation diff --git a/website/src/index.md b/website/src/index.md index 5e0a5c7f..b7efe1ac 100644 --- a/website/src/index.md +++ b/website/src/index.md @@ -12,6 +12,9 @@ hero: - theme: brand text: Guide link: /guide/overview + - theme: brand + text: Reference + link: /reference/builder - theme: alt text: Blog link: /blog diff --git a/website/src/reference/builder.md b/website/src/reference/builder.md index d6b97bb1..6e80c76f 100644 --- a/website/src/reference/builder.md +++ b/website/src/reference/builder.md @@ -2,10 +2,6 @@ outline: deep --- -# `#[derive(Builder)]` / `#[builder]` - -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 These attributes are placed on top of a `struct` or `fn` declaration. diff --git a/website/src/reference/builder/member/default.md b/website/src/reference/builder/member/default.md index db968bf8..efbb34d8 100644 --- a/website/src/reference/builder/member/default.md +++ b/website/src/reference/builder/member/default.md @@ -146,7 +146,7 @@ assert_eq!(value.x3, "lyra"); ::: -## Evaluation context +## Evaluation Context You can use the values of other members by referencing their names in the `default` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `default` expression. @@ -238,6 +238,12 @@ assert_eq!(value, (3, 6, 9)); The `self` parameter in associated method syntax is not available to the `default` expression. If you need the `self` context for your defaulting logic, then set your member's type to `Option` and handle the defaulting in the function's body manually. -## Compile errors +## Compile Errors This attribute is incompatible with members of `Option` type, since `Option` already implies the default value of `None`. However, it can be used together with [`#[builder(required)]`](./required). + +## Generated Docs + +The documentation for the setters will include the default value expression. If you use `#[builder(default)]`, then the default value expression will be inferred for primitive and some well-known types. + +See [examples of the generated docs](https://docs.rs/bon-sandbox/latest/bon_sandbox/attr_default/struct.ExampleBuilder.html) with `#[builder(default)]`.