From 57558ddd18358a26e445b22dec989688b9b6f0a3 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 27 Oct 2024 17:43:28 +0000 Subject: [PATCH] Prepare docs split for v3 --- website/.vitepress/config.mts | 7 +- website/.vitepress/theme/utils/versioning.ts | 4 +- website/guide/overview.md | 2 + website/package-lock.json | 1041 +++++++++---- website/package.json | 8 +- website/v2/config.mts | 162 ++ website/v2/guide/alternatives.md | 105 ++ website/v2/guide/benchmarks.md | 49 + website/v2/guide/compatibility.md | 207 +++ website/v2/guide/documenting.md | 38 + website/v2/guide/inspecting.md | 33 + website/v2/guide/internal/contributing.md | 46 + website/v2/guide/limitations.md | 149 ++ website/v2/guide/optional-members.md | 111 ++ website/v2/guide/overview.md | 309 ++++ .../v2/guide/patterns/conditional-building.md | 144 ++ .../v2/guide/patterns/fallible-builders.md | 41 + .../patterns/into-conversions-in-depth.md | 379 +++++ .../v2/guide/patterns/shared-configuration.md | 105 ++ website/v2/guide/positional-members.md | 146 ++ website/v2/guide/troubleshooting.md | 23 + website/v2/reference/bon.md | 10 + website/v2/reference/builder.md | 1313 +++++++++++++++++ 23 files changed, 4145 insertions(+), 287 deletions(-) create mode 100644 website/v2/config.mts create mode 100644 website/v2/guide/alternatives.md create mode 100644 website/v2/guide/benchmarks.md create mode 100644 website/v2/guide/compatibility.md create mode 100644 website/v2/guide/documenting.md create mode 100644 website/v2/guide/inspecting.md create mode 100644 website/v2/guide/internal/contributing.md create mode 100644 website/v2/guide/limitations.md create mode 100644 website/v2/guide/optional-members.md create mode 100644 website/v2/guide/overview.md create mode 100644 website/v2/guide/patterns/conditional-building.md create mode 100644 website/v2/guide/patterns/fallible-builders.md create mode 100644 website/v2/guide/patterns/into-conversions-in-depth.md create mode 100644 website/v2/guide/patterns/shared-configuration.md create mode 100644 website/v2/guide/positional-members.md create mode 100644 website/v2/guide/troubleshooting.md create mode 100644 website/v2/reference/bon.md create mode 100644 website/v2/reference/builder.md diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 0f28f111..9df01f8f 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -1,11 +1,13 @@ import { defineConfig } from "vitepress"; import { abbr } from "@mdit/plugin-abbr"; import * as v1 from "../v1/config.mjs"; +import * as v2 from "../v2/config.mjs"; // https://vitepress.dev/reference/site-config export default defineConfig({ title: "Bon", - description: "Generate builders for everything!", + description: + "Next-gen compile-time-checked builder generator, named function's arguments, and more!", cleanUrls: true, lastUpdated: true, @@ -22,6 +24,8 @@ export default defineConfig({ }, }, + srcExclude: ["README.md", "infra/**"], + head: [ ["link", { rel: "icon", href: `bon-logo-thumb.png` }], ["meta", { property: "og:image", content: `bon-logo-thumb.png` }], @@ -71,6 +75,7 @@ export default defineConfig({ sidebar: { ...v1.sidebars, + ...v2.sidebars, "/guide": [ { text: "Guide", diff --git a/website/.vitepress/theme/utils/versioning.ts b/website/.vitepress/theme/utils/versioning.ts index 43fe8c6d..5847de3d 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 = "v2"; -export const versions = ["v2", "v1"]; +export const latestVersion = "v3-rc"; +export const versions = ["v3-rc", "v2", "v1"]; export const sectionRoots = { guide: "guide/overview", diff --git a/website/guide/overview.md b/website/guide/overview.md index b99d89bb..003608e4 100644 --- a/website/guide/overview.md +++ b/website/guide/overview.md @@ -269,6 +269,7 @@ If you can't figure something out, consult the docs and maybe use that search ` ## Socials + +
@@ -293,6 +294,7 @@ If you can't figure something out, consult the docs and maybe use that search `
Profile of the maintainer. There are only posts about bon and Rust in general here.
diff --git a/website/package-lock.json b/website/package-lock.json index 171d3013..040b5e68 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -6,10 +6,10 @@ "": { "name": "bon-website", "dependencies": { - "vue": "^3.4.35" + "vue": "^3.5.12" }, "devDependencies": { - "@mdit/plugin-abbr": "^0.13.0", + "@mdit/plugin-abbr": "^0.13.1", "@types/node": "^20.14.11", "chalk": "^4", "css-select": "^5.1.0", @@ -20,8 +20,8 @@ "medium-zoom": "^1.1.0", "smol-toml": "^1.3.0", "ts-node": "^10.9.2", - "ts-pattern": "^5.3.1", - "vitepress": "^1.3.4" + "ts-pattern": "^5.5.0", + "vitepress": "^1.4.1" } }, "node_modules/@algolia/autocomplete-core": { @@ -159,9 +159,9 @@ } }, "node_modules/@algolia/client-common": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.1.1.tgz", - "integrity": "sha512-jkQNQbGY+XQB3Eln7wqqdUZKBzG8lETcsaUk5gcMc6iIwyN/qW0v0fhpKPH+Kli+BImLxo0CWk12CvVvx2exWA==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.10.2.tgz", + "integrity": "sha512-eE4OaTlb5KZdCehWmDARq2KEmMF7DEeFLjKqFDcZNb56k1DMSsa9zCQRXZMovlf2AXLsx0A/1q+SGAEgzF7G3w==", "dev": true, "peer": true, "engines": { @@ -190,15 +190,16 @@ } }, "node_modules/@algolia/client-search": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.1.1.tgz", - "integrity": "sha512-SFpb3FI/VouGou/vpuS7qeCA5Y/KpV42P6CEA/1MZQtl/xJkl6PVjikb+Q9YadeHi2jtDV/aQ6PyiVDnX4PQcw==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.10.2.tgz", + "integrity": "sha512-rGX8uil2uvPycFgtS9Fzwvh4tgKvfFWG5RIh3E77W42HrO66bykCf9jHqhIIlCxdDJih1PuUqBYZIkIAAoSkww==", "dev": true, "peer": true, "dependencies": { - "@algolia/client-common": "5.1.1", - "@algolia/requester-browser-xhr": "5.1.1", - "@algolia/requester-node-http": "5.1.1" + "@algolia/client-common": "5.10.2", + "@algolia/requester-browser-xhr": "5.10.2", + "@algolia/requester-fetch": "5.10.2", + "@algolia/requester-node-http": "5.10.2" }, "engines": { "node": ">= 14.0.0" @@ -278,13 +279,13 @@ } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.1.1.tgz", - "integrity": "sha512-NXmN1ujJCj5GlJQaMK6DbdiXdcf6nhRef/X40lu9TYi71q9xTo/5RPMI0K2iOp6g07S26BrXFOz6RSV3Ny4LLw==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.10.2.tgz", + "integrity": "sha512-gzlfE/H05ggDiEWNi8WaDSRhpE5X8rD1JqYKPPeM31JRI3TutQIKAk3MSUsa1bHM/Di56r2Gm6L1g3ZlZv2ETA==", "dev": true, "peer": true, "dependencies": { - "@algolia/client-common": "5.1.1" + "@algolia/client-common": "5.10.2" }, "engines": { "node": ">= 14.0.0" @@ -296,14 +297,27 @@ "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", "dev": true }, + "node_modules/@algolia/requester-fetch": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.10.2.tgz", + "integrity": "sha512-Q27ciW9WRdq3pUITVlxpHIwe9QWOe+oPvgs8Z+gsv8vMkwXnLfANvSgeZCyQgx3SqzUPzhel0ozVq7Qoh8xIkg==", + "dev": true, + "peer": true, + "dependencies": { + "@algolia/client-common": "5.10.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/requester-node-http": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.1.1.tgz", - "integrity": "sha512-xwrgnNTIzgxDEx6zuCKSKTPzQLA8fL/WZiVB6fRpIu5agLMjoAi0cWA5YSDbo+2FFxqVgLqKY/Jz6mKmWtY15Q==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.10.2.tgz", + "integrity": "sha512-WMUQ4iFhNnQXC4F1Yj51x8tgIvq5h8jtTLMBs7LbMiW6JhnLHfBVl7IVk6X1fZJO5YcvXW051HN8aFlfAb5QEw==", "dev": true, "peer": true, "dependencies": { - "@algolia/client-common": "5.1.1" + "@algolia/client-common": "5.10.2" }, "engines": { "node": ">= 14.0.0" @@ -321,27 +335,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -351,13 +365,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -376,30 +389,30 @@ } }, "node_modules/@docsearch/css": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.1.tgz", - "integrity": "sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.2.tgz", + "integrity": "sha512-vKNZepO2j7MrYBTZIGXvlUOIR+v9KRf70FApRgovWrj3GTs1EITz/Xb0AOlm1xsQBp16clVZj1SY/qaOJbQtZw==", "dev": true }, "node_modules/@docsearch/js": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.1.tgz", - "integrity": "sha512-erI3RRZurDr1xES5hvYJ3Imp7jtrXj6f1xYIzDzxiS7nNBufYWPbJwrmMqWC5g9y165PmxEmN9pklGCdLi0Iqg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.2.tgz", + "integrity": "sha512-pS4YZF+VzUogYrkblCucQ0Oy2m8Wggk8Kk7lECmZM60hTbaydSIhJTTiCrmoxtBqV8wxORnOqcqqOfbmkkQEcA==", "dev": true, "dependencies": { - "@docsearch/react": "3.6.1", + "@docsearch/react": "3.6.2", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.1.tgz", - "integrity": "sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.2.tgz", + "integrity": "sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==", "dev": true, "dependencies": { "@algolia/autocomplete-core": "1.9.3", "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.1", + "@docsearch/css": "3.6.2", "algoliasearch": "^4.19.1" }, "peerDependencies": { @@ -816,9 +829,9 @@ } }, "node_modules/@mdit/plugin-abbr": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-abbr/-/plugin-abbr-0.13.0.tgz", - "integrity": "sha512-Ap1nevIG+pF98L1T9tOhVsrDSGX/b46l3LR4P9q9rE2cKrNHUn5xXRMsD72t1cktJT6RijbKSSnxA16PteDKxg==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mdit/plugin-abbr/-/plugin-abbr-0.13.1.tgz", + "integrity": "sha512-sAdL1cOMWrZsm5YTM0U7zUwhydN8KtKGtnO7Gdtr73JuFyczP+30ilnpAJ2KNF8w+kaYeg6SMcqfhpQu5mtuZA==", "dev": true, "dependencies": { "@types/markdown-it": "^14.1.2" @@ -868,9 +881,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz", + "integrity": "sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==", "cpu": [ "arm" ], @@ -881,9 +894,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.2.tgz", + "integrity": "sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==", "cpu": [ "arm64" ], @@ -894,9 +907,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.2.tgz", + "integrity": "sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==", "cpu": [ "arm64" ], @@ -907,9 +920,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.2.tgz", + "integrity": "sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==", "cpu": [ "x64" ], @@ -919,10 +932,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.2.tgz", + "integrity": "sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.2.tgz", + "integrity": "sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.2.tgz", + "integrity": "sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==", "cpu": [ "arm" ], @@ -933,9 +972,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.2.tgz", + "integrity": "sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==", "cpu": [ "arm" ], @@ -946,9 +985,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.2.tgz", + "integrity": "sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==", "cpu": [ "arm64" ], @@ -959,9 +998,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.2.tgz", + "integrity": "sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==", "cpu": [ "arm64" ], @@ -972,9 +1011,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.2.tgz", + "integrity": "sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==", "cpu": [ "ppc64" ], @@ -985,9 +1024,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.2.tgz", + "integrity": "sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==", "cpu": [ "riscv64" ], @@ -998,9 +1037,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.2.tgz", + "integrity": "sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==", "cpu": [ "s390x" ], @@ -1011,9 +1050,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.2.tgz", + "integrity": "sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==", "cpu": [ "x64" ], @@ -1024,9 +1063,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.2.tgz", + "integrity": "sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==", "cpu": [ "x64" ], @@ -1037,9 +1076,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.2.tgz", + "integrity": "sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==", "cpu": [ "arm64" ], @@ -1050,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.2.tgz", + "integrity": "sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==", "cpu": [ "ia32" ], @@ -1063,9 +1102,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.2.tgz", + "integrity": "sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==", "cpu": [ "x64" ], @@ -1076,23 +1115,65 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.14.1.tgz", - "integrity": "sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.1.tgz", + "integrity": "sha512-bqAhT/Ri5ixV4oYsvJNH8UJjpjbINWlWyXY6tBTsP4OmD6XnFv43nRJ+lTdxd2rmG5pgam/x+zGR6kLRXrpEKA==", "dev": true, "dependencies": { - "@types/hast": "^3.0.4" + "@shikijs/engine-javascript": "1.22.1", + "@shikijs/engine-oniguruma": "1.22.1", + "@shikijs/types": "1.22.1", + "@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==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.22.1", + "@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==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.22.1", + "@shikijs/vscode-textmate": "^9.3.0" } }, "node_modules/@shikijs/transformers": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.14.1.tgz", - "integrity": "sha512-JJqL8QBVCJh3L61jqqEXgFq1cTycwjcGj7aSmqOEsbxnETM9hRlaB74QuXvY/fVJNjbNt8nvWo0VwAXKvMSLRg==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.22.1.tgz", + "integrity": "sha512-KvG49YFV6gV116sC4L3Sn1Rp6HXsioMKBBG373j088rw849440hm8s2r+/dgjsGLvT4p+QB7newev+5a3ARM6w==", + "dev": true, + "dependencies": { + "shiki": "1.22.1" + } + }, + "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==", "dev": true, "dependencies": { - "shiki": "1.14.1" + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" } }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", + "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "dev": true + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -1130,9 +1211,9 @@ "dev": true }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/hast": { @@ -1160,6 +1241,15 @@ "@types/mdurl": "^2" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", @@ -1187,10 +1277,16 @@ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", "dev": true }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.2.tgz", - "integrity": "sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", + "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -1201,68 +1297,68 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.38.tgz", - "integrity": "sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "dependencies": { - "@babel/parser": "^7.24.7", - "@vue/shared": "3.4.38", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.38.tgz", - "integrity": "sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "dependencies": { - "@vue/compiler-core": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.38.tgz", - "integrity": "sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==", - "dependencies": { - "@babel/parser": "^7.24.7", - "@vue/compiler-core": "3.4.38", - "@vue/compiler-dom": "3.4.38", - "@vue/compiler-ssr": "3.4.38", - "@vue/shared": "3.4.38", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", "estree-walker": "^2.0.2", - "magic-string": "^0.30.10", - "postcss": "^8.4.40", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.38.tgz", - "integrity": "sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", "dependencies": { - "@vue/compiler-dom": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/devtools-api": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.3.8.tgz", - "integrity": "sha512-NURFwmxz4WukFU54IHgyGI2KSejdgHG5JC4xTcWmTWEBIc8aelj9fBy4qsboObGHFp3JIdRxxANO9s2wZA/pVQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.5.4.tgz", + "integrity": "sha512-j9UC/KeYUNZ6AyCJxBROBCbogB5YHW6PZv9VnCNp2ntE4rq426Lfc8WP5B9V+rXBwqWmrgZTGYBa31CBSxdAUg==", "dev": true, "dependencies": { - "@vue/devtools-kit": "^7.3.8" + "@vue/devtools-kit": "^7.5.4" } }, "node_modules/@vue/devtools-kit": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.8.tgz", - "integrity": "sha512-HYy3MQP1nZ6GbE4vrgJ/UB+MvZnhYmEwCa/UafrEpdpwa+jNCkz1ZdUrC5I7LpkH1ShREEV2/pZlAQdBj+ncLQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.5.4.tgz", + "integrity": "sha512-0i7WFgc1B2TL52tstn82zlb9opSA0aIiHfkUYFXtZb8CIpmlFMTkHtgwVl6PMWNBj3LNhYou1YJCLpCYvJYYoA==", "dev": true, "dependencies": { - "@vue/devtools-shared": "^7.3.8", - "birpc": "^0.2.17", + "@vue/devtools-shared": "^7.5.4", + "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", @@ -1271,68 +1367,68 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.8.tgz", - "integrity": "sha512-1NiJbn7Yp47nPDWhFZyEKpB2+5/+7JYv8IQnU0ccMrgslPR2dL7u1DIyI7mLqy4HN1ll36gQy0k8GqBYSFgZJw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.5.4.tgz", + "integrity": "sha512-dwuq4YmwTyLc7eBOqX63s3JB8il7qnKsNgENglSMkUPwiItHkVAYYfPESN1rxSdYkl1RCux1l5TBidYqfUDNAA==", "dev": true, "dependencies": { "rfdc": "^1.4.1" } }, "node_modules/@vue/reactivity": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.38.tgz", - "integrity": "sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", "dependencies": { - "@vue/shared": "3.4.38" + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.38.tgz", - "integrity": "sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.38.tgz", - "integrity": "sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/runtime-core": "3.4.38", - "@vue/shared": "3.4.38", + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.38.tgz", - "integrity": "sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", "dependencies": { - "@vue/compiler-ssr": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { - "vue": "3.4.38" + "vue": "3.5.12" } }, "node_modules/@vue/shared": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.38.tgz", - "integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==" + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==" }, "node_modules/@vueuse/core": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.0.1.tgz", - "integrity": "sha512-YTrekI18WwEyP3h168Fir94G/HNC27wvXJI21Alm0sPOwvhihfkrvHIe+5PNJq+MpgWdRcsjvE/38JaoKrgZhQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.1.0.tgz", + "integrity": "sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==", "dev": true, "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "11.0.1", - "@vueuse/shared": "11.0.1", + "@vueuse/metadata": "11.1.0", + "@vueuse/shared": "11.1.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -1366,13 +1462,13 @@ } }, "node_modules/@vueuse/integrations": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.0.1.tgz", - "integrity": "sha512-V/FQTS/aiV6RTFXOj8cXgqhtNJBvxvbHeLElOUR7N7F3Kr0btS+dkymLB54mFd0Or6uEGpgwwb41cs/q2/rdOg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.1.0.tgz", + "integrity": "sha512-O2ZgrAGPy0qAjpoI2YR3egNgyEqwG85fxfwmA9BshRIGjV4G6yu6CfOPpMHAOoCD+UfsIl7Vb1bXJ6ifrHYDDA==", "dev": true, "dependencies": { - "@vueuse/core": "11.0.1", - "@vueuse/shared": "11.0.1", + "@vueuse/core": "11.1.0", + "@vueuse/shared": "11.1.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -1458,18 +1554,18 @@ } }, "node_modules/@vueuse/metadata": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.0.1.tgz", - "integrity": "sha512-dTFvuHFAjLYOiSd+t9Sk7xUiuL6jbfay/eX+g+jaipXXlwKur2VCqBCZX+jfu+2vROUGcUsdn3fJR9KkpadIOg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.1.0.tgz", + "integrity": "sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.0.1.tgz", - "integrity": "sha512-eAPf5CQB3HR0S76HqrhjBqFYstZfiHWZq8xF9EQmobGBkrhPfErJEhr8aMNQMqd6MkENIx2pblIEfJGlHpClug==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.1.0.tgz", + "integrity": "sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==", "dev": true, "dependencies": { "vue-demi": ">=0.14.10" @@ -1612,9 +1708,9 @@ "dev": true }, "node_modules/birpc": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.17.tgz", - "integrity": "sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==", + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", + "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" @@ -1638,6 +1734,16 @@ "node": ">=8" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1654,6 +1760,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1672,6 +1798,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/copy-anything": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", @@ -1726,6 +1862,28 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1888,9 +2046,9 @@ } }, "node_modules/focus-trap": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", - "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.0.tgz", + "integrity": "sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==", "dev": true, "dependencies": { "tabbable": "^6.2.0" @@ -1951,12 +2109,58 @@ "node": ">=8" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "dev": true }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -2040,9 +2244,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -2059,6 +2263,27 @@ "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", "dev": true }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/medium-zoom": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/medium-zoom/-/medium-zoom-1.1.0.tgz", @@ -2074,6 +2299,95 @@ "node": ">= 8" } }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -2128,6 +2442,18 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oniguruma-to-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", + "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", + "dev": true, + "dependencies": { + "regex": "^4.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -2147,9 +2473,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2164,9 +2490,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -2183,23 +2509,33 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/preact": { - "version": "10.23.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz", - "integrity": "sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==", + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2220,6 +2556,12 @@ } ] }, + "node_modules/regex": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", + "integrity": "sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==", + "dev": true + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2237,12 +2579,12 @@ "dev": true }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.2.tgz", + "integrity": "sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -2252,22 +2594,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@rollup/rollup-android-arm-eabi": "4.24.2", + "@rollup/rollup-android-arm64": "4.24.2", + "@rollup/rollup-darwin-arm64": "4.24.2", + "@rollup/rollup-darwin-x64": "4.24.2", + "@rollup/rollup-freebsd-arm64": "4.24.2", + "@rollup/rollup-freebsd-x64": "4.24.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.2", + "@rollup/rollup-linux-arm-musleabihf": "4.24.2", + "@rollup/rollup-linux-arm64-gnu": "4.24.2", + "@rollup/rollup-linux-arm64-musl": "4.24.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.2", + "@rollup/rollup-linux-riscv64-gnu": "4.24.2", + "@rollup/rollup-linux-s390x-gnu": "4.24.2", + "@rollup/rollup-linux-x64-gnu": "4.24.2", + "@rollup/rollup-linux-x64-musl": "4.24.2", + "@rollup/rollup-win32-arm64-msvc": "4.24.2", + "@rollup/rollup-win32-ia32-msvc": "4.24.2", + "@rollup/rollup-win32-x64-msvc": "4.24.2", "fsevents": "~2.3.2" } }, @@ -2295,19 +2639,23 @@ } }, "node_modules/search-insights": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.0.tgz", - "integrity": "sha512-AskayU3QNsXQzSL6v4LTYST7NNfs2HWyHHB+sdORP9chsytAhro5XRfToAMI/LAVYgNbzowVZTMfBRodgbUHKg==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.2.tgz", + "integrity": "sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==", "dev": true, "peer": true }, "node_modules/shiki": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.14.1.tgz", - "integrity": "sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.1.tgz", + "integrity": "sha512-PbJ6XxrWLMwB2rm3qdjIHNm3zq4SfFnOx0B3rEoi4AN8AUngsdyZ1tRe5slMPtn6jQkbUURLNZPpLR7Do3k78g==", "dev": true, "dependencies": { - "@shikijs/core": "1.14.1", + "@shikijs/core": "1.22.1", + "@shikijs/engine-javascript": "1.22.1", + "@shikijs/engine-oniguruma": "1.22.1", + "@shikijs/types": "1.22.1", + "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4" } }, @@ -2336,13 +2684,23 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/speakingurl": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", @@ -2352,6 +2710,20 @@ "node": ">=0.10.0" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/superjson": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", @@ -2382,14 +2754,6 @@ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2402,6 +2766,16 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -2446,9 +2820,9 @@ } }, "node_modules/ts-pattern": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.3.1.tgz", - "integrity": "sha512-1RUMKa8jYQdNfmnK4jyzBK3/PS/tnjcZ1CW0v1vWDeYe5RBklc/nquw03MEoB66hVBm4BnlCfmOqDVxHyT1DpA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.5.0.tgz", + "integrity": "sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==", "dev": true }, "node_modules/typescript": { @@ -2483,20 +2857,116 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", + "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", "dev": true, "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -2549,27 +3019,28 @@ } }, "node_modules/vitepress": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.3.4.tgz", - "integrity": "sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.4.1.tgz", + "integrity": "sha512-C2rQ7PMlDVqgsaHOa0uJtgGGWaGv74QMaGL62lxKbtFkYtosJB5HAfZ8+pEbfzzvLemYaYwaiQdFLBlexK2sFw==", "dev": true, "dependencies": { - "@docsearch/css": "^3.6.1", - "@docsearch/js": "^3.6.1", - "@shikijs/core": "^1.13.0", - "@shikijs/transformers": "^1.13.0", + "@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", "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^5.1.2", - "@vue/devtools-api": "^7.3.8", - "@vue/shared": "^3.4.38", - "@vueuse/core": "^11.0.0", - "@vueuse/integrations": "^11.0.0", - "focus-trap": "^7.5.4", + "@vitejs/plugin-vue": "^5.1.4", + "@vue/devtools-api": "^7.4.6", + "@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.13.0", - "vite": "^5.4.1", - "vue": "^3.4.38" + "shiki": "^1.22.0", + "vite": "^5.4.8", + "vue": "^3.5.12" }, "bin": { "vitepress": "bin/vitepress.js" @@ -2588,15 +3059,15 @@ } }, "node_modules/vue": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz", - "integrity": "sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", "dependencies": { - "@vue/compiler-dom": "3.4.38", - "@vue/compiler-sfc": "3.4.38", - "@vue/runtime-dom": "3.4.38", - "@vue/server-renderer": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { "typescript": "*" @@ -2615,6 +3086,16 @@ "engines": { "node": ">=6" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/website/package.json b/website/package.json index c2f9fbba..4c762196 100644 --- a/website/package.json +++ b/website/package.json @@ -8,7 +8,7 @@ "preview": "vitepress preview" }, "devDependencies": { - "@mdit/plugin-abbr": "^0.13.0", + "@mdit/plugin-abbr": "^0.13.1", "@types/node": "^20.14.11", "chalk": "^4", "css-select": "^5.1.0", @@ -19,10 +19,10 @@ "medium-zoom": "^1.1.0", "smol-toml": "^1.3.0", "ts-node": "^10.9.2", - "ts-pattern": "^5.3.1", - "vitepress": "^1.3.4" + "ts-pattern": "^5.5.0", + "vitepress": "^1.4.1" }, "dependencies": { - "vue": "^3.4.35" + "vue": "^3.5.12" } } diff --git a/website/v2/config.mts b/website/v2/config.mts new file mode 100644 index 00000000..fe1722a7 --- /dev/null +++ b/website/v2/config.mts @@ -0,0 +1,162 @@ +import { DefaultTheme } from "vitepress"; + +export const sidebars = { + "/v2/guide": [ + { + text: "Guide", + items: [ + { + text: "Overview", + link: "/guide/overview", + }, + { + text: "Optional Members", + link: "/guide/optional-members", + }, + { + text: "Compatibility", + link: "/guide/compatibility", + }, + { + text: "Positional Members", + link: "/guide/positional-members", + }, + { + text: "Inspecting", + link: "/guide/inspecting", + }, + { + text: "Documenting", + link: "/guide/documenting", + }, + { + text: "Limitations", + link: "/guide/limitations", + }, + { + text: "Benchmarks", + link: "/guide/benchmarks", + }, + { + text: "Alternatives", + link: "/guide/alternatives", + }, + { + text: "Troubleshooting", + link: "/guide/troubleshooting", + }, + ], + }, + { + text: "Patterns", + items: [ + { + text: "Conditional Building", + link: "/guide/patterns/conditional-building", + }, + { + text: "Fallible Builders", + link: "/guide/patterns/fallible-builders", + }, + { + text: "Into Conversions In-Depth", + link: "/guide/patterns/into-conversions-in-depth", + }, + { + text: "Shared Configuration", + link: "/guide/patterns/shared-configuration", + }, + ], + }, + { + text: "Internal", + items: [ + { + text: "Contributing", + link: "/guide/internal/contributing", + }, + ], + }, + ], + "/v2/reference": [ + { + text: "Reference", + items: [ + { + text: "#[derive(Builder)] / #[builder]", + link: "/reference/builder", + items: [ + { + text: "Top-level attributes", + link: "/reference/builder#top-level-attributes", + items: [ + { + text: "builder_type", + link: "/reference/builder#builder-type", + }, + { + text: "derive", + link: "/reference/builder#derive", + }, + { + text: "expose_positional_fn", + link: "/reference/builder#expose-positional-fn", + }, + { + text: "finish_fn", + link: "/reference/builder#finish-fn", + }, + { + text: "on", + link: "/reference/builder#on", + }, + { + text: "start_fn", + link: "/reference/builder#start-fn", + }, + ], + }, + { + text: "Member attributes", + link: "/reference/builder#member-attributes", + items: [ + { + text: "default", + link: "/reference/builder#default", + }, + { + text: "finish_fn", + link: "/reference/builder#finish-fn-1", + }, + { + text: "into", + link: "/reference/builder#into", + }, + { + text: "name", + link: "/reference/builder#name", + }, + { + text: "skip", + link: "/reference/builder#skip", + }, + { + text: "start_fn", + link: "/reference/builder#start-fn-1", + }, + ], + }, + ], + }, + { + text: "#[bon]", + link: "/reference/bon", + }, + { + text: "Other items on docs.rs", + link: "https://docs.rs/bon/latest/bon/", + }, + ], + }, + ], +} satisfies Record; diff --git a/website/v2/guide/alternatives.md b/website/v2/guide/alternatives.md new file mode 100644 index 00000000..e665b0e3 --- /dev/null +++ b/website/v2/guide/alternatives.md @@ -0,0 +1,105 @@ +--- +aside: false +--- + +# Alternatives + +There are several other existing alternative crates that generate builders. `bon` was designed with many lessons learned from them. Here is a table that compares the builder crates with some additional explanations below. + + + +Feature | `bon` | [`buildstructor`] | [`typed-builder`] | [`derive_builder`] +---------------------------------------------------------|--------------------------------------------------------------|---------------------------------|---------------------------------------------------------------------|------------------- +Builder for structs | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: +Builder for free functions | :white_check_mark: | | | +Builder for associated methods | :white_check_mark: | :white_check_mark: | | +Panic safe | :white_check_mark: | :white_check_mark: | :white_check_mark: | `build()` returns a `Result` +Member of `Option` type is optional by default | :white_check_mark: | :white_check_mark: | opt-in `#[builder(default)]` | opt-in `#[builder(default)]` +Making required member optional is compatible by default | :white_check_mark: | :white_check_mark: | opt-in `#[builder(setter(strip_option))]` | opt-in `#[builder(setter(strip_option))]` +Generates `T::builder()` method | :white_check_mark: | :white_check_mark: | :white_check_mark: | only `Builder::default()` +`Into` conversion in setters | opt-in ([members subset][bon-on], [single member][bon-into]) | [implicit (automatic)][bs-into] | opt-in (all members + out-out, single member) | [opt-in (all members, single member)][db-into] + `impl Trait` supported for functions | :white_check_mark: | | | +Anonymous lifetimes supported for functions | :white_check_mark: | | | +`Self` mentions in functions/structs are supported | :white_check_mark: | | | +Positional function is hidden by default | :white_check_mark: | | | +Special setter methods for collections | [(see below)][r1] | :white_check_mark: | | :white_check_mark: +Custom methods can be added to the builder type | | | :white_check_mark: ([mutators]) | :white_check_mark: +Builder may be configured to use &self/&mut self | | | | :white_check_mark: + +## Function builder fallback paradigm + +The builder crates `typed-builder` and `derive_builder` have a bunch of attributes that allow users to insert custom behaviour into the building process of the struct. However, `bon` and `buildstructor` avoid the complexity of additional config attributes for advanced use cases by proposing the user fallback to defining a custom function with the `#[builder]` attached to it where it's possible to do anything you want. + +However, `bon` still provides some simple attributes for common use cases to configure the behaviour without falling back to a more verbose syntax. + +## 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: + +```rust +#[derive(buildstructor::Builder)] +struct User { + friends: Vec +} + +fn main() { + User::builder() + .friend("Foo") + .friend("Bar") + .friend("`String` value is also accepted".to_owned()) + .build(); +} +``` + +::: tip + +Why is there an explicit `main()` function in this code snippet 🤔? It's a long story explained in a [blog post](/blog/the-weird-of-function-local-types-in-rust) (feel free to skip). + +::: + +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`. + +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`. + +Alternatively, `bon` provides a separate solution. `bon` exposes the following macros that provide convenient syntax to create collections. + +`Vec` | `[T; N]` | `*Map` | `*Set` +---------------------|----------------------|----------------------|--------------------- +[`bon::vec![]`][vec] | [`bon::arr![]`][arr] | [`bon::map!{}`][map] | [`bon::set![]`][set] + +These macros share a common feature that every element of the collection is converted with `Into` to shorten the syntax if you, for example, need to initialize a `Vec` with items of type `&str`. Use these macros only if you need this behaviour, or ignore them if you want to be explicit in code and avoid implicit `Into` conversions. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct User { + friends: Vec +} + +User::builder() + .friends(bon::vec![ + "Foo", + "Bar", + "`String` value is also accepted".to_owned(), + ]) + .build(); +``` + +Another difference is that fields of collection types are considered required by default, which isn't the case in `buildstructor`. + +[`buildstructor`]: https://docs.rs/buildstructor/latest/buildstructor/ +[`typed-builder`]: https://docs.rs/typed-builder/latest/typed_builder/ +[`derive_builder`]: https://docs.rs/derive_builder/latest/derive_builder/ +[vec]: https://docs.rs/bon/latest/bon/macro.vec.html +[arr]: https://docs.rs/bon/latest/bon/macro.arr.html +[map]: https://docs.rs/bon/latest/bon/macro.map.html +[set]: https://docs.rs/bon/latest/bon/macro.set.html +[mutators]: https://docs.rs/typed-builder/latest/typed_builder/derive.TypedBuilder.html#mutators +[bon-on]: ../reference/builder#on +[bon-into]: ../reference/builder#into +[bs-into]: https://docs.rs/buildstructor/latest/buildstructor/#into-field +[db-into]: https://docs.rs/derive_builder/latest/derive_builder/#generic-setters +[r1]: #special-setter-methods-for-collections diff --git a/website/v2/guide/benchmarks.md b/website/v2/guide/benchmarks.md new file mode 100644 index 00000000..b90c5f4d --- /dev/null +++ b/website/v2/guide/benchmarks.md @@ -0,0 +1,49 @@ +# Benchmarks + +`#[builder]` generates code that is easily optimizable by the compiler. This has been tested by the benchmarks below. The benchmarks compare regular positional function call syntax and builder syntax for functions annotated with `#[builder]`. + +In many cases `rustc` generates the same assembly code for the builder syntax as it would for a regular function call. Even when the generated assembly differs, the performance differences are negligible. + +::: tip TIP + +Don't take these microbenchmarks for granted. Do your own performance measurements in your application in real conditions. Feel free to [open an issue](https://github.com/elastio/bon/issues) if you find performance problems in `bon`. + +::: + +## Wallclock statistics + +| Benchmark | Description | Assembly output | Run time +| -- | -- | -- | -- +| `args_3` | 3 args of primitive types | [Equal](https://godbolt.org/z/YbTc4xGGY) | regular: `6.6536ns`
builder: `6.6494ns` +| `args_5` | 5 args of primitive types | [Equal](https://godbolt.org/z/TM3E7M6b3) | regular: `7.9592ns`
builder: `7.9731ns` +| `args_10` | 10 args of primitive types | [Ordering diff](https://godbolt.org/z/1d1fa38co) | regular: `18.082ns`
builder: `18.217ns` +| `args_10_structs` | 10 args of primitive types and structs | [Equal](https://godbolt.org/z/d6nn16E8q) | regular: `9.2492ns`
builder: `9.2325ns` +| `args_10_alloc` | 10 args of primitive and heap-allocated types | [Instructions diff](https://godbolt.org/z/fEMvnWvbc) | regular: `86.090ns`
builder: `86.790ns` +| `args_20` | 20 args of primitive types | [Ordering diff](https://godbolt.org/z/3czM3h68s) | regular: `36.121ns`
builder: `36.298ns` + +## High-precision statistics + +| Benchmark | Instructions count | L1 accesses | L2 accesses | RAM accesses +| -- | -- | -- | -- | -- +| `args_3` | regular: `108`
builder: `108` | regular: `138`
builder: `138` | regular: `2`
builder: `2` | regular: `4`
builder: `4` +| `args_5` | regular: `126`
builder: `126` | regular: `161`
builder: `161` | regular: `2`
builder: `2` | regular: `10`
builder: `10` +| `args_10` | regular: `281`
builder: `281` | regular: `381`
builder: `380` | regular: `2`
builder: `2` | regular: `19`
builder: `20` +| `args_10_structs` | regular: `75`
builder: `75` | regular: `106`
builder: `106` | regular: `4`
builder: `4` | regular: `12`
builder: `12` +| `args_10_alloc` | regular: `2028`
builder: `2027`| regular: `2824`
builder: `2824` | regular: `3`
builder: `2` | regular: `36`
builder: `36` +| `args_20` | regular: `556`
builder: `556` | regular: `767`
builder: `767` | regular: `4`
builder: `4` | regular: `36`
builder: `36` + +## Conditions + +The code was compiled with `opt-level = 3` and `debug = 0`. + +### 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](https://github.com/elastio/bon/tree/master/benchmarks). diff --git a/website/v2/guide/compatibility.md b/website/v2/guide/compatibility.md new file mode 100644 index 00000000..68daffbe --- /dev/null +++ b/website/v2/guide/compatibility.md @@ -0,0 +1,207 @@ +# Compatibility + +## Making a required member optional + +It's totally backwards compatible to make a required member optional by changing the type from `T` to `Option` or by adding [`#[builder(default)]`](../reference/builder.md#default) to it. + +This is because both required and optional members have a setter that accepts `T` (not wrapped in an `Option`). The only change to the public API when making the required member optional is that a `maybe_`-prefixed setter is added to the builder. That new method accepts an `Option`. + +**Example:** + +Suppose you have a function with a required argument, and there is existing code that sets that required argument using builder syntax. + +```rust +use bon::builder; + +#[builder] +fn get_page(password: &str) -> String { + format!("Secret knowledge") +} + +// Existing code that uses the builder API +let page = get_page() + .password("I know the password!") + .call(); +assert_eq!(page, "Secret knowledge"); +``` + +Then you change this function to take an `Option`. This is totally fine, the old code that sets that parameter to `T` still compiles: + +```rust +use bon::builder; + +#[builder] +fn get_page(password: Option<&str>) -> String { // [!code highlight] + format!("Secret knowledge") +} + +// Existing code that uses the builder API (unchanged, still compiles) +let page = get_page() + .password("I know the password!") + .call(); +assert_eq!(page, "Secret knowledge"); + +// Now this code can also pass `Option`, including `None` to the function +// using the new `maybe_password` method. +get_page() + .maybe_password(Some("password")) + .call(); +``` + +## Switching between `Option` and `#[builder(default)]` + +Switching between `Option` for the member type and `#[builder(default)]` on `T` is fully compatible. Nothing changes in the builder API when this happens. + +**Example:** + +```rust +use bon::builder; + +#[builder] +fn example(filter: Option) {} + +example().maybe_filter(Some("filter".to_owned())).call(); +``` + +This code can be changed to use `#[builder(default)]` and the call site will still compile: + +```rust ignore +use bon::builder; + +#[builder] +fn example( + #[builder(default)] // [!code ++] + filter: Option // [!code --] + filter: String // [!code ++] +) {} + +example.maybe_filter(Some("filter".to_owned())).call(); +``` + +## Marking member as unused with a leading `_` + +You may add `_` prefix to the member name to mark it as unused for the time being. The builder API won't change if you do that. Leading underscores are stripped from the setter names automatically. + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +struct Example { + _name: String +} + +Example::builder() + .name("The setter is still called `name`".to_owned()) + .build(); +``` + +```rust [Free function] +use bon::builder; + +#[builder] +fn example( + _name: String +) {} + +example() + .name("The setter is still called `name`".to_owned()) + .call(); +``` + +```rust [Associated method] +use bon::{bon, builder}; + +struct Example; + +#[bon] +impl Example { + #[builder] + fn example(_name: String) {} +} + +Example::example() + .name("The setter is still called `name`".to_owned()) + .call(); +``` + +::: + +## Switching between `#[derive(Builder)]` and `#[builder]` on the `new()` method + +`#[derive(Builder)]` on a struct generates builder API that is fully compatible with placing `#[builder]` on the `new()` method with the signature similar to struct's fields. + +This means, for example, it's preferable to place the `#[derive(Builder)]` attribute on top of your struct in most cases because it's convenient. However, if you need to have some custom logic during the construction of your type, you may simply create a `new()` method annotated with `#[builder]` where you can do anything you want to create an instance of your type. + +To keep your struct's public API compatible with the time when `#[derive(Builder)]` was on the struct directly, the `new()` method must accept the same parameters as there were fields in the struct. + +**Example:** + +```rust ignore +use bon::Builder; // [!code --] +use bon::bon; // [!code ++] + +// Previously we used `#[derive(Builder)]` on the struct +#[derive(Builder)] // [!code --] +struct User { + // But then we decided to change the internal representation + // of the `id` field to use `String` instead of `u32` + id: u32, // [!code --] + id: String, // [!code ++] + name: String, +} + +// To preserve compatibility we need to define a `new()` method with `#[builder]` +// that still accepts `u32` for the `id` member. +#[bon] // [!code ++] +impl User { // [!code ++] + #[builder] // [!code ++] + fn new(id: u32, name: String) -> Self { // [!code ++] + Self { // [!code ++] + id: format!("u-{id}"), // [!code ++] + name, // [!code ++] + } // [!code ++] + } // [!code ++] +} // [!code ++] + +// The caller's code didn't change. It still uses `u32` for the `id` member. +let user = User::builder() + // `id` is still accepted as a `u32` here + .id(1) + .name("Bon".to_owned()) + .build(); +``` + +## Adding `#[builder]` to existing code + +If your existing code defines functions with positional parameters in its public API that you'd like to change to use builder syntax, but you want to keep the old code compatible with the positional functions API, then you may use `#[builder(expose_positional_fn)]` attribute to keep both syntaxes available. + +See [this attribute's docs](../reference/builder#expose-positional-fn) for details. + +**Example:** + +```rust ignore +use bon::builder; + +// The previous name of the positional function needs to be specified +// as the value for `expose_positional_fn`. +#[builder(expose_positional_fn = example)] // [!code ++] +fn example(x: u32, y: u32) {} // [!code --] +fn example_builder(x: u32, y: u32) {} // [!code ++] + +// The positional function is now available under the specified (old) name +example(1, 2); + +// The builder syntax is also available under the new function name +example_builder() + .x(1) + .y(2) + .call(); +``` + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/documenting.md b/website/v2/guide/documenting.md new file mode 100644 index 00000000..aec8207e --- /dev/null +++ b/website/v2/guide/documenting.md @@ -0,0 +1,38 @@ +# Documenting + +In regular Rust, it's not possible to place doc comments on function arguments. But with `#[builder]` it is. Documentation written on the arguments will be placed on the generated setter methods. + +**Example:** + +```rust +use bon::builder; + +/// Function that returns a greeting special-tailored for a given person +#[builder] +fn greet( + /// Name of the person to greet. + /// + /// **Example:** + /// ``` + /// greet().name("John"); + /// ``` + name: &str, + + /// Age expressed in full years passed since the birth date. + age: u32 +) -> String { + format!("Hello {name} with age {age}!") +} +``` + +::: details How does this work? 🤔 + +This works because Rust compiler checks for invalid placement of `#[doc = ...]` attributes only after the macro expansion stage. `#[builder]` makes sure to remove the docs from the function's arguments in the expanded code, and instead moves them to the docs on setter methods. + +::: + +When `#[derive(Builder)]` is placed on top of a struct, then documentation on the struct fields will be copied to the docs on the setter methods. + +## Positional members + +Documentation comments are allowed on [positional members](./positional-members). However, since there are no separate setter methods generated for them, the docs on these members will not be copied anywhere, and thus they won't appear in `rustdoc`. Instead, it's recommended to write documentation for these members on the top level of the struct or function. diff --git a/website/v2/guide/inspecting.md b/website/v2/guide/inspecting.md new file mode 100644 index 00000000..063d7ab0 --- /dev/null +++ b/website/v2/guide/inspecting.md @@ -0,0 +1,33 @@ +# Inspecting + +If you want to inspect the values set in the builder for debugging purposes you can leverage the [`#[builder(derive(...))]`](../reference/builder#derive) attribute to derive the [`Debug`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) trait for your builder. + +**Example:** + +```rust +use bon::builder; + +#[builder(derive(Debug))] // [!code highlight] +fn example( + name: String, + is_admin: bool, + level: Option, +) {} + +let builder = example().name("Bon".to_owned()); + +// This will output the current state of the builder to `stderr` +dbg!(&builder); + +// You can also format the debug output to `String`: +assert_eq!( + format!("{builder:?}"), + // Only the fields that were set will be output + r#"ExampleBuilder { name: "Bon" }"# +); + +// Finish building +builder.is_admin(true).call(); +``` + +You can also derive the [`Clone`](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) trait for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../reference/builder#derive). diff --git a/website/v2/guide/internal/contributing.md b/website/v2/guide/internal/contributing.md new file mode 100644 index 00000000..da04be5b --- /dev/null +++ b/website/v2/guide/internal/contributing.md @@ -0,0 +1,46 @@ +--- +outline: deep +--- +# Contributing + +**Any contributions are welcome!** + +Before you start working on a code contribution make sure it will be accepted. If your change introduces new behavior like a new macro attribute or some new syntax then you should probably [open an issue](https://github.com/elastio/bon/issues) that describes your change first. We'll let you know if we'll accept a pull request for the change suggested in that issue. + +However, even though desirable, creating an issue before making a pull request is optional. Just make sure your change is really straightforward and doesn't require any discussions. + +## Development + +This repository is a regular [`cargo` workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html). Just fork it and do the usual `cargo` business. + +## Testing + +Test your changes with `cargo test`. You may add new tests to the `bon/tests/integration` folder. + +If you want to validate that the macro generates a good compile error or warning, then extend the [`trybuild`](https://docs.rs/trybuild/latest/trybuild/) tests in `bon/tests/ui/compile_fail`. There are `errors.rs` and `warnings.rs` files where we have all the erroneous code examples. + +## Docs + +Make sure the documentation reflects your change. Add or update the docs in the following places: + +- Doc comments on `pub` items of the crate. +- Docs in the `website/docs` folder. Markdown files that live in this folder automatically turn into HTML pages of this website during release. + +## Pull request + +Once you are ready, commit your code changes and create a pull request into the `master` branch. + +We use [squash-and-merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits) to land pull requests into master. We don't enforce the format of the commit messages and PR descriptions. + +## Release + +We'll release your change soon after it lands in `master`, or later as part of a bigger release. It depends on the kind of change you made and the state of the code in `master` at that specific moment in time. Anyway, we'll tell you in PR comments when we plan to release it. + +## License + +Licensed under either of [Apache License, Version +2.0](https://github.com/elastio/bon/blob/master/LICENSE-APACHE) or [MIT license](https://github.com/elastio/bon/blob/master/LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/website/v2/guide/limitations.md b/website/v2/guide/limitations.md new file mode 100644 index 00000000..bb83ca2c --- /dev/null +++ b/website/v2/guide/limitations.md @@ -0,0 +1,149 @@ +# Limitations + +Every tool has its constraints, and `bon` is not an exception. The limitations described below shouldn't generally occur in your day-to-day code, and if they do, there are ways to work around them. If you feel that some of the limitations are unacceptable, feel free to [open an issue] to ask for some of them to be relaxed. + +## Intra-doc links to `Self` on setter methods + +Documentation placed on the original function arguments or struct fields is copied verbatim to the documentation on the generated setter methods of the builder struct. The shortcoming of this approach is that references to `Self` break when moved into the `impl` block of the generated builder struct. + +`bon` checks for the presence of ``[`Self`]`` and `[Self]` in the documentation on the function arguments and struct fields. If there are any, then a compile error suggesting to use the full type name will be generated. + +The following example doesn't compile with the reference to ``[`Self`]``. The fix is to replace that reference with the actual name of the struct ``[`Foo`]``. + +```rust compile_fail +use bon::bon; + +struct Foo; + +#[bon] +impl Foo { + #[builder] + fn promote( + /// Promotes [`Self`] to the level specified in this argument // [!code --] + /// Promotes [`Foo`] to the level specified in this argument // [!code ++] + new_level: String + ) {} +} +``` + +## Implicit generic lifetimes + +Rust allows omitting generic lifetime parameters of a type in function parameters. + +**Example:** + +```rust compile_fail +use bon::builder; + +struct User<'a> { + name: &'a str +} + +#[builder] +fn example(value: User) {} // [!code error] +``` + +In this example, the type `User` is referenced in the function argument without lifetime parameters. This breaks the logic of the `#[builder]` macro. It must know **all** the lifetime parameters involved in the function signature to properly generate lifetime parameters for the builder struct. + +Unfortunately, macros in Rust don't have access to semantic information. All that macros see is just a sequence of syntax tokens. All that the `#[builder]` macro sees in the example above is just this: + +```rust ignore +fn example(value: User) {} +``` + +This means the `#[builder]` macro thinks as if there are no lifetime parameters in the `User` type, and thus it generates the code that doesn't compile. + +To fix this, we need to make it clear to the `#[builder]` macro that `User` expects some lifetime parameters. This can be done like this: + +```rust compile_fail +#[builder] +fn example(value: User) {} // [!code --] +fn example(value: User<'_>) {} // [!code ++] +``` + +If you want to make sure your code doesn't accidentally omit a generic lifetime parameter you may enable the `rustc` lint called [`elided_lifetimes_in_paths`](https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html). This lint is `allow` by default, you can enable it in your `Cargo.toml` like this: + +```toml +[package.lints.rust] +elided_lifetimes_in_paths = "warn" +``` + +## Destructuring patterns in function parameters + +When `#[builder]` is placed on a function (including associated methods), then the parameters of that function must be simple identifiers. Destructuring patterns aren't supported because it's not obvious what identifier to assign to the member in this case. This identifier will appear in `#[builder(default = ...)]` expressions, for example. + +Instead, if you need to destructure your function parameter, just do that inside of the function's body. + +**Example:** + +```rust +use bon::builder; + +#[builder] +fn example(point: (u32, u32)) { + let (x, y) = point; +} + +example() + .point((1, 2)) + .call(); +``` + +## Formatting of attributes on function arguments + +At the time of this writing, `rustfmt` does a fairly bad job of formatting attributes placed on function arguments. Here is an example of `rustfmt`-formatted code that uses `#[bon::builder]`: + +```rust +#[bon::builder] +fn example( + #[builder(default = 1)] foo: u32, + bar: u32, + fizz: u32, +) { +} +``` + +The attribute on the function's parameter was formatted on the same line with the parameter itself, even though the signature of the function already takes up multiple lines. It is harder to read the signature this way because the names of function parameters aren't aligned. + +As a workaround, you can place a dummy line comment right after the attribute to prevent `rustfmt` from placing the attribute on the same line with the function parameter: + +**Example:** + +```rust +#[bon::builder] +fn example( + #[builder(default = 1)] // // [!code highlight] + foo: u32, + bar: u32, + fizz: u32, +) { +} +``` + +Another workaround for this is to write a doc comment on top of the function argument: + +```rust +#[bon::builder] +fn example( + /// Doc comment fixes formatting // [!code highlight] + #[builder(default = 1)] + foo: u32, + bar: u32, + fizz: u32, +) { +} +``` + +Here is [the related issue](https://github.com/rust-lang/rustfmt/issues/6276) in `rustfmt` about this problem. + +## `const` functions + +It's possible to place `#[builder]` on top of a `const fn`, but the generated builder methods won't be marked `const`. They use the non-const method `Into::into` to transition between type states. Except for that, the generated code should be `const`-compatible. + +If you have a strong use case that requires full support for `const`, feel free to [open an issue]. We'll figure something out for sure 🐱. + +[open an issue]: https://github.com/elastio/bon/issues + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/optional-members.md b/website/v2/guide/optional-members.md new file mode 100644 index 00000000..814512c0 --- /dev/null +++ b/website/v2/guide/optional-members.md @@ -0,0 +1,111 @@ +# Optional Members + +## `Option` + +Setters generated for members of `Option` type are optional to call. If they aren't invoked, then `None` will be used as the default. + +**Example:** + +```rust +use bon::builder; + +#[builder] +fn example(level: Option) {} + +// We can call it without specifying the `level` +example().call(); +``` + +The generated builder has two setters for each optional member. One setter accepts `T` and the other accepts `Option`. The following setters will be generated in the example above (simplified): + +```rust ignore +impl ExampleBuilder { + // Accepts the underlying value. Wraps it in `Some()` internally + fn level(value: u32) -> NextBuilderState { /* */ } + + // Accepts the `Option` directly. + fn maybe_level(value: Option) -> NextBuilderState { /* */ } +} +``` + +::: tip + +Thanks to this design, changing the member from required to optional [preserves compatibility](./compatibility#making-a-required-member-optional). + +::: + +--- + +If you need to pass a simple literal value, then the syntax is very short + +```rust ignore +example().level(42).call(); +``` + +If you already have an `Option` variable somewhere or you need to dynamically decide if the value should be `Some` or `None`, then you can use the `maybe_` variant of the setter. + +```rust ignore +let value = if some_condition { + Some(42) +} else { + None +}; + +example().maybe_level(value).call(); +``` + +## `#[builder(default)]` + +To make a member of non-`Option` type optional you may use the attribute [`#[builder(default)]`](../reference/builder#default). This attribute uses the [`Default`](https://doc.rust-lang.org/stable/std/default/trait.Default.html) trait or the provided expression to assign the default value for the member. + +::: tip + +Switching between `#[builder(default)]` and `Option` is [compatible](./compatibility#switching-between-option-t-and-builder-default). + +::: + +**Example:** + +```rust +use bon::builder; + +#[builder] +fn example( + // This uses the `Default` trait // [!code highlight] + #[builder(default)] // [!code highlight] + a: u32, + + // This uses the given custom default value // [!code highlight] + #[builder(default = 4)] // [!code highlight] + b: u32, +) -> u32 { + a + b +} + +// Here, the default values will be used `a = 0` and `b = 4` // [!code highlight] +let result = example().call(); +assert_eq!(result, 4); + +// The same couple of setters `{member}(T)` and `maybe_{member}(Option)` // [!code highlight] +// are generated just like it works with members of `Option` type // [!code highlight] +let result = example() + .a(3) + .b(5) + .call(); +assert_eq!(result, 8); + +let result = example() + .maybe_a(Some(3)) + .maybe_b(Some(5)) + .call(); +assert_eq!(result, 8); +``` + +## Conditional building + +Now that you know how optional members work you can check out the ["Conditional building" patterns](./patterns/conditional-building) or continue studying other features of `bon` by following the "Next page" link at the bottom. + + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/overview.md b/website/v2/guide/overview.md new file mode 100644 index 00000000..a86ddfa7 --- /dev/null +++ b/website/v2/guide/overview.md @@ -0,0 +1,309 @@ + + +# Overview + +`bon` is a Rust crate for generating compile-time-checked builders for functions and structs. It also provides idiomatic partial application with optional and named parameters for functions and methods. + +If you are new to the concept of builders or named function arguments, and you don't know what problems they may solve for you, then check out the motivational [blog post](/blog/how-to-do-named-function-arguments-in-rust). + +## Installation + +Add this to your `Cargo.toml` to use this crate: + +```toml-vue +[dependencies] +bon = "{{ versionWildcard }}" +``` + +You can opt out of `std` and `alloc` cargo features with `default-features = false` for `no_std` environments. + + +## Builder for a function + +`bon` can turn a function with positional parameters into a function with "named" parameters via a builder. It's as easy as placing the `#[builder]` macro on top of it. + +**Example:** + +```rust +use bon::builder; + +#[builder] // [!code highlight] +fn greet(name: &str, age: u32) -> String { + format!("Hello {name} with age {age}!") +} + +let greeting = greet() + .name("Bon") + .age(24) + .call(); + +assert_eq!(greeting, "Hello Bon with age 24!"); +``` + +::: tip + +Many things are customizable with additional attributes. [`#[builder]` macro reference](../reference/builder) describes all of them. + +::: + +Any syntax for functions is supported including `async`, fallible, generic functions, `impl Trait`, etc. If you find an edge case where `bon` doesn't work, please [create an issue on GitHub](https://github.com/elastio/bon/issues). + +## Builder for an associated method + +You can also generate a builder for associated methods. For this to work you need to add a `#[bon]` macro on top of the `impl` block additionally. + +**Example:** + +```rust +use bon::bon; + +struct Counter { + val: u32, +} + +#[bon] // <- this macro is required on the impl block // [!code highlight] +impl Counter { + #[builder] // [!code highlight] + fn new(initial: Option) -> Self { + Self { + val: initial.unwrap_or_default(), + } + } + + #[builder] // [!code highlight] + fn increment(&mut self, diff: u32) { + self.val += diff; + } +} + +let mut counter = Counter::builder() + .initial(3) + .build(); + +counter + .increment() + .diff(3) + .call(); + +assert_eq!(counter.val, 6); +``` + +::: details Why is that `#[bon]` macro on top of the `impl` block required? 🤔 (feel free to skip) + +There are a couple of technical reasons. + +First, it's the lack of surrounding context given to a proc macro in Rust. A proc macro sees only the syntax it is placed on top of. For example, the `#[builder]` macro inside of the `impl` block can't see the `impl Counter` part of the impl block above it. However, it needs that information to tell the actual type of `Self`. + +Second, the `#[builder]` proc macro generates new items such as the builder struct type definition, which it needs to output **adjacently** to the `impl` block itself. However, proc macros in Rust can only modify the part of the syntax they are placed on and generate new items on the same level of nesting. The `#[builder]` macro inside of the `impl` block can't just break out of it. + +::: + +::: details Why does it compile without an import of `bon::builder`? 🤔 (feel free to skip) + +This is because there is no separate `#[builder]` proc macro running in this case. Only the `#[bon]` macro handles code generation, it's an active attribute, while `#[builder]` is a dumb inert data attribute (see [the Rust Reference](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes) for details about active and inert attributes). + +It wouldn't harm if `bon::builder` was imported. It won't shadow the inert `#[builder]` attribute, but the compiler will report that the import of that macro is unused. + +::: + + +To follow the usual Rust builder naming conventions `bon` treats the method named `new` inside of the impl block specially. It generates functions with a bit of different names. + +If `#[builder]` is placed on the method called `new`, then the generated functions are called: + +| Start function | Finish function +|---------------------------|--------------------- +| `builder() -> {T}Builder` | `build(self) -> T` + +For any other methods not called `new` and for any free function the naming is a bit different: + +| Start function | Finish function +|-----------------------------------------------|--------------------- +| `{fn_name}() -> {T?}{PascalCaseFnName}Builder` | `call(self) -> T` + +## Builder for a struct + +`bon` supports the classic pattern of annotating a struct to generate a builder with the `#[derive(Builder)]` syntax. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct User { + id: u32, + name: String, +} + +let user = User::builder() + .id(1) + .name("Bon".to_owned()) + .build(); + +assert_eq!(user.id, 1); +assert_eq!(user.name, "Bon"); +``` + +::: tip + +`#[derive(Builder)]` on a struct generates builder API that is fully compatible with placing `#[builder]` attribute on the `new()` method with a signature similar to the struct's fields. + +See [compatibility](./compatibility#switching-between-derive-builder-and-builder-on-the-new-method) page for details. +::: + +In general, both `#[derive(Builder)]` on structs and `#[builder]` on functions/methods have almost the same API. We'll use both of them throughout the documentation to provide examples. If the example shows only the usage of one syntax (e.g. `#[builder]`), it's very likely that the other syntax (e.g. `#[derive(Builder)]`) works similarly unless explicitly stated otherwise. + +## No panics possible + +The builders generated by `#[builder]` and `#[derive(Builder)]` use the typestate pattern to make sure all required parameters are filled, and the same setters aren't called repeatedly to prevent unintentional overwrites and typos. If something is wrong, a compile error will be created. There are no potential panics and `unwrap()` calls inside of the builder. + +## `Option` values are optional + +If your function argument or struct field (or member for short) is of type `Option`, then the generated builder will not enforce setting a value for this member, defaulting to `None`. + +It also generates two setters: one accepts `T` and the other accepts `Option`. The first avoids wrapping values with `Some()` on the call site. The second allows passing the `Option` value directly. + +```rust +use bon::Builder; + +#[derive(Builder)] +struct Projection { + x: Option, + y: Option, + + // Use an annotation for members of non-`Option` type + #[builder(default)] + z: u32, +} + +// Both `x` and `y` will be set to `None`, `z` will be set to `0` +Projection::builder().build(); + +Projection::builder() + // Pass the value without wrapping it with `Some()` + .x(10) + // Or use a `maybe_`-prefixed setter that accepts `Option` + .maybe_y(Some(20)) + // The APIs generated for `#[builder(default)]` and `Option` are equivalent. + // `z` will be set to `0` when `build()` is called. + .maybe_z(None) + .build(); +``` + +See [optional members](./optional-members) page for details. + +## `Into` conversions + +If you have members of type `String`, or `PathBuf`, and you need to set them to a hard-coded string literal, then you have to write `.to_owned()` or `.to_string()` or `.into()`. + +**Example:** + +```rust +use bon::Builder; +use std::path::PathBuf; + +#[derive(Builder)] // [!code focus] +struct Project { // [!code focus] + name: String, // [!code focus] + description: String, // [!code focus] + path: PathBuf, // [!code focus] +} // [!code focus] + +Project::builder() + .name("Bon".to_owned()) // [!code focus] + .description("Awesome crate 🐱".to_string()) // [!code focus] + .path("/path/to/bon".into()) // [!code focus] + .build(); +``` + +However, you can ask `bon` to generate setters that accept `impl Into` to remove the need for manual conversion. + +This can be configured with [`#[builder(into)]`](../reference//builder#into) for a single member or with [`#[builder(on({type}, into))]`](../reference/builder#on) for many members at once. + +```rust +use bon::Builder; +use std::path::PathBuf; + +// All setters for members of type `String` will accept `impl Into` // [!code highlight] +#[derive(Builder)] // [!code highlight] +#[builder(on(String, into))] // [!code highlight] +struct Project { + name: String, + description: String, + + // The setter only for this member will accept `impl Into` // [!code highlight] + #[builder(into)] // [!code highlight] + path: PathBuf, +} + +Project::builder() + // &str is converted to `String` internally // [!code highlight] + .name("Bon") + .description("Awesome crate 🐱") + // `&str` is converted to `PathBuf` internally // [!code highlight] + .path("/path/to/your/heart") + .build(); +``` + +See the ["Into Conversions In-Depth"](./patterns/into-conversions-in-depth) page for more details and important caveats (!). + +## What's next? + +::: tip + +If you like the idea of this crate and want to say "thank you" or "keep up doing this" consider giving us a [star ⭐ on Github](https://github.com/elastio/bon). Any support and contribution are appreciated 🐱! + +::: + +This is just part of what's available in `bon`. You may consider reading the rest of the `Guide` section to harness the full power of `bon` and understand the decisions it makes. Just click on the "Next page" link at the bottom. + +However, feel free to skip the docs and just use the `#[builder]` and `#[derive(Builder)]` in your code. They are designed to be intuitive, so they'll probably do the thing you want them to do already. + +If you can't figure something out, consult the docs and maybe use that search `🔍 Search` thing at the top to navigate. You may also create an issue or a discussion in the [Github repository](https://github.com/elastio/bon) for help, or write us a message in [Discord](https://discord.gg/QcBYSamw4c) (see below). + +## Socials + + + + + + + + + + + + +
+
+ + Discord +
+
Here you can leave feedback, ask questions, report bugs, or just write "thank you".
+
+ + X (Twitter) +
+
Profile of the maintainer. There are only posts about bon and Rust in general here.
+ + +## Acknowledgments + +This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them. + +See [alternatives](./alternatives) for comparison. + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/patterns/conditional-building.md b/website/v2/guide/patterns/conditional-building.md new file mode 100644 index 00000000..ea8f0cea --- /dev/null +++ b/website/v2/guide/patterns/conditional-building.md @@ -0,0 +1,144 @@ +# Conditional Building + +On this page, we'll review a case when you have multiple branches in your code that need to set different values for different builder members. Since the builders generated by `bon` use the type-state pattern and the setters consume `self`, it is a bit more complicated for conditional code to use them. But, not until you know the patterns described below 🐱. So let's learn how to write better conditional code 📚. + +The patterns described here aren't mutually exclusive. You can combine them as you see necessary to keep your code clean. + +## Shared partial builder + +If your conditional code needs to set the same values for the same members, consider extracting this shared code into a variable that holds a partial builder. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder(on(String, into))] +struct User { + name: String, + tags: Vec, + alias: Option, + description: Option, +} + +// Common part is here // [!code highlight] +let user = User::builder() + .name("Bon") + .tags(vec!["confectioner".to_owned()]); + +// Differences are in each branch // [!code highlight] +let user = if 2 + 2 == 4 { + user + .alias("Good girl") + .description("Knows mathematics 🐱") + .build() +} else { + user + .description("Skipped math classes 😿") + .build() +}; +``` + +It's important to call the `build()` method inside each of the branches of conditional code. This way every branch returns the value of the same type. + +This also works with `match` expressions. Just remember to call `.build()` in every arm. + +::: tip Deep Thought 👀 + +This pattern of branching and converging on the same code path can be described using the terms "upcasting" or "context loss". + +The part of the code inside of the conditional branches has more context than the code that comes after. The code inside of the conditional block has a strongly typed builder that encodes the info that, for example, the `alias` member was set (and thus we can no longer call this setter). + +Once the `build()` method is called, we no longer have the context of how exactly the `User` was built. + +::: + +## Shared total builder + +In contrast to the [shared partial builder](#shared-partial-builder), here we are going to use the builder strictly _after_ the conditional code. The conditional code needs to create the variables that hold the component values for the builder beforehand. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder(on(String, into))] +struct User { + name: String, + tags: Vec, + alias: Option, + description: Option, +} + +let knows_math = 2 + 2 == 4; + +// Differences are in branches for each variable // [!code highlight] +let alias = if knows_math { + Some("Good girl") +} else { + None +}; +// ^^^^ This can be reduced to a single line with `bool::then_some()` +// (try it yourself) + +let description = if knows_math { + "Knows mathematics 🐱" +} else { + "Skipped math classes 😿" +}; + +// Common part is here // [!code highlight] +let user = User::builder() + .name("Bon") + .tags(vec!["confectioner".to_owned()]) + // Use the `maybe_` setter to provide an `Option` + .maybe_alias(alias) + // If the description could be `None`, we'd use `maybe_description`, + // but in this case, all branches set the `description`, so we can + // use this shorter setter that wraps with `Some()` internally + .description(description) + .build(); +``` + +In this case, we create a variable for each conditional member beforehand and initialize them separately, then we just pass the results to the builder. We benefit from the `maybe_` setters for [optional members](../optional-members) such that we can pass the `Option` values directly. + +::: tip NOTE + +However, creating separate variables is not strictly required. You can inline the usage of the variables in such a simple code like here, where each branch of the `if` takes a single line. Anyhow, branches can be much bigger in real code. + +::: + +If you'd like to avoid checking the same condition multiple times, you can write a single `if` that returns a tuple of values for several members: + +```rust +let (alias, description) = if 2 + 2 == 4 { + (Some("Good girl"), "Knows mathematics 🐱") +} else { + (None, "Skipped math classes 😿") +}; +``` + +However, when the number of members in the conditional logic grows, such an `if` with tuples becomes hard to read. In such a case, consider defining variables outside of the `if` or using the [shared partial builder](#shared-partial-builder) pattern instead. + +**Example:** + +```rust +let mut alias = None; +let description; + +if 2 + 2 == 4 { + alias = Some("Good girl"); + description = "Knows mathematics 🐱"; +} else { + description = "Skipped math classes 😿"; +} + +// Pass the variables to the builder now +// ... +``` + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/patterns/fallible-builders.md b/website/v2/guide/patterns/fallible-builders.md new file mode 100644 index 00000000..bbc2940b --- /dev/null +++ b/website/v2/guide/patterns/fallible-builders.md @@ -0,0 +1,41 @@ +# Fallible Builders + +With `bon`, you can write a builder that validates its inputs and returns a `Result`. It's possible to do this only via the function or associated method syntax. + +If you need to build a struct and do some validation, you won't be able to use the `#[derive(Builder)]` annotation on the struct for that. You'll *have to* define your logic in the associated constructor method (e.g. `new`). + +**Example:** + +```rust +use anyhow::Error; +use bon::bon; + +struct User { + id: u32, + name: String, +} + +#[bon] +impl User { + #[builder] + fn new(id: u32, name: String) -> Result { + if name.is_empty() { + return Err(anyhow::anyhow!("Empty name is disallowed (user id: {id})")); + } + + Ok(Self { id, name }) + } +} + +// The `build()` method returns a `Result` +let result = User::builder() + .id(42) + .name(String::new()) + .build(); + +if let Err(error) = result { + // Handle the error +} +``` + +If you have a use case for some convenience attributes to do automatic validations using the `#[derive(Builder)]` macro with the struct syntax, then add a 👍 reaction to [this Github issue](https://github.com/elastio/bon/issues/34). diff --git a/website/v2/guide/patterns/into-conversions-in-depth.md b/website/v2/guide/patterns/into-conversions-in-depth.md new file mode 100644 index 00000000..3db8d730 --- /dev/null +++ b/website/v2/guide/patterns/into-conversions-in-depth.md @@ -0,0 +1,379 @@ +--- +outline: deep +--- + + +# `Into` Conversions In-Depth + +## Preface + +This is the continuation of the ["Into conversions" section](../overview#into-conversions) from the general overview page. This page describes the important caveats of using `impl Into` that you should know before enabling them. + +Make sure you are familiar with the standard [`From`](https://doc.rust-lang.org/stable/std/convert/trait.From.html) and [`Into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html) traits before you proceed. Reading their docs is pretty much enough. For example, you should know that every type that implements `From` automatically implements `Into`. Also, you should know that you can pass a value of type `T` at no cost directly to a function that accepts `impl Into` thanks to [this blanket](https://github.com/rust-lang/rust/blob/1a94d839be8b248b972b9e022cb940d56de72fa1/library/core/src/convert/mod.rs#L763-L771) impl in `std`. + +::: warning + +This is generally a controversial topic 🐱. Some people like to be more explicit, but others prefer the shorter notation. This also depends on the kind of code you are writing. + +If you prefer being explicit in code, feel free not to use `Into` conversions at all. They are fully opt-in. This article isn't prescriptive. The syntax savings are arguably small, so use your best judgement, and refer to this page if you can't decide. + +::: + +We'll cover the following: + +- [Use `Into` conversions](#use-into-conversions) +- [Avoid `Into` conversions](#avoid-into-conversions) + +## Use `Into` conversions + +The main advantage of `impl Into` in setters is that it reduces the boilerplate for the caller. The code becomes shorter and cleaner, although not without [the drawbacks](#avoid-into-conversions). + +`Into` conversions usually make sense only if *all of the following* are true (AND): + +::: tip The Rules of `Into` + +1. The code where the builder is supposed to be *used* is not performance-sensitive. +2. The builder is going to be used with literal values a lot or require wrapping the values. + +::: + + +### Shorter syntax for literals + +Here is an example that shows the *non-exhaustive* list of standard types where it's usually fine to enable `Into` conversions. + +::: tip + +Switch between the UI tabs in the code snippets below to see how the code looks like with `Into Conversions` enabled and with the `Default` syntax. + +::: + +::: code-group +```rust [Into Conversions] +use bon::Builder; + +#[derive(Builder)] +struct Example { + #[builder(into)] // [!code highlight] + string: String, + + #[builder(into)] // [!code highlight] + path_buf: std::path::PathBuf, + + #[builder(into)] // [!code highlight] + ip_addr: std::net::IpAddr, +} + +Example::builder() + // We can pass `&str` literal // [!code highlight] + .string("string literal") // [!code highlight] + // We can pass `&str` literal or a String // [!code highlight] + .path_buf("string/literal") // [!code highlight] + // We can pass an array of IP components or `Ipv4Addr` or `Ipv6Addr` // [!code highlight] + .ip_addr([127, 0, 0, 1]) // [!code highlight] + .build(); +``` + +```rust [Default] +use bon::Builder; + +#[derive(Builder)] +struct Example { + // No attributes + string: String, + + // No attributes + path_buf: std::path::PathBuf, + + // No attributes + ip_addr: std::net::IpAddr, +} + +Example::builder() + // We have to convert `&str -> String` manually + .string("string literal".to_owned()) + // We have to convert `&str -> PathBuf` manually + .path_buf("string/literal".into()) + // We have to convert `[u8; 4] -> IpAddr` manually + .ip_addr([127, 0, 0, 1].into()) + .build(); +``` +::: + +### Automatic enum wrapping + +If you are working with enums a lot, you may implement the `From` for your enum and avoid wrapping your enum variants when passing them to the builder. Pay attention to the difference in the focused code below. + +::: code-group + +```rust [Into Conversions] +use bon::builder; + +#[builder] +fn evaluate(#[builder(into)] expr: Expr) { /* */ } // [!code focus] + +evaluate() + .expr(BinaryExpr { // [!code focus] + /* */ // [!code focus] + }) // [!code focus] + .call(); + +enum Expr { + Binary(BinaryExpr), + Unary(UnaryExpr) +} + +struct BinaryExpr { /* */ } +struct UnaryExpr { /* */ } + +impl From for Expr { + fn from(expr: BinaryExpr) -> Self { + Self::Binary(expr) + } +} + +impl From for Expr { + fn from(expr: UnaryExpr) -> Self { + Self::Unary(expr) + } +} +``` + +```rust [Default] +use bon::builder; + +#[builder] +fn evaluate(expr: Expr) { /* */ } // [!code focus] + +evaluate() + .expr(Expr::Binary(BinaryExpr { // [!code focus] + /* */ // [!code focus] + })) // [!code focus] + .call(); + +enum Expr { + Binary(BinaryExpr), + Unary(UnaryExpr) +} + +struct BinaryExpr { /* */ } +struct UnaryExpr { /* */ } + +impl From for Expr { + fn from(expr: BinaryExpr) -> Self { + Self::Binary(expr) + } +} + +impl From for Expr { + fn from(expr: UnaryExpr) -> Self { + Self::Unary(expr) + } +} +``` + +::: + +As you can see, the difference isn't significant in this case. It makes more sense when you have deeply nested enums. + +## Avoid `Into` conversions + +### Performance-sensitive code + +If allocations can pose a bottleneck for your application and you need to see every place in code where an allocation is performed, you should avoid using `impl Into` overall. It can lead to implicitly moving data to the heap or cloning it. + +**Example:** + +```rust +use bon::builder; + +#[builder] +fn process_heavy_json(#[builder(into)] data: String) { /* */ } + +let json = String::from( + r#"{ + "key": "Pretend this is a huge JSON string with hundreds of MB in size" + }"# +); + +process_heavy_json() + // Whoops, we passed a `&String`. // [!code error] + // The builder will clone the data internally. // [!code error] + .data(&json) // [!code error] + .call(); +``` + +The problem here is that we unintentionally passed a `String` by reference instead of moving the ownership of the `String` to `process_heavy_json()`. This code implicitly uses [this `From` impl](https://github.com/rust-lang/rust/blob/1a94d839be8b248b972b9e022cb940d56de72fa1/library/alloc/src/string.rs#L2774-L2784) from the standard library. + +### Primitive numeric literals + +`impl Into` breaks type inference for numeric literal values. For example, the following code doesn't compile. + +```rust compile_fail +fn half(x: impl Into) -> u32 { + x.into() / 2 +} + +half(10); // [!code error] +``` + +The compile error is the following ([Rust playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6b1b38e0de6f7747dc1ea3975fcffc06)): +```log +half(10); +---- ^^ the trait `std::convert::From` is not implemented for `u32`, +| which is required by `{integer}: std::convert::Into` +| +required by a bound introduced by this call +``` + +The reason for this error is that `rustc` can't infer the type for the numeric literal `10` because it could be one of the following types: `u8`, `u16`, `u32`, which all implement `Into`. There isn't a suffix like `10_u16` in this code to tell the compiler the type of the numeric literal `10`. When the compiler can't infer the type of a numeric literal it falls back to assigning the type `i32` for an integer literal and `f64` for a floating point literal. In this case `i32` is inferred, which isn't convertible to `u32`. + +Requiring an explicit type suffix in numeric literals would be the opposite of good ergonomics that `impl Into` is trying to achieve in the first place. + +### Weakened generics inference + +If you have a function that returns a generic type, then the compiler needs to infer that generic type from usage unless it's specified explicitly. A classic example of such a function is [`str::parse()`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.parse) or [`serde_json::from_str()`](https://docs.rs/serde_json/latest/serde_json/fn.from_str.html). + +**Example:** + +```rust +use bon::builder; +use std::net::IpAddr; + +#[builder] +fn connect(ip_addr: IpAddr) { /* */ } + +let ip_addr = "127.0.0.1".parse().unwrap(); + +connect() + .ip_addr(ip_addr) + .call(); +``` +Notice how we didn't add a type annotation for the variable `ip_addr`. The compiler can deduce (infer) the type of `ip_addr` because it sees that the variable is passed to the `ip_addr()` setter method that expects a parameter of type `IpAddr`. It's a really simple exercise for the compiler in this case because all the context to solve it is there. + +However, if you use an `Into` conversion, not even Sherlock Holmes can answer the question "What type did you intend to parse?": + +```rust ignore +use bon::builder; +use std::net::IpAddr; + +#[builder] +fn connect(ip_addr: IpAddr) { /* */ } // [!code --] +fn connect(#[builder(into)] ip_addr: IpAddr) { /* */ } // [!code ++] + +let ip_addr = "127.0.0.1".parse().unwrap(); + +connect() + .ip_addr(ip_addr) + .call(); +``` + +In this case, there is a compile error: + +```log +error[E0284]: type annotations needed // [!code error] + | // [!code error] +9 | let ip_addr = "127.0.0.1".parse().unwrap(); // [!code error] + | ^^^^^^^ ----- type must be known at this point // [!code error] + | // [!code error] + = note: cannot satisfy `<_ as std::str::FromStr>::Err == _` // [!code error] +help: consider giving `ip_addr` an explicit type // [!code error] + | // [!code error] +9 | let ip_addr: /* Type */ = "127.0.0.1".parse().unwrap(); // [!code error] + | ++++++++++++ // [!code error] +``` + +This is because now the `ip_addr` setter looks like this: + +```rust ignore +fn ip_addr(self, value: impl Into) -> NextBuilderState { /* */ } +``` + +This signature implies that the `value` parameter can be of any type that implements `Into`. There are several types that implement such a trait. Among them: [`Ipv4Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#impl-From%3CIpv4Addr%3E-for-IpAddr) and [`Ipv6Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#impl-From%3CIpv6Addr%3E-for-IpAddr), and, obviously, `IpAddr` itself (thanks to [this blanket impl](https://github.com/rust-lang/rust/blob/1a94d839be8b248b972b9e022cb940d56de72fa1/library/core/src/convert/mod.rs#L763-L771)). + +This means the setter for `ip_addr` can no longer hint the compiler a single type that it accepts. Thus the compiler can't decide which type to assign to the `ip_addr` variable in the original code, because *there can be many that make sense*. I.e. the code will compile if any of the `Ipv4Addr` or `Ipv6Addr` or `IpAddr` type annotations are added to the `ip_addr` variable, but the compiler has no right to decide which of them to use on your behalf. + +This is the drawback of using not only `impl Into`, but any generics at all. + + +### `None` literals inference + +`impl Into` breaks type inference for `None` literals. For example, this code doesn't use `Into` conversions and compiles fine: + +```rust +use bon::Builder; + +#[derive(Builder)] +struct Example { + member: Option +} + +Example::builder() + // Suppose we want to be explicit about omitting the `member`, + // so we intentionally invoke the `maybe_` setter and pass `None` to it + .maybe_member(None) + .build(); +``` + +Now, let's enable an `Into` conversion for the `member`: + +```rust compile_fail +use bon::Builder; + +#[derive(Builder)] +struct Example { + #[builder(into)] // [!code ++] + member: Option +} + +Example::builder() + .maybe_member(None) + .build(); +``` + +When we compile this code we get the following error: + +```log +.maybe_member(None) // [!code error] + ------------ ^^^^ cannot infer type of the type parameter `T` // [!code error] + declared on the enum `Option` // [!code error] +``` + +The problem here is that the compiler doesn't know the complete type of the `None` literal. It definitely knows that it's a value of type `Option<_>`, but it doesn't know what type to use in place of the `_`. There could be many potential candidates for the `_` inside of the `Option<_>`. This is because the signature of the `maybe_member()` setter changed: + +```rust ignore +fn maybe_member(self, value: Option) -> NextBuilderState // [!code --] +fn maybe_member(self, value: Option>) -> NextBuilderState // [!code ++] +``` + +Before we enabled `Into` conversions the signature provided a hint for the compiler because the setter expected a single concrete type `Option`, so it was obvious that the `None` literal was of type `Option`. + +However, after we enabled `Into` conversions, the signature no longer provides a single concrete type. It says that it accepts an `Option` of any type that implements `Into`. + +It means that the `None` literal could be of types `Option<&str>` or `Option`, for example, so the compiler can't decide which one you meant. And this matters, because `Option<&str>` and `Option` are totally different types. Simplified, `Option<&str>` is 16 bytes in size and `Option` is 24 bytes, even when they are `None`. + +To work around this problem the caller would need to explicitly specify the generic parameter for the `Option` type when passing the `None` literal: + +```rust +use bon::Builder; + +#[derive(Builder)] +struct Example { + #[builder(into)] // [!code ++] + member: Option +} + +Example::builder() + .maybe_member(None::) // [!code focus] + .build(); +``` + +### Code complexity + +This is quite subjective, but `impl Into` is a bit harder to read than just `T`. It makes the signature of the setter slightly bigger and requires you to understand what the `impl Trait` does, and what its implications are. + +If you want to keep your code simpler and more accessible (especially for beginner rustaceans), just avoid the `Into` conversions. + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments diff --git a/website/v2/guide/patterns/shared-configuration.md b/website/v2/guide/patterns/shared-configuration.md new file mode 100644 index 00000000..f550bb08 --- /dev/null +++ b/website/v2/guide/patterns/shared-configuration.md @@ -0,0 +1,105 @@ +--- +outline: deep +--- + +# Shared Configuration + +On this page, you'll learn how to share common configurations for builders to avoid code duplication. + +## Problem statement + +As an example, let's suppose you want to enable [`Into` conversions](./into-conversions-in-depth) for specific types across all your builders and maybe also override the name of the finishing function that consumes the builder from the default `build` to `finish`. The problem that you'll quickly run into is that you'll need to repeat the same configuration everywhere: + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder( + on(String, into), // [!code highlight] + on(Box<_>, into), // [!code highlight] + finish_fn = finish, // [!code highlight] +)] +struct MyLovelyStruct1 { /**/ } + + +#[derive(Builder)] +#[builder( + on(String, into), // [!code highlight] + on(Box<_>, into), // [!code highlight] + finish_fn = finish, // [!code highlight] +)] +struct MyLovelyStruct2 { /**/ } +``` + +::: tip + +This code uses the [`#[builder(on(...))]`](../../reference/builder#on) attribute to configure the types of members for which `bon` should enable `Into` conversions. + +::: + +The annoying thing here is that we need to copy all these configurations on every struct where we derive the builder. + +## Solution + +### Structs + +To overcome this problem we can utilize the [`macro_rules_attribute`] crate. It allows you to declare an [`attribute_alias`](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/macro.attribute_alias.html) that defines all the shared configuration for your builders and makes it reusable. + +So with the [`macro_rules_attribute`] your code will look like this: + +```rust +use macro_rules_attribute::{attribute_alias, apply}; + +// The alias can also be defined in a separate module. +// Under the hood it creates a macro with `pub(crate)` visibility. +attribute_alias! { + #[apply(derive_builder!)] = + #[derive(::bon::Builder)] + #[builder( + on(String, into), // [!code highlight] + on(Box<_>, into), // [!code highlight] + finish_fn = finish, // [!code highlight] + )]; +} + +#[apply(derive_builder!)] +struct MyLovelyStruct1 { /**/ } + +#[apply(derive_builder!)] +struct MyLovelyStruct2 { /**/ } +``` + +Use this approach if you have a lot of structs in your crate that need a builder. Adding [`macro_rules_attribute`] to your dependencies shouldn't have a noticeable impact on the compilation performance. This approach [was tested](https://github.com/ayrat555/frankenstein/blob/91ac379a52ed716e09632f78b984852c85f2adaa/src/macros.rs#L3-L14) on a crate with ~320 structs that derive a builder and compile time was the same as before adding the [`macro_rules_attribute`] crate. + +### Free functions + +A similar approach works with `#[bon::builder]` on free functions. +**Example:** + +```rust + +use macro_rules_attribute::{attribute_alias, apply}; + +attribute_alias! { + #[apply(builder!)] = + #[::bon::builder( + on(String, into), // [!code highlight] + on(Box<_>, into), // [!code highlight] + finish_fn = finish, // [!code highlight] + )]; +} + +#[apply(builder!)] +fn my_lovely_fn1(/**/) { /**/ } + +#[apply(builder!)] +fn my_lovely_fn2(/**/) { /**/ } +``` + +### Associated methods + +Unfortunately, this technique doesn't quite work with associated methods (functions inside impl blocks) due to the limitations of proc macro attribute expansion order. The `#[bon]` macro on top of the impl block is expanded first before the `#[apply(...)]` macro inside of the impl block, so `#[bon]` doesn't see the configuration expanded from the `#[apply(...)]`. + +There is a proposed solution to this problem in the issue [#elastio/bon#144](https://github.com/elastio/bon/issues/144). Add a 👍 to that issue if your use case needs a solution for this, and maybe leave a comment about your particular use case where you'd like to have this feature. + +[`macro_rules_attribute`]: https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/ diff --git a/website/v2/guide/positional-members.md b/website/v2/guide/positional-members.md new file mode 100644 index 00000000..95001b24 --- /dev/null +++ b/website/v2/guide/positional-members.md @@ -0,0 +1,146 @@ +# Positional Members + +While having the ability to use separate setters for the members gives you a ton of flexibility and extensibility described on the ["Compatibility"](./compatibility) page, sometimes you don't need all of that. + +Maybe you'd like to pick out some specific members and let the user pass their values as positional parameters to the starting function that creates the builder or to the finishing function that consumes it. This reduces the syntax a bit at the cost of some extensibility loss ⚖️, but it may be worth it! + +## Starting function + +As an example, suppose we have a `Treasure` struct with `x` and `y` coordinates and a `label` that describes the payload of the treasure. Since all treasures are located somewhere, they all have coordinates, and it would be cool to specify them in a single starting function call. + +To do that we can use the `#[builder(start_fn)]` attribute. There are two contexts where we can place it, and they both have a different meaning: + +- [Top-level `#[builder(start_fn = ...)]`](../reference/builder#start-fn) - configures the name of the starting function and optionally its visibility +- [Member-level `#[builder(start_fn)]`](../reference/builder#start-fn-1) - configures the member to be a positional parameter on the starting function + +We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure `x` and `y` as positional parameters on the starting function as well. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +// Top-level attribute to give a better name for the starting function // [!code highlight] +#[builder(start_fn = with_coordinates)] // [!code highlight] +struct Treasure { + // Member-level attribute to mark the member as // [!code highlight] + // a parameter of `with_coordinates()` // [!code highlight] + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: Option, +} + +let treasure = Treasure::with_coordinates(2, 9) // [!code highlight] + .label("oats".to_owned()) + .build(); + +assert_eq!(treasure.x, 2); +assert_eq!(treasure.y, 9); +assert_eq!(treasure.label.as_deref(), Some("oats")); +``` + +Here, the generated `with_coordinates` method has the following signature: + +```rust ignore +impl Treasure { + fn with_coordinates(x: u32, y: u32) -> TreasureBuilder { /**/ } +} +``` + +## Finishing function + +Now let's say we need to know the person who claimed the `Treasure`. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process. + +We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder#finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder#finish-fn-1) attributes to do that. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder( + start_fn = with_coordinates, + finish_fn = claim // [!code highlight] +)] +struct Treasure { + #[builder(start_fn)] + x: u32, + + #[builder(start_fn)] + y: u32, + + #[builder(finish_fn)] // [!code highlight] + claimed_by_first_name: String, // [!code highlight] + + #[builder(finish_fn)] // [!code highlight] + claimed_by_last_name: String, // [!code highlight] + + label: Option, +} + +let treasure = Treasure::with_coordinates(2, 9) + .label("oats".to_owned()) + .claim("Lyra".to_owned(), "Heartstrings".to_owned()); // [!code highlight] + +assert_eq!(treasure.x, 2); +assert_eq!(treasure.y, 9); +assert_eq!(treasure.label.as_deref(), Some("oats")); +assert_eq!(treasure.claimed_by_first_name, "Lyra"); // [!code highlight] +assert_eq!(treasure.claimed_by_last_name, "Heartstrings"); // [!code highlight] +``` + +## Into conversions + +You may also combine these attributes with [`#[builder(into)]`](../reference/builder#into) or [`#[builder(on(..., into))]`](../reference/builder#into) to reduce the number of `to_owned()` calls a bit. + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder( + start_fn = with_coordinates, + finish_fn = claim // [!code focus] +)] +struct Treasure { + #[builder(start_fn)] + x: u32, + + #[builder(start_fn)] + y: u32, + + #[builder(finish_fn, into)] // [!code focus] + claimed_by_first_name: String, // [!code focus] + + #[builder(finish_fn, into)] // [!code focus] + claimed_by_last_name: String, // [!code focus] + + #[builder(into)] // [!code focus] + label: Option, // [!code focus] +} + +let treasure = Treasure::with_coordinates(2, 9) + .label("oats") // [!code focus] + .claim("Lyra", "Heartstrings"); // [!code focus] +``` + +However, keep in mind that positional members (ones annotated with `#[builder(start_fn/finish_fn)]`) are always required to pass. There is no special treatment of the `Option` type for such members. + +For example `#[builder(into)]` on a regular (named) member of the `Option` type generates two setters: +- One that accepts `impl Into`. +- The other that accepts `Option>`. + +For positional members, the story is completely different because there are no separate setters generated for them. There is just a single starting or finishing function. So if you enable an into conversion for a positional member of the `Option` type, it will be accepted as `impl Into>` in the starting or finishing function. + +Also, the type pattern of the `#[builder(on(..., into))]` attribute matches the `Option` fully. So, for example `on(String, into)` will not match the positional member of type `Option`, but `on(Option, into)` will. + +::: tip + +In general, it's not recommended to annotate optional members with `#[builder(start_fn/finish_fn)]` because you can't omit setting them using the positional function call syntax. + +::: diff --git a/website/v2/guide/troubleshooting.md b/website/v2/guide/troubleshooting.md new file mode 100644 index 00000000..9f591922 --- /dev/null +++ b/website/v2/guide/troubleshooting.md @@ -0,0 +1,23 @@ +# Troubleshooting + +If you encounter any issues, then try the following. + +## Check for known limitations + +Check if your problem is already described on the [known limitations page](./limitations), where you'll also find some suggested workarounds. + +## Expand the macro + +Suppose you see a compile error coming from the code generated by a macro. The error message is not very informative because it points to some code inside of the macro that you don't see. + +In this case, try expanding the macro using Rust Analyzer's command ["Expand macro recursively"](https://rust-analyzer.github.io/manual.html#expand-macro-recursively). It works for any kind of macro including attribute macros. + +Then copy the expanded code, delete the original macro invocation, and replace it with the expanded code. Compile your program again to see where exactly the error occurs. + +**Example:** + + + +## Open an issue + +If nothing else helps, feel free to [open an issue](https://github.com/elastio/bon/issues). We'll definitely figure this out 🐱. diff --git a/website/v2/reference/bon.md b/website/v2/reference/bon.md new file mode 100644 index 00000000..1b24b699 --- /dev/null +++ b/website/v2/reference/bon.md @@ -0,0 +1,10 @@ +# `#[bon]` + +This is a companion macro for [`builder`]. You should place it on top of the `impl` block +where you want to define methods with the [`builder`] macro. It provides +the necessary context to the [`builder`] macros on top of the functions +inside of the `impl` block. You'll get compile errors without that context. + +For the examples of the usage of this macro and the reason why it's needed see this paragraph in [the overview](../guide/overview#builder-for-an-associated-method). + +[`builder`]: ./builder diff --git a/website/v2/reference/builder.md b/website/v2/reference/builder.md new file mode 100644 index 00000000..d961aea2 --- /dev/null +++ b/website/v2/reference/builder.md @@ -0,0 +1,1313 @@ +--- +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 to all kinds of syntax. However, some of them are only available with structs or only with functions/methods, for example. The **"Applies to"** clause specifies the contexts where the attribute can be used. + +::: tip Historical note + +`#[derive(bon::Builder)]` syntax appeared with the version `2.2` of `bon`. The older versions of `bon` (i.e. `<= 2.1`) supported only `#[bon::builder]` syntax with structs, but that syntax was deprecated in favor of the `derive` syntax for various reasons described in the [2.2 release blog post](/blog/bon-builder-v2-2-release). + +::: + +## Top-level attributes + +### `builder_type` + +**Applies to:** + +Overrides the name of the generated builder struct. + +The default naming pattern is the following: + +| Underlying item | Naming pattern | +| -------------------------- | --------------------------------------------- | +| Struct | `{StructName}Builder` | +| `StructName::new()` method | `{StructName}Builder` | +| Free function | `{PascalCaseFunctionName}Builder` | +| Associated method | `{SelfTypeName}{PascalCaseMethodName}Builder` | + +The attribute expects the desired builder type identifier as its input. + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(builder_type = MyBuilder)] // [!code highlight] +struct Brush {} + +let builder: MyBuilder = Brush::builder(); +``` + +```rust [Free function] +use bon::builder; + +#[builder(builder_type = MyBuilder)] // [!code highlight] +fn brush() {} + +let builder: MyBuilder = brush(); +``` + +```rust [Associated method] +use bon::bon; + +struct Brush; + +#[bon] +impl Brush { + #[builder(builder_type = MyBuilder)] // [!code highlight] + fn new() -> Self { + Self + } +} + +let builder: MyBuilder = Brush::builder(); +``` + +::: + +You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name annotated with `#[builder]`: + +::: code-group + +```rust compile_fail [Errored] +use bon::{builder, Builder}; + +#[derive(Builder)] // [!code error] +struct Brush {} + +#[builder] // [!code error] +fn brush() {} + +// `BrushBuilder` builder type name was generated for both +// the struct and the function. This is a compile error +let builder: BrushBuilder = Brush::builder(); +let builder: BrushBuilder = brush(); +``` + +```rust [Fixed] +use bon::{builder, Builder}; + +#[derive(Builder)] +#[builder(builder_type = MyBuilder)] // [!code highlight] +struct Brush {} + +#[builder] +fn brush() {} + +// Now builder types are named differently +let builder: MyBuilder = Brush::builder(); +let builder: BrushBuilder = brush(); +``` + +::: + +### `derive` + +**Applies to:** + +Generates additional derives on the builder type. The syntax is similar to the regular `#[derive(...)]` attribute. You need to specify one or more of the supported derives separated by commas. + +The following derives are supported: `Clone`, `Debug`. + +::: warning +The format of the `Debug` output of the builder is not stable and it may change between the patch versions of `bon`. +::: + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(derive(Clone, Debug))] // [!code highlight] +struct Example { + name: String, + is_admin: bool, + level: Option, +} + +let builder = Example::builder().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleBuilder { name: "Bon" }"# +); + +// Finish building +let example = builder.is_admin(true).build(); +``` + +```rust [Free function] +use bon::builder; + +#[builder(derive(Clone, Debug))] // [!code highlight] +fn example( + name: String, + is_admin: bool, + level: Option, +) {} + +let builder = example().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleBuilder { name: "Bon" }"# +); + +// Finish building +builder.is_admin(true).call(); +``` + +```rust [Associated method] +use bon::bon; + +#[derive(Debug)] +struct Example; + +#[bon] +impl Example { + #[builder(derive(Clone, Debug))] // [!code highlight] + fn method( + name: String, + is_admin: bool, + level: Option, + ) {} + + #[builder(derive(Debug))] + fn method_with_self(&self) {} +} + +let builder = Example::method().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleMethodBuilder { name: "Bon" }"# +); + +// Finish building +builder.is_admin(true).call(); + +// The debug output of the builder for methods with `self` includes +// the special `self` field with the receiver. +assert_eq!( + format!("{:?}", Example.method_with_self()), + "ExampleMethodWithSelfBuilder { self: Example }" +) +``` + +::: + +#### Compile errors + +_Requires_ that all members of the builder including the receiver (if this is a builder for an associated method) implement the target trait. For example, this doesn't compile because not all members implement `Clone`: + +**Example:** + +```rust compile_fail +use bon::Builder; + +struct NonClone; + +#[derive(Builder)] +#[builder(Clone)] +struct Example { + // Doesn't derive `Clone`, so this code doesn't compile // [!code error] + non_clone NonClone, // [!code error] + cloneable: u32 +} +``` + +### `expose_positional_fn` + +**Applies to:** + +When generating builder code for functions the `#[builder]` macro hides the original function with positional parameters used to define the builder. That function is invoked inside of the builder's `call()` or `build()` method. + +Usually you'd want the underlying positional function to be hidden to provide only the builder syntax to the callers. However, in some situations you may want to keep the positional function exposed along with the builder syntax for compatibility with old code that still uses the old positional function call syntax. + +This attribute can take several forms. + +- Simple: `#[builder(expose_positional_fn = identifier)]`. Sets only the name of the positional function. +- Verbose: `#[builder(expose_positional_fn(name = identifier, vis = "visibility"))]`. + Allows setting both the name and the visibility of the positional function. + Each key is optional. The `vis` must be specified as a string literal e.g. `"pub(crate)"`, `"pub"` or `""` (empty string means private visibility). + +If `vis` parameter is not specified, then the visibility of the exposed positional function will be the same as specified on the function that the `#[builder]` was applied to. + +**Example:** + +::: code-group + +```rust ignore [Free function] +use bon::builder; + +#[builder(expose_positional_fn = example_positional)] // [!code highlight] +fn example(x: u32, y: u32) {} + +// Positional function is now available under the given name // [!code highlight] +example_positional(1, 2); // [!code highlight] + +// Builder syntax is also available (unchanged) +example() + .x(1) + .y(2) + .call(); +``` + +```rust ignore [Associated method] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder(expose_positional_fn = example_positional)] // [!code highlight] + fn example(x: u32, y: u32) {} +} + +// Positional function is now available under the given name // [!code highlight] +Example::example_positional(1, 2); // [!code highlight] + +// Builder syntax is also available (unchanged) +Example::example() + .x(1) + .y(2) + .call(); +``` + +::: + +#### `new` method special case + +There are two conventional names in Rust ecosystem for constructors and builders: + +- `new` is used for a constructor method that uses positional parameters +- `builder` is used for a method that returns a builder for a type + +So when `#[builder]` is placed on a method called `new`, it'll generate a method called `builder` that starts the building process. This means there is already a default obvious name for the positional function that `expose_positional_fn` may use in this case if you don't specify any value for this attribute. + +**Example:** + +```rust ignore +use bon::bon; + +struct Example { + x: u32, + y: u32, +} + +#[bon] +impl Example { + #[builder(expose_positional_fn)] // [!code highlight] + fn new(x: u32, y: u32) -> Self { + Self { x, y } + } +} + +// Positional function is available under the name `new` // [!code highlight] +Example::new(1, 2); // [!code highlight] + +// Builder syntax is also available (unchanged) +Example::builder() + .x(1) + .y(2) + .build(); +``` + +This makes it possible to add builder syntax to your existing types that have the `new` method without breaking compatibility with old code. Old code can still use `T::new()` syntax, while new code can benefit from `T::builder()` syntax. + +### `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"); +``` + +::: + +### `on` + +**Applies to:** + +Applies the given builder attributes to all members that match the selected 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`](#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(); +``` + +### `start_fn` + +**Applies to:** + +Overrides the name and visibility of the associated method that starts the building process, i.e. returns the builder for the struct. + +The default name for this method is `builder`, and the default visibility is the same as the visibility of the struct itself. + +This attribute can take several forms. + +- Simple: `#[builder(start_fn = identifier)]`. Overrides only the name of the "start" method. +- Verbose: `#[builder(start_fn(name = identifier, vis = "visibility"))]`. + Allows overriding both the name and the visibility of the "start" method. + Each key is optional. The `vis` must be specified as a string literal e.g. `"pub(crate)"`, `"pub"` or `""` (empty string means private visibility). + +**Example:** + +::: code-group + +```rust [Simple form] +use bon::Builder; + +#[derive(Builder)] +#[builder(start_fn = init)] // [!code highlight] +struct User { + id: u32 +} + +User::init() // [!code highlight] + .id(42) + .build(); +``` + +```rust [Verbose form] +use bon::Builder; + +// `User::init()` method will have `pub(crate)` visibility // [!code highlight] +// Use `vis = ""` to make it fully private instead // [!code highlight] +#[derive(Builder)] +#[builder(start_fn(name = init, vis = "pub(crate)"))] // [!code highlight] +pub struct User { + id: u32 +} + +User::init() // [!code highlight] + .id(42) + .build(); +``` + +::: + +## Member attributes + +### `default` + +**Applies to:** + +Makes the member optional and assigns a default value to it. There will be two setter methods generated for the member just like for [members of type `Option`](../guide/optional-members). One setter accepts a value of type `T` (type of the member) and the other (with the `maybe_` prefix) accepts an `Option`. + +::: tip + +Switching between `#[builder(default)]` and `Option` is [compatible](../guide/compatibility#switching-between-option-t-and-builder-default). + +::: + +The default value will be lazily computed inside of the [finishing function](#finish-fn) (i.e. `build()` or `call()`). It is computed only if the setter for the member wasn't called or `None` was passed to the `maybe_{member}()` setter. + +The default value is computed based on the form of this attribute: + +| Form | How default value is computed | +| ---------------------------------- | ----------------------------- | +| `#[builder(default)]` | `Default::default()` | +| `#[builder(default = expression)]` | `expression` | + +The result of the `expression` will be converted into the target type using [`Into::into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html) if [`#[builder(into)]`](#into) is enabled for the setter. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct User { + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, +} + +let user = User::builder().build(); + +assert_eq!(user.level, 0); +assert_eq!(user.name, "anon"); +assert_eq!(user.permissions, ["read"]); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn greet_user( + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, +) -> String { + format!("Hello {name}! Your level is {level}, permissions: {permissions:?}") +} + +let greeting = greet_user().call(); + +assert_eq!(greeting, "Hello anon! Your level is 0, permissions: [\"read\"]"); +``` + +```rust [Associated method argument] +use bon::bon; + +struct User { + level: u32, + name: String, + permissions: Vec, +} + +#[bon] +impl User { + #[builder] + fn new( + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, + ) -> Self { + Self { level, name, permissions } + } +} + +let user = User::builder().build(); + +assert_eq!(user.name, "anon"); +assert_eq!(user.level, 0); +assert_eq!(user.permissions, ["read"]); +``` + +::: + +You can also 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. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Example { + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, +} + +let example = Example::builder() + .member_1(3) + .build(); + +assert_eq!(example.member_1, 3); +assert_eq!(example.member_2, 6); +assert_eq!(example.member_3, 9); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn example( + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, +) -> (u32, u32, u32) { + (member_1, member_2, member_3) +} + +let example = example() + .member_1(3) + .call(); + +assert_eq!(example, (3, 6, 9)); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder] + fn example( + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, + ) -> (u32, u32, u32) { + (member_1, member_2, member_3) + } +} + +let example = Example::example() + .member_1(3) + .call(); + +assert_eq!(example, (3, 6, 9)); +``` + +::: + +#### Caveats + +The `self` parameter in associated methods 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 + +This attribute is incompatible with members of `Option` type, since `Option` already implies the default value of `None`. + +### `finish_fn` + +**Applies to:** + +Makes the member a positional argument on the finishing function that consumes the builder and returns the resulting object (for struct syntax) or performs the requested action (for function/method syntax). + +The ordering of members annotated with `#[builder(finish_fn)]` matters! They will appear in the same order relative to each other in the finishing function signature. They must also be declared at the top of the members list strictly after members annotated with [`#[builder(start_fn)]`](#start-fn-1) (if any). + +This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. + +::: tip + +Don't confuse this with the top-level [`#[builder(finish_fn = ...)]`](#finish-fn) attribute that can be used to configure the name and visibility of the finishing function. You'll likely want to use it in combination with this member-level attribute to define a better name for the finishing function. + +::: + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +// Top-level attribute to give a better name for the finishing function // [!code highlight] +#[builder(finish_fn = sign)] // [!code highlight] +struct Message { + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + author_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + author_last_name: String, + + payload: String, +} + +let message = Message::builder() + .payload("Bon is great! Give it a ⭐".to_owned()) + .sign("Sweetie".to_owned(), "Drops".to_owned()); + +assert_eq!(message.payload, "Bon is great! Give it a ⭐"); +assert_eq!(message.author_first_name, "Sweetie"); +assert_eq!(message.author_last_name, "Drops"); +``` + +```rust [Free function] +use bon::builder; + +// Top-level attribute to give a better name for the finishing function // [!code highlight] +#[builder(finish_fn = send)] // [!code highlight] +fn message( + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + receiver_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + receiver_last_name: String, + + payload: String, +) {} + +message() + .payload("Bon is great! Give it a ⭐".to_owned()) + .send("Sweetie".to_owned(), "Drops".to_owned()); +``` + +```rust [Associated method] +use bon::bon; + +struct Chat {} + +#[bon] +impl Chat { + // Top-level attribute to give a better name for the finishing function // [!code highlight] + #[builder(finish_fn = send)] // [!code highlight] + fn message( + &self, + + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + receiver_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + receiver_last_name: String, + + payload: String, + ) {} +} + +let chat = Chat {}; + +chat.message() + .payload("Bon is great! Give it a ⭐".to_owned()) + .send("Sweetie".to_owned(), "Drops".to_owned()); +``` + +::: + +You can also combine this attribute with [`#[builder(into)]`](#into) or [`#[builder(on(..., into))]`](#on) to add an into conversion for the parameter. + +Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(finish_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. + +::: tip + +In general, it's not recommended to annotate optional members with `#[builder(finish_fn)]` because you can't omit setting them using the positional function call syntax. + +::: + +### `into` + +**Applies to:** + +Changes the signature of the generated setters to accept [`impl Into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html), where `T` is the type of the member. + +For [optional members](../guide/optional-members), the `maybe_{member}()` setter method will accept an `Option>` type instead of just `Option`. + +For members that use `#[builder(default = expression)]`, the `expression` will be converted with `Into::into`. + +This parameter is often used with the `String` type, which allows you to pass `&str` into the setter without calling `.to_owned()` or `.to_string()` on it. + +See the ["Into Conversions In-Depth"](../guide/patterns/into-conversions-in-depth) page that shows the common patterns and antipatterns of `impl Into`. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Example { + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String +} + +Example::builder() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .build(); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn example( + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String +) {} + +example() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .call(); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder] + fn example( + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String + ) {} +} + +Example::example() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .call(); +``` + +::: + +### `name` + +**Applies to:** + +Overrides the name of the member in the builder's setters and type state. This is most useful when with struct syntax (`#[derive(Builder)]`) where you'd like to use a different name for the field internally. For functions this attribute makes less sense since it's easy to just create a variable named differently `let new_name = param_name;`. However, this attribute is still supported on function arguments. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Player { + #[builder(name = rank)] // [!code highlight] + level: u32 +} + +Player::builder() + .rank(10) // [!code highlight] + .build(); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn player( + #[builder(name = rank)] // [!code highlight] + level: u32 +) {} + +player() + .rank(10) // [!code highlight] + .call(); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Player { + level: u32, +} + +#[bon] +impl Player { + #[builder] + fn new( + #[builder(name = rank)] // [!code highlight] + level: u32 + ) -> Self { + Self { level } + } +} + +Player::builder() + .rank(10) // [!code highlight] + .build(); +``` + +::: + +### `skip` + +**Applies to:** + +Skips generating setters for the member. This hides the member from the generated builder API, so the caller can't set its value. + +The value for the member will be computed based on the form of the attribute: + +| Form | How value for the member is computed | +| ------------------------------- | ------------------------------------ | +| `#[builder(skip)]` | `Default::default()` | +| `#[builder(skip = expression)]` | `expression` | + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct User { + #[builder(skip)] // [!code highlight] + level: u32, + + // Any complex expression is accepted // [!code highlight] + #[builder(skip = "anon".to_owned())] // [!code highlight] + name: String, +} + +let user = User::builder() + // There are no `level`, and `name` setters generated // [!code highlight] + .build(); + +assert_eq!(user.level, 0); +assert_eq!(user.name, "anon"); +``` + +You can also use the values of other members by referencing their names in the `skip` 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 `skip` expression. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct Example { + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(skip = 2 * member_1)] + member_2: u32, + + #[builder(skip = member_2 + member_1)] + member_3: u32, +} + +let example = Example::builder() + .member_1(3) + .build(); + +assert_eq!(example.member_1, 3); +assert_eq!(example.member_2, 6); +assert_eq!(example.member_3, 9); +``` + +This attribute is not supported with free function arguments or associated method arguments because it's simply unnecessary there and can easier be expressed with local variables. + +### `start_fn` + +**Applies to:** + +Makes the member a positional argument on the starting function that creates the builder. + +The ordering of members annotated with `#[builder(start_fn)]` matters! They will appear in the same order relative to each other in the starting function signature. They must also be declared at the top of the members' list. + +This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. + +::: tip + +Don't confuse this with the top-level [`#[builder(start_fn = ...)]`](#start-fn) attribute that can be used to configure the name and visibility of the starting function. You'll likely want to use it in combination with this member-level attribute to define a better name for the starting function. + +::: + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +// Top-level attribute to give a better name for the starting function // [!code highlight] +#[builder(start_fn = with_coordinates)] // [!code highlight] +struct Treasure { + // Member-level attribute to mark the member as // [!code highlight] + // a parameter of `with_coordinates()` // [!code highlight] + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: Option, +} + +let treasure = Treasure::with_coordinates(2, 9) // [!code highlight] + .label("knowledge".to_owned()) + .build(); + +assert_eq!(treasure.x, 2); +assert_eq!(treasure.y, 9); +assert_eq!(treasure.label.as_deref(), Some("knowledge")); +``` + +```rust [Free function] +use bon::builder; + +#[builder] +fn mark_treasure_at( + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: Option, +) {} + +mark_treasure_at(2, 9) + .label("knowledge".to_owned()) + .call(); +``` + +```rust [Associated method] +use bon::bon; + +struct Navigator {} + +#[bon] +impl Navigator { + #[builder] + fn mark_treasure_at( + &self, + + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: String, + ) {} +} + +let navigator = Navigator {}; + +navigator + .mark_treasure_at(2, 9) + .label("knowledge".to_owned()) + .call(); +``` + +::: + +You can also combine this attribute with [`#[builder(into)]`](#into) or [`#[builder(on(..., into))]`](#on) to add an into conversion for the parameter. + +Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(start_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. + +::: tip + +In general, it's not recommended to annotate optional members with `#[builder(start_fn)]` because you can't omit setting them using the positional function call syntax. + +::: + +*[Member]: Struct field or a function argument +*[member]: Struct field or a function argument +*[members]: Struct fields or function arguments