agy-superpowers 5.2.2 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -150
- package/package.json +1 -1
- package/template/agent/rules/CLAUDE.md +80 -0
- package/template/agent/rules/code-styles.md +31 -32
- package/template/agent/rules/debug-confirmation-policy.md +2 -0
- package/template/agent/rules/file-length-policy.md +2 -0
- package/template/agent/rules/git-policy.md +7 -0
- package/template/agent/rules/language-matching.md +2 -0
- package/template/agent/rules/scratch-scripts.md +39 -0
- package/template/agent/rules/superpowers.md +8 -51
- package/template/agent/skills/executing-plans/SKILL.md +17 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +16 -0
- package/template/agent/skills/test-driven-development/SKILL.md +16 -0
- package/template/agent/skills/verification-before-completion/SKILL.md +22 -0
- package/template/agent/skills/writing-plans/SKILL.md +16 -0
- package/template/agent/skills/ai-integrated-product/SKILL.md +0 -57
- package/template/agent/skills/analytics-setup/SKILL.md +0 -51
- package/template/agent/skills/api-design/SKILL.md +0 -193
- package/template/agent/skills/app-store-optimizer/SKILL.md +0 -127
- package/template/agent/skills/auth-and-identity/SKILL.md +0 -167
- package/template/agent/skills/backend-developer/SKILL.md +0 -148
- package/template/agent/skills/bootstrapper-finance/SKILL.md +0 -55
- package/template/agent/skills/chrome-extension-developer/SKILL.md +0 -53
- package/template/agent/skills/community-manager/SKILL.md +0 -115
- package/template/agent/skills/content-marketer/SKILL.md +0 -111
- package/template/agent/skills/conversion-optimizer/SKILL.md +0 -142
- package/template/agent/skills/cto-architect/SKILL.md +0 -133
- package/template/agent/skills/customer-success-manager/SKILL.md +0 -126
- package/template/agent/skills/data-analyst/SKILL.md +0 -147
- package/template/agent/skills/devops-engineer/SKILL.md +0 -117
- package/template/agent/skills/email-infrastructure/SKILL.md +0 -164
- package/template/agent/skills/game-design/SKILL.md +0 -194
- package/template/agent/skills/game-developer/SKILL.md +0 -175
- package/template/agent/skills/growth-hacker/SKILL.md +0 -122
- package/template/agent/skills/idea-validator/SKILL.md +0 -55
- package/template/agent/skills/indie-legal/SKILL.md +0 -53
- package/template/agent/skills/influencer-marketer/SKILL.md +0 -141
- package/template/agent/skills/landing-page-builder/SKILL.md +0 -59
- package/template/agent/skills/launch-strategist/SKILL.md +0 -62
- package/template/agent/skills/market-researcher/SKILL.md +0 -53
- package/template/agent/skills/micro-saas-builder/SKILL.md +0 -56
- package/template/agent/skills/monetization-strategist/SKILL.md +0 -119
- package/template/agent/skills/paid-acquisition-specialist/SKILL.md +0 -119
- package/template/agent/skills/pricing-psychologist/SKILL.md +0 -58
- package/template/agent/skills/real-time-features/SKILL.md +0 -194
- package/template/agent/skills/retention-specialist/SKILL.md +0 -123
- package/template/agent/skills/rust-developer/SKILL.md +0 -281
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +0 -231
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +0 -132
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +0 -121
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +0 -165
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +0 -163
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +0 -184
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +0 -182
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +0 -199
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +0 -185
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +0 -203
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +0 -195
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +0 -191
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +0 -198
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +0 -116
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +0 -128
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +0 -179
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +0 -145
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +0 -115
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +0 -107
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +0 -157
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +0 -174
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +0 -164
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +0 -99
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +0 -104
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +0 -94
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +0 -78
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +0 -76
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +0 -129
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +0 -86
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +0 -92
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +0 -101
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +0 -181
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +0 -105
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +0 -97
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +0 -119
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +0 -153
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +0 -150
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +0 -148
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +0 -186
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +0 -226
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +0 -188
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +0 -131
- package/template/agent/skills/saas-architect/SKILL.md +0 -139
- package/template/agent/skills/security-engineer/SKILL.md +0 -133
- package/template/agent/skills/seo-specialist/SKILL.md +0 -130
- package/template/agent/skills/solo-founder-ops/SKILL.md +0 -56
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# api-builder-pattern
|
|
2
|
-
|
|
3
|
-
> Use Builder pattern for complex construction
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
When a type has many optional parameters or complex initialization, the Builder pattern provides a clear, flexible API. It avoids constructors with many parameters (which are error-prone) and makes the code self-documenting.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Constructor with many parameters - hard to read, easy to get wrong
|
|
13
|
-
let client = Client::new(
|
|
14
|
-
"https://api.example.com", // Which is which?
|
|
15
|
-
30, // Timeout? Retries?
|
|
16
|
-
true, // What does this mean?
|
|
17
|
-
None,
|
|
18
|
-
Some("auth_token"),
|
|
19
|
-
false,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
// Or many Option fields
|
|
23
|
-
struct Client {
|
|
24
|
-
url: String,
|
|
25
|
-
timeout: Option<Duration>,
|
|
26
|
-
retries: Option<u32>,
|
|
27
|
-
// ... 10 more optional fields
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
#[derive(Default)]
|
|
35
|
-
#[must_use = "builders do nothing unless you call build()"]
|
|
36
|
-
pub struct ClientBuilder {
|
|
37
|
-
base_url: Option<String>,
|
|
38
|
-
timeout: Option<Duration>,
|
|
39
|
-
max_retries: u32,
|
|
40
|
-
auth_token: Option<String>,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
impl ClientBuilder {
|
|
44
|
-
pub fn new() -> Self {
|
|
45
|
-
Self::default()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/// Sets the base URL for all requests.
|
|
49
|
-
pub fn base_url(mut self, url: impl Into<String>) -> Self {
|
|
50
|
-
self.base_url = Some(url.into());
|
|
51
|
-
self
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/// Sets the request timeout. Default is 30 seconds.
|
|
55
|
-
pub fn timeout(mut self, timeout: Duration) -> Self {
|
|
56
|
-
self.timeout = Some(timeout);
|
|
57
|
-
self
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/// Sets the maximum number of retries. Default is 3.
|
|
61
|
-
pub fn max_retries(mut self, n: u32) -> Self {
|
|
62
|
-
self.max_retries = n;
|
|
63
|
-
self
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/// Sets the authentication token.
|
|
67
|
-
pub fn auth_token(mut self, token: impl Into<String>) -> Self {
|
|
68
|
-
self.auth_token = Some(token.into());
|
|
69
|
-
self
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/// Builds the client with the configured options.
|
|
73
|
-
pub fn build(self) -> Result<Client, BuilderError> {
|
|
74
|
-
let base_url = self.base_url
|
|
75
|
-
.ok_or(BuilderError::MissingBaseUrl)?;
|
|
76
|
-
|
|
77
|
-
Ok(Client {
|
|
78
|
-
base_url,
|
|
79
|
-
timeout: self.timeout.unwrap_or(Duration::from_secs(30)),
|
|
80
|
-
max_retries: self.max_retries,
|
|
81
|
-
auth_token: self.auth_token,
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Usage - clear and self-documenting
|
|
87
|
-
let client = ClientBuilder::new()
|
|
88
|
-
.base_url("https://api.example.com")
|
|
89
|
-
.timeout(Duration::from_secs(10))
|
|
90
|
-
.max_retries(5)
|
|
91
|
-
.auth_token("secret")
|
|
92
|
-
.build()?;
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Builder Variations
|
|
96
|
-
|
|
97
|
-
```rust
|
|
98
|
-
// 1. Infallible builder (build() returns T, not Result)
|
|
99
|
-
impl WidgetBuilder {
|
|
100
|
-
pub fn build(self) -> Widget {
|
|
101
|
-
Widget {
|
|
102
|
-
color: self.color.unwrap_or(Color::Black),
|
|
103
|
-
size: self.size.unwrap_or(Size::Medium),
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 2. Typestate builder (compile-time required field checking)
|
|
109
|
-
pub struct ClientBuilder<Url> {
|
|
110
|
-
url: Url,
|
|
111
|
-
timeout: Option<Duration>,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
pub struct NoUrl;
|
|
115
|
-
pub struct HasUrl(String);
|
|
116
|
-
|
|
117
|
-
impl ClientBuilder<NoUrl> {
|
|
118
|
-
pub fn new() -> Self {
|
|
119
|
-
Self { url: NoUrl, timeout: None }
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
pub fn url(self, url: String) -> ClientBuilder<HasUrl> {
|
|
123
|
-
ClientBuilder { url: HasUrl(url), timeout: self.timeout }
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
impl ClientBuilder<HasUrl> {
|
|
128
|
-
pub fn build(self) -> Client {
|
|
129
|
-
// url is guaranteed to be set
|
|
130
|
-
Client { url: self.url.0, timeout: self.timeout }
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 3. Consuming vs borrowing (consuming is more common)
|
|
135
|
-
// Consuming (takes self)
|
|
136
|
-
pub fn timeout(mut self, t: Duration) -> Self { ... }
|
|
137
|
-
|
|
138
|
-
// Borrowing (takes &mut self, allows reuse)
|
|
139
|
-
pub fn timeout(&mut self, t: Duration) -> &mut Self { ... }
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Evidence from reqwest
|
|
143
|
-
|
|
144
|
-
```rust
|
|
145
|
-
// https://github.com/seanmonstar/reqwest/blob/master/src/async_impl/client.rs
|
|
146
|
-
|
|
147
|
-
#[must_use]
|
|
148
|
-
pub struct ClientBuilder {
|
|
149
|
-
config: Config,
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
impl ClientBuilder {
|
|
153
|
-
pub fn new() -> ClientBuilder {
|
|
154
|
-
ClientBuilder {
|
|
155
|
-
config: Config::default(),
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
|
|
160
|
-
self.config.timeout = Some(timeout);
|
|
161
|
-
self
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
pub fn build(self) -> Result<Client, Error> {
|
|
165
|
-
// Validation and construction
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Key Attributes
|
|
171
|
-
|
|
172
|
-
```rust
|
|
173
|
-
#[derive(Default)] // Enables MyBuilder::default()
|
|
174
|
-
#[must_use = "builders do nothing unless you call build()"]
|
|
175
|
-
pub struct MyBuilder { ... }
|
|
176
|
-
|
|
177
|
-
impl MyBuilder {
|
|
178
|
-
#[must_use] // Each method should have this
|
|
179
|
-
pub fn option(mut self, value: T) -> Self { ... }
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## See Also
|
|
184
|
-
|
|
185
|
-
- [api-builder-must-use](api-builder-must-use.md) - Add #[must_use] to builders
|
|
186
|
-
- [api-typestate](api-typestate.md) - Compile-time state machines
|
|
187
|
-
- [api-impl-into](api-impl-into.md) - Accept impl Into for flexibility
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# api-common-traits
|
|
2
|
-
|
|
3
|
-
> Implement standard traits (Debug, Clone, PartialEq, etc.) for public types
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Standard traits make your types interoperable with the Rust ecosystem. `Debug` enables `println!("{:?}")` and error messages. `Clone` allows explicit duplication. `PartialEq` enables `==`. Without these, users can't use your types in common patterns like testing, collections, or debugging.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Bare struct - severely limited usability
|
|
13
|
-
pub struct Point {
|
|
14
|
-
pub x: f64,
|
|
15
|
-
pub y: f64,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Can't debug
|
|
19
|
-
println!("{:?}", point); // Error: Debug not implemented
|
|
20
|
-
|
|
21
|
-
// Can't compare
|
|
22
|
-
if point1 == point2 { } // Error: PartialEq not implemented
|
|
23
|
-
|
|
24
|
-
// Can't use in HashMap
|
|
25
|
-
let mut map: HashMap<Point, Value> = HashMap::new(); // Error: Hash not implemented
|
|
26
|
-
|
|
27
|
-
// Can't clone
|
|
28
|
-
let copy = point.clone(); // Error: Clone not implemented
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
35
|
-
pub struct Point {
|
|
36
|
-
pub x: f64,
|
|
37
|
-
pub y: f64,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Now everything works
|
|
41
|
-
println!("{:?}", point);
|
|
42
|
-
assert_eq!(point1, point2);
|
|
43
|
-
let copy = point; // Copy, not just Clone
|
|
44
|
-
|
|
45
|
-
// For hashable types
|
|
46
|
-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
47
|
-
pub struct UserId(u64);
|
|
48
|
-
|
|
49
|
-
let mut map: HashMap<UserId, User> = HashMap::new();
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Trait Derivation Guide
|
|
53
|
-
|
|
54
|
-
| Trait | Derive When | Requirements |
|
|
55
|
-
|-------|-------------|--------------|
|
|
56
|
-
| `Debug` | Always for public types | All fields implement Debug |
|
|
57
|
-
| `Clone` | Type can be duplicated | All fields implement Clone |
|
|
58
|
-
| `Copy` | Small, simple types | All fields implement Copy, no Drop |
|
|
59
|
-
| `PartialEq` | Comparison makes sense | All fields implement PartialEq |
|
|
60
|
-
| `Eq` | Total equality | PartialEq, no floating-point fields |
|
|
61
|
-
| `Hash` | Used as HashMap/HashSet key | Eq, consistent with PartialEq |
|
|
62
|
-
| `Default` | Sensible default exists | All fields implement Default |
|
|
63
|
-
| `PartialOrd` | Ordering makes sense | PartialEq, all fields implement PartialOrd |
|
|
64
|
-
| `Ord` | Total ordering | Eq + PartialOrd, no floating-point |
|
|
65
|
-
|
|
66
|
-
## Common Trait Bundles
|
|
67
|
-
|
|
68
|
-
```rust
|
|
69
|
-
// ID types
|
|
70
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
71
|
-
pub struct EntityId(u64);
|
|
72
|
-
|
|
73
|
-
// Value types
|
|
74
|
-
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
|
75
|
-
pub struct Vector2 { x: f32, y: f32 }
|
|
76
|
-
|
|
77
|
-
// Configuration
|
|
78
|
-
#[derive(Debug, Clone, PartialEq, Default)]
|
|
79
|
-
pub struct Config {
|
|
80
|
-
name: String,
|
|
81
|
-
options: HashMap<String, String>,
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Error types
|
|
85
|
-
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
86
|
-
pub enum ParseError {
|
|
87
|
-
InvalidSyntax(String),
|
|
88
|
-
UnexpectedToken(Token),
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Manual Implementations
|
|
93
|
-
|
|
94
|
-
```rust
|
|
95
|
-
// When derive doesn't do what you want
|
|
96
|
-
struct CaseInsensitiveString(String);
|
|
97
|
-
|
|
98
|
-
impl PartialEq for CaseInsensitiveString {
|
|
99
|
-
fn eq(&self, other: &Self) -> bool {
|
|
100
|
-
self.0.to_lowercase() == other.0.to_lowercase()
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
impl Eq for CaseInsensitiveString {}
|
|
105
|
-
|
|
106
|
-
impl Hash for CaseInsensitiveString {
|
|
107
|
-
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
108
|
-
// Must be consistent with PartialEq
|
|
109
|
-
self.0.to_lowercase().hash(state);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Custom Debug for sensitive data
|
|
114
|
-
struct Password(String);
|
|
115
|
-
|
|
116
|
-
impl Debug for Password {
|
|
117
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
118
|
-
write!(f, "Password([REDACTED])")
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Serde Traits
|
|
124
|
-
|
|
125
|
-
```rust
|
|
126
|
-
use serde::{Serialize, Deserialize};
|
|
127
|
-
|
|
128
|
-
// For serializable types
|
|
129
|
-
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
130
|
-
pub struct ApiResponse {
|
|
131
|
-
pub status: String,
|
|
132
|
-
pub data: Vec<Item>,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// With custom serialization
|
|
136
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
137
|
-
pub struct Config {
|
|
138
|
-
#[serde(default)]
|
|
139
|
-
pub verbose: bool,
|
|
140
|
-
|
|
141
|
-
#[serde(skip_serializing_if = "Option::is_none")]
|
|
142
|
-
pub api_key: Option<String>,
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Minimum Recommended
|
|
147
|
-
|
|
148
|
-
```rust
|
|
149
|
-
// At minimum, public types should have:
|
|
150
|
-
#[derive(Debug, Clone, PartialEq)]
|
|
151
|
-
pub struct MyType { ... }
|
|
152
|
-
|
|
153
|
-
// Add based on use case:
|
|
154
|
-
// + Eq, Hash → for HashMap keys
|
|
155
|
-
// + Ord, PartialOrd → for BTreeMap, sorting
|
|
156
|
-
// + Default → for Option::unwrap_or_default()
|
|
157
|
-
// + Copy → for small value types
|
|
158
|
-
// + Serialize → for serialization
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
## See Also
|
|
162
|
-
|
|
163
|
-
- [own-copy-small](./own-copy-small.md) - When to implement Copy
|
|
164
|
-
- [api-default-impl](./api-default-impl.md) - Implementing Default
|
|
165
|
-
- [doc-examples-section](./doc-examples-section.md) - Documenting trait implementations
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# api-default-impl
|
|
2
|
-
|
|
3
|
-
> Implement `Default` for types with sensible default values
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`Default` is a standard trait that provides a canonical way to create a default instance. It integrates with many ecosystem patterns: `Option::unwrap_or_default()`, `#[derive(Default)]`, struct update syntax `..Default::default()`, and generic code that requires `T: Default`. Implementing it makes your types more ergonomic.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
struct Config {
|
|
13
|
-
timeout: Duration,
|
|
14
|
-
retries: u32,
|
|
15
|
-
verbose: bool,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
impl Config {
|
|
19
|
-
// Custom constructor - works but non-standard
|
|
20
|
-
fn new() -> Self {
|
|
21
|
-
Config {
|
|
22
|
-
timeout: Duration::from_secs(30),
|
|
23
|
-
retries: 3,
|
|
24
|
-
verbose: false,
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Can't use with standard patterns
|
|
30
|
-
let config: Config = Default::default(); // Error: Default not implemented
|
|
31
|
-
let timeout = settings.get("timeout").unwrap_or_default(); // Won't work
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Good
|
|
35
|
-
|
|
36
|
-
```rust
|
|
37
|
-
#[derive(Default)]
|
|
38
|
-
struct Config {
|
|
39
|
-
#[default = Duration::from_secs(30)] // Nightly, or implement manually
|
|
40
|
-
timeout: Duration,
|
|
41
|
-
retries: u32, // Defaults to 0 with derive
|
|
42
|
-
verbose: bool, // Defaults to false with derive
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Or implement manually for custom defaults
|
|
46
|
-
impl Default for Config {
|
|
47
|
-
fn default() -> Self {
|
|
48
|
-
Config {
|
|
49
|
-
timeout: Duration::from_secs(30),
|
|
50
|
-
retries: 3,
|
|
51
|
-
verbose: false,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Now works with all standard patterns
|
|
57
|
-
let config = Config::default();
|
|
58
|
-
let config = Config { retries: 5, ..Default::default() };
|
|
59
|
-
let value = map.get("key").cloned().unwrap_or_default();
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Derive vs Manual
|
|
63
|
-
|
|
64
|
-
```rust
|
|
65
|
-
// Derive: all fields use their own Default
|
|
66
|
-
#[derive(Default)]
|
|
67
|
-
struct Simple {
|
|
68
|
-
count: u32, // 0
|
|
69
|
-
name: String, // ""
|
|
70
|
-
items: Vec<i32>, // []
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Manual: when you need custom defaults
|
|
74
|
-
struct Connection {
|
|
75
|
-
host: String,
|
|
76
|
-
port: u16,
|
|
77
|
-
timeout: Duration,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
impl Default for Connection {
|
|
81
|
-
fn default() -> Self {
|
|
82
|
-
Connection {
|
|
83
|
-
host: "localhost".to_string(),
|
|
84
|
-
port: 8080,
|
|
85
|
-
timeout: Duration::from_secs(30),
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Builder with Default
|
|
92
|
-
|
|
93
|
-
```rust
|
|
94
|
-
#[derive(Default)]
|
|
95
|
-
struct ServerBuilder {
|
|
96
|
-
host: String,
|
|
97
|
-
port: u16,
|
|
98
|
-
workers: usize,
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
impl ServerBuilder {
|
|
102
|
-
fn host(mut self, host: impl Into<String>) -> Self {
|
|
103
|
-
self.host = host.into();
|
|
104
|
-
self
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
fn port(mut self, port: u16) -> Self {
|
|
108
|
-
self.port = port;
|
|
109
|
-
self
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Clean initialization
|
|
114
|
-
let server = ServerBuilder::default()
|
|
115
|
-
.host("0.0.0.0")
|
|
116
|
-
.port(3000)
|
|
117
|
-
.build();
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Default with Required Fields
|
|
121
|
-
|
|
122
|
-
```rust
|
|
123
|
-
// When some fields have no sensible default, don't implement Default
|
|
124
|
-
struct User {
|
|
125
|
-
id: UserId, // No sensible default
|
|
126
|
-
name: String, // Could default to ""
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Instead, provide a constructor
|
|
130
|
-
impl User {
|
|
131
|
-
fn new(id: UserId, name: impl Into<String>) -> Self {
|
|
132
|
-
User { id, name: name.into() }
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Or use builder with required fields
|
|
137
|
-
struct UserBuilder {
|
|
138
|
-
id: Option<UserId>,
|
|
139
|
-
name: String,
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
impl Default for UserBuilder {
|
|
143
|
-
fn default() -> Self {
|
|
144
|
-
UserBuilder {
|
|
145
|
-
id: None,
|
|
146
|
-
name: String::new(),
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Generic Default
|
|
153
|
-
|
|
154
|
-
```rust
|
|
155
|
-
// Require Default in generic bounds when needed
|
|
156
|
-
fn create_or_default<T: Default>(opt: Option<T>) -> T {
|
|
157
|
-
opt.unwrap_or_default()
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// PhantomData is Default regardless of T
|
|
161
|
-
use std::marker::PhantomData;
|
|
162
|
-
struct Wrapper<T> {
|
|
163
|
-
_marker: PhantomData<T>,
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
impl<T> Default for Wrapper<T> {
|
|
167
|
-
fn default() -> Self {
|
|
168
|
-
Wrapper { _marker: PhantomData }
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## See Also
|
|
174
|
-
|
|
175
|
-
- [api-builder-pattern](./api-builder-pattern.md) - Building complex types
|
|
176
|
-
- [api-common-traits](./api-common-traits.md) - Other common traits to implement
|
|
177
|
-
- [api-from-not-into](./api-from-not-into.md) - Conversion traits
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# api-extension-trait
|
|
2
|
-
|
|
3
|
-
> Use extension traits to add methods to external types
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Rust's orphan rules prevent implementing external traits on external types. Extension traits provide a workaround: define a new trait with your methods, then implement it for the external type. This pattern is used extensively in the ecosystem (e.g., `itertools::Itertools`, `tokio::AsyncReadExt`).
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Can't add methods directly to external types
|
|
13
|
-
impl Vec<u8> {
|
|
14
|
-
fn as_hex(&self) -> String {
|
|
15
|
-
// Error: cannot define inherent impl for a type outside this crate
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Can't implement external trait for external type
|
|
20
|
-
impl SomeExternalTrait for Vec<u8> {
|
|
21
|
-
// Error: orphan rules violation
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Good
|
|
26
|
-
|
|
27
|
-
```rust
|
|
28
|
-
// Define an extension trait
|
|
29
|
-
pub trait ByteSliceExt {
|
|
30
|
-
fn as_hex(&self) -> String;
|
|
31
|
-
fn is_ascii_printable(&self) -> bool;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Implement for the external type
|
|
35
|
-
impl ByteSliceExt for [u8] {
|
|
36
|
-
fn as_hex(&self) -> String {
|
|
37
|
-
self.iter()
|
|
38
|
-
.map(|b| format!("{:02x}", b))
|
|
39
|
-
.collect()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fn is_ascii_printable(&self) -> bool {
|
|
43
|
-
self.iter().all(|b| b.is_ascii_graphic() || b.is_ascii_whitespace())
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Usage: import the trait to use the methods
|
|
48
|
-
use my_crate::ByteSliceExt;
|
|
49
|
-
|
|
50
|
-
let data: &[u8] = b"hello";
|
|
51
|
-
println!("{}", data.as_hex()); // "68656c6c6f"
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Convention: Ext Suffix
|
|
55
|
-
|
|
56
|
-
```rust
|
|
57
|
-
// Standard naming: TypeExt for extending Type
|
|
58
|
-
pub trait OptionExt<T> {
|
|
59
|
-
fn unwrap_or_log(self, msg: &str) -> Option<T>;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
impl<T> OptionExt<T> for Option<T> {
|
|
63
|
-
fn unwrap_or_log(self, msg: &str) -> Option<T> {
|
|
64
|
-
if self.is_none() {
|
|
65
|
-
log::warn!("{}", msg);
|
|
66
|
-
}
|
|
67
|
-
self
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// For generic extensions
|
|
72
|
-
pub trait ResultExt<T, E> {
|
|
73
|
-
fn log_err(self) -> Self;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
impl<T, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
|
|
77
|
-
fn log_err(self) -> Self {
|
|
78
|
-
if let Err(ref e) = self {
|
|
79
|
-
log::error!("{}", e);
|
|
80
|
-
}
|
|
81
|
-
self
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Ecosystem Examples
|
|
87
|
-
|
|
88
|
-
```rust
|
|
89
|
-
// itertools::Itertools
|
|
90
|
-
use itertools::Itertools;
|
|
91
|
-
let groups = vec![1, 1, 2, 2, 3].into_iter().group_by(|x| *x);
|
|
92
|
-
|
|
93
|
-
// futures::StreamExt
|
|
94
|
-
use futures::StreamExt;
|
|
95
|
-
let next = stream.next().await;
|
|
96
|
-
|
|
97
|
-
// tokio::io::AsyncReadExt
|
|
98
|
-
use tokio::io::AsyncReadExt;
|
|
99
|
-
let mut buf = [0u8; 1024];
|
|
100
|
-
reader.read(&mut buf).await?;
|
|
101
|
-
|
|
102
|
-
// anyhow::Context
|
|
103
|
-
use anyhow::Context;
|
|
104
|
-
let content = std::fs::read_to_string(path)
|
|
105
|
-
.with_context(|| format!("failed to read {}", path))?;
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Scoped Extensions
|
|
109
|
-
|
|
110
|
-
```rust
|
|
111
|
-
// Extension only visible where imported
|
|
112
|
-
mod string_utils {
|
|
113
|
-
pub trait StringExt {
|
|
114
|
-
fn truncate_ellipsis(&self, max_len: usize) -> String;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
impl StringExt for str {
|
|
118
|
-
fn truncate_ellipsis(&self, max_len: usize) -> String {
|
|
119
|
-
if self.len() <= max_len {
|
|
120
|
-
self.to_string()
|
|
121
|
-
} else {
|
|
122
|
-
format!("{}...", &self[..max_len.saturating_sub(3)])
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Only available when explicitly imported
|
|
129
|
-
use string_utils::StringExt;
|
|
130
|
-
let short = "very long string".truncate_ellipsis(10);
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Generic Extensions with Bounds
|
|
134
|
-
|
|
135
|
-
```rust
|
|
136
|
-
pub trait VecExt<T> {
|
|
137
|
-
fn push_if_unique(&mut self, item: T)
|
|
138
|
-
where
|
|
139
|
-
T: PartialEq;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
impl<T> VecExt<T> for Vec<T> {
|
|
143
|
-
fn push_if_unique(&mut self, item: T)
|
|
144
|
-
where
|
|
145
|
-
T: PartialEq,
|
|
146
|
-
{
|
|
147
|
-
if !self.contains(&item) {
|
|
148
|
-
self.push(item);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Works with any T: PartialEq
|
|
154
|
-
let mut v = vec![1, 2, 3];
|
|
155
|
-
v.push_if_unique(2); // No-op
|
|
156
|
-
v.push_if_unique(4); // Adds 4
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## See Also
|
|
160
|
-
|
|
161
|
-
- [api-sealed-trait](./api-sealed-trait.md) - Controlling trait implementations
|
|
162
|
-
- [api-impl-into](./api-impl-into.md) - Using standard conversion traits
|
|
163
|
-
- [name-as-free](./name-as-free.md) - Naming conventions for conversions
|