@girardmedia/bootspring 3.3.2 → 3.4.0

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.
Files changed (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,199 @@
1
+ ---
2
+ name: responsive-design
3
+ description: Responsive design patterns with mobile-first CSS, container queries, fluid typography, and breakpoint strategy.
4
+ ---
5
+
6
+ # Responsive Design
7
+
8
+ ## When to Use
9
+ Apply to every web interface. Over 60% of web traffic is mobile. Responsive design ensures your UI works on phones (320px), tablets (768px), laptops (1024px), and ultrawide monitors (2560px) without separate codebases. Start mobile-first and progressively enhance.
10
+
11
+ ## How It Works
12
+
13
+ ### Mobile-First Approach
14
+
15
+ Write base styles for the smallest screen, then add complexity with `min-width` media queries:
16
+
17
+ ```css
18
+ /* Base -- mobile (320px+) */
19
+ .card-grid {
20
+ display: grid;
21
+ grid-template-columns: 1fr;
22
+ gap: 1rem;
23
+ padding: 1rem;
24
+ }
25
+
26
+ /* Tablet (640px+) */
27
+ @media (min-width: 640px) {
28
+ .card-grid {
29
+ grid-template-columns: repeat(2, 1fr);
30
+ gap: 1.5rem;
31
+ }
32
+ }
33
+
34
+ /* Desktop (1024px+) */
35
+ @media (min-width: 1024px) {
36
+ .card-grid {
37
+ grid-template-columns: repeat(3, 1fr);
38
+ gap: 2rem;
39
+ max-width: 1200px;
40
+ margin: 0 auto;
41
+ }
42
+ }
43
+ ```
44
+
45
+ Never use `max-width` queries as your primary strategy. You end up overriding desktop styles for mobile, which is more code and more bugs.
46
+
47
+ ### Container Queries
48
+
49
+ Media queries respond to the viewport. Container queries respond to the parent container -- critical for reusable components:
50
+
51
+ ```css
52
+ /* Define the container */
53
+ .sidebar {
54
+ container-type: inline-size;
55
+ container-name: sidebar;
56
+ }
57
+
58
+ /* Component adapts to its container, not the viewport */
59
+ @container sidebar (min-width: 300px) {
60
+ .user-card {
61
+ display: flex;
62
+ flex-direction: row;
63
+ gap: 1rem;
64
+ }
65
+ }
66
+
67
+ @container sidebar (max-width: 299px) {
68
+ .user-card {
69
+ display: flex;
70
+ flex-direction: column;
71
+ text-align: center;
72
+ }
73
+ }
74
+ ```
75
+
76
+ Use container queries when a component lives in multiple layout contexts (sidebar, main content, modal).
77
+
78
+ ### Fluid Typography
79
+
80
+ Scale text smoothly between breakpoints with `clamp()`:
81
+
82
+ ```css
83
+ :root {
84
+ --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
85
+ --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
86
+ --text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
87
+ --text-lg: clamp(1.125rem, 1rem + 0.65vw, 1.375rem);
88
+ --text-xl: clamp(1.375rem, 1.1rem + 1.2vw, 2rem);
89
+ --text-2xl: clamp(1.75rem, 1.3rem + 2vw, 3rem);
90
+ }
91
+
92
+ h1 { font-size: var(--text-2xl); }
93
+ h2 { font-size: var(--text-xl); }
94
+ p { font-size: var(--text-base); }
95
+ ```
96
+
97
+ The formula `clamp(min, preferred, max)` prevents text from being too small on phones or too large on 4K monitors.
98
+
99
+ ### Breakpoint Strategy
100
+
101
+ Use content-driven breakpoints, not device-specific ones:
102
+
103
+ ```css
104
+ /* Tailwind defaults -- a solid starting point */
105
+ /* sm: 640px -- small tablets, large phones landscape */
106
+ /* md: 768px -- tablets portrait */
107
+ /* lg: 1024px -- tablets landscape, small laptops */
108
+ /* xl: 1280px -- standard laptops */
109
+ /* 2xl: 1536px -- large monitors */
110
+
111
+ /* Custom breakpoint only when content demands it */
112
+ @media (min-width: 480px) {
113
+ .pricing-card { flex-direction: row; }
114
+ }
115
+ ```
116
+
117
+ Rules: use 3-5 breakpoints maximum. Name them by size (sm/md/lg), not by device.
118
+
119
+ ### Touch Targets
120
+
121
+ Mobile users tap with fingers. Minimum touch target: 44x44px (WCAG), recommended 48x48px:
122
+
123
+ ```css
124
+ .nav-link {
125
+ min-height: 44px;
126
+ padding: 12px 16px;
127
+ display: flex;
128
+ align-items: center;
129
+ }
130
+
131
+ .button-group {
132
+ display: flex;
133
+ gap: 8px; /* minimum 8px between adjacent targets */
134
+ }
135
+
136
+ .icon-button {
137
+ width: 48px;
138
+ height: 48px;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ border-radius: 50%;
143
+ }
144
+ ```
145
+
146
+ ### Responsive Layout Patterns
147
+
148
+ ```css
149
+ /* Sidebar layout -- collapses on mobile */
150
+ .app-layout {
151
+ display: grid;
152
+ grid-template-columns: 1fr;
153
+ }
154
+ @media (min-width: 1024px) {
155
+ .app-layout { grid-template-columns: 280px 1fr; }
156
+ }
157
+
158
+ /* Responsive table -- stacks on mobile */
159
+ @media (max-width: 639px) {
160
+ table, thead, tbody, th, td, tr { display: block; }
161
+ thead { position: absolute; width: 1px; height: 1px; overflow: hidden; }
162
+ td::before {
163
+ content: attr(data-label);
164
+ font-weight: 600;
165
+ display: block;
166
+ }
167
+ }
168
+ ```
169
+
170
+ Responsive images with art direction:
171
+
172
+ ```html
173
+ <picture>
174
+ <source media="(min-width: 1024px)" srcset="/hero-wide.webp" />
175
+ <source media="(min-width: 640px)" srcset="/hero-medium.webp" />
176
+ <img src="/hero-mobile.webp" alt="Hero" loading="lazy" />
177
+ </picture>
178
+ ```
179
+
180
+ ## Examples
181
+
182
+ | Problem | Solution | Benefit |
183
+ |---------|----------|---------|
184
+ | Cards overflow on 320px | Mobile-first grid, 1fr base | Content always fits |
185
+ | Component in sidebar and modal | Container queries | Adapts to container |
186
+ | Heading huge on mobile | `clamp()` fluid typography | Smooth scaling |
187
+ | Users mis-tap small buttons | 48px minimum touch targets | Fewer mis-taps |
188
+ | Data table unreadable on mobile | Stack cells with `data-label` | Readable at any width |
189
+ | Desktop images on mobile | `<picture>` with sources | 3x faster mobile load |
190
+
191
+ ## Checklist
192
+ - [ ] Base styles target mobile (320px), enhanced with `min-width` queries
193
+ - [ ] Layout uses CSS Grid or Flexbox, not floats
194
+ - [ ] 3-5 breakpoints maximum, driven by content
195
+ - [ ] Typography uses `clamp()` for fluid scaling
196
+ - [ ] Reusable components use container queries
197
+ - [ ] Touch targets at least 44x44px with 8px minimum spacing
198
+ - [ ] Images use responsive sources (`<picture>`, `srcset`)
199
+ - [ ] Tested at 320px, 768px, 1024px, and 1440px widths
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: ruby-rails-patterns
3
+ description: Apply idiomatic Ruby on Rails patterns for models, controllers, background jobs, and Hotwire/Turbo real-time UIs.
4
+ ---
5
+
6
+ # Ruby on Rails Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when building or reviewing Rails applications. They cover
11
+ ActiveRecord modeling, concern extraction, service objects for complex business
12
+ logic, background jobs, and Hotwire/Turbo Streams for server-rendered real-time
13
+ UIs. Use this skill when standing up new features, refactoring fat controllers
14
+ or models, or adding real-time behavior without a JavaScript framework.
15
+
16
+ ## How It Works
17
+
18
+ ### 1. ActiveRecord — Keep Models Thin
19
+
20
+ Push queries into scopes. Push multi-step mutations into service objects.
21
+
22
+ ```ruby
23
+ class Order < ApplicationRecord
24
+ belongs_to :user
25
+ has_many :line_items, dependent: :destroy
26
+
27
+ scope :recent, -> { where("created_at > ?", 30.days.ago) }
28
+ scope :fulfilled, -> { where(status: :fulfilled) }
29
+ scope :expensive, ->(min) { where("total_cents >= ?", min) }
30
+
31
+ validates :total_cents, numericality: { greater_than: 0 }
32
+
33
+ # Callbacks are fine for simple lifecycle hooks — not business logic
34
+ after_create_commit :broadcast_to_dashboard
35
+ end
36
+ ```
37
+
38
+ ### 2. Concerns — Shared Behavior Modules
39
+
40
+ Extract cross-cutting behavior into concerns. Keep each concern under 50 lines.
41
+
42
+ ```ruby
43
+ # app/models/concerns/sluggable.rb
44
+ module Sluggable
45
+ extend ActiveSupport::Concern
46
+
47
+ included do
48
+ before_validation :generate_slug, on: :create
49
+ validates :slug, presence: true, uniqueness: true
50
+ end
51
+
52
+ private
53
+
54
+ def generate_slug
55
+ self.slug = name&.parameterize
56
+ end
57
+ end
58
+ ```
59
+
60
+ ### 3. Service Objects — Encapsulate Business Logic
61
+
62
+ One public method (`call`), explicit inputs, return a result object.
63
+
64
+ ```ruby
65
+ class Orders::Checkout
66
+ def initialize(user:, cart:, payment_method:)
67
+ @user = user
68
+ @cart = cart
69
+ @payment_method = payment_method
70
+ end
71
+
72
+ def call
73
+ ActiveRecord::Base.transaction do
74
+ order = Order.create!(user: @user, total_cents: @cart.total_cents)
75
+ @cart.items.each { |item| order.line_items.create!(item.attributes) }
76
+ charge = PaymentGateway.charge(@payment_method, order.total_cents)
77
+ order.update!(payment_id: charge.id, status: :paid)
78
+ Result.new(success: true, order: order)
79
+ end
80
+ rescue PaymentGateway::Error => e
81
+ Result.new(success: false, error: e.message)
82
+ end
83
+
84
+ Result = Struct.new(:success, :order, :error, keyword_init: true)
85
+ end
86
+ ```
87
+
88
+ ### 4. Background Jobs — Sidekiq/ActiveJob
89
+
90
+ Isolate slow work: emails, API calls, reports. Keep jobs idempotent.
91
+
92
+ ```ruby
93
+ class SyncInventoryJob < ApplicationJob
94
+ queue_as :default
95
+ retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 5
96
+
97
+ def perform(product_id)
98
+ product = Product.find(product_id)
99
+ stock = WarehouseApi.fetch_stock(product.sku)
100
+ product.update!(quantity: stock.available)
101
+ end
102
+ end
103
+ ```
104
+
105
+ ### 5. Hotwire — Turbo Frames and Streams
106
+
107
+ Replace SPA complexity with server-rendered partials streamed over WebSocket.
108
+
109
+ ```erb
110
+ <!-- app/views/orders/index.html.erb -->
111
+ <%= turbo_stream_from "orders" %>
112
+
113
+ <div id="orders">
114
+ <%= turbo_frame_tag "orders_list" do %>
115
+ <%= render @orders %>
116
+ <% end %>
117
+ </div>
118
+ ```
119
+
120
+ ```ruby
121
+ # Broadcast from model or job
122
+ class Order < ApplicationRecord
123
+ after_create_commit -> {
124
+ broadcast_prepend_to "orders",
125
+ partial: "orders/order",
126
+ locals: { order: self }
127
+ }
128
+ end
129
+ ```
130
+
131
+ ### 6. Turbo Stream Actions
132
+
133
+ Use `turbo_stream` responses for form submissions without full page reloads.
134
+
135
+ ```ruby
136
+ # app/controllers/comments_controller.rb
137
+ def create
138
+ @comment = @post.comments.build(comment_params)
139
+ if @comment.save
140
+ respond_to do |format|
141
+ format.turbo_stream
142
+ format.html { redirect_to @post }
143
+ end
144
+ else
145
+ render :new, status: :unprocessable_entity
146
+ end
147
+ end
148
+ ```
149
+
150
+ ```erb
151
+ <!-- app/views/comments/create.turbo_stream.erb -->
152
+ <%= turbo_stream.append "comments", @comment %>
153
+ <%= turbo_stream.replace "comment_form", partial: "comments/form", locals: { comment: Comment.new } %>
154
+ ```
155
+
156
+ ## Examples
157
+
158
+ | Pattern | When | Benefit |
159
+ |---------|------|---------|
160
+ | Scopes | Repeated query logic | Composable, testable queries |
161
+ | Concerns | Shared model behavior | DRY without deep inheritance |
162
+ | Service objects | Multi-step transactions | Testable, single-responsibility |
163
+ | Background jobs | Slow external calls | Non-blocking, retry-safe |
164
+ | Turbo Frames | Inline editing, modals | No JS framework needed |
165
+ | Turbo Streams | Live updates | Real-time without polling |
166
+
167
+ ## Checklist
168
+
169
+ - [ ] Models have no business logic beyond validations, scopes, and associations
170
+ - [ ] Multi-model mutations live in service objects with explicit inputs
171
+ - [ ] Concerns are under 50 lines and represent a single behavior
172
+ - [ ] Background jobs are idempotent — safe to retry
173
+ - [ ] Jobs use `retry_on` with backoff for transient failures
174
+ - [ ] Turbo Frames wrap independently-loadable page sections
175
+ - [ ] Turbo Streams broadcast from `after_commit` (not `after_save`) to avoid race conditions
176
+ - [ ] N+1 queries are caught with `strict_loading` or Bullet gem
177
+ - [ ] Database indexes exist for all foreign keys and columns used in scopes
178
+ - [ ] Controllers stay under 10 lines per action — delegate to services
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: rust-patterns
3
+ description: Rust patterns for ownership, lifetimes, traits, error handling with thiserror/anyhow, async, and serde.
4
+ ---
5
+
6
+ # Rust Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when writing Rust code. Use this skill for managing ownership
11
+ and lifetimes correctly, designing trait-based abstractions, handling errors with
12
+ thiserror (libraries) and anyhow (applications), writing async code with tokio, and
13
+ serializing data with serde.
14
+
15
+ ## How It Works
16
+
17
+ ### Ownership and Borrowing
18
+
19
+ Pass by reference (`&T`) when you only need to read. Pass by `&mut T` when you
20
+ need to modify. Take ownership (`T`) when the function needs to store or consume
21
+ the value. Use `Clone` explicitly rather than fighting the borrow checker.
22
+
23
+ ```rust
24
+ // Borrow for read-only access
25
+ fn word_count(text: &str) -> usize {
26
+ text.split_whitespace().count()
27
+ }
28
+
29
+ // Take ownership when storing
30
+ struct Cache {
31
+ entries: HashMap<String, Vec<u8>>,
32
+ }
33
+
34
+ impl Cache {
35
+ fn insert(&mut self, key: String, value: Vec<u8>) {
36
+ self.entries.insert(key, value);
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### Lifetimes
42
+
43
+ Only annotate lifetimes when the compiler cannot infer them. The most common case
44
+ is a struct that borrows data.
45
+
46
+ ```rust
47
+ struct Parser<'a> {
48
+ input: &'a str,
49
+ pos: usize,
50
+ }
51
+
52
+ impl<'a> Parser<'a> {
53
+ fn new(input: &'a str) -> Self {
54
+ Self { input, pos: 0 }
55
+ }
56
+
57
+ fn remaining(&self) -> &'a str {
58
+ &self.input[self.pos..]
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Traits and Trait Objects
64
+
65
+ Use traits for polymorphism. Prefer generics (`impl Trait`) for static dispatch.
66
+ Use `dyn Trait` for dynamic dispatch when you need heterogeneous collections.
67
+
68
+ ```rust
69
+ trait Renderer {
70
+ fn render(&self, data: &Document) -> String;
71
+ }
72
+
73
+ // Static dispatch (monomorphized, zero-cost)
74
+ fn render_to_file(renderer: &impl Renderer, doc: &Document, path: &Path) -> io::Result<()> {
75
+ let output = renderer.render(doc);
76
+ fs::write(path, output)
77
+ }
78
+
79
+ // Dynamic dispatch (when you need a collection of different types)
80
+ fn render_all(renderers: &[Box<dyn Renderer>], doc: &Document) -> Vec<String> {
81
+ renderers.iter().map(|r| r.render(doc)).collect()
82
+ }
83
+ ```
84
+
85
+ ### Error Handling: thiserror for Libraries
86
+
87
+ Use `thiserror` in library crates to define structured error types. Each variant
88
+ wraps an underlying error or carries context.
89
+
90
+ ```rust
91
+ use thiserror::Error;
92
+
93
+ #[derive(Error, Debug)]
94
+ pub enum StorageError {
95
+ #[error("file not found: {path}")]
96
+ NotFound { path: String },
97
+
98
+ #[error("permission denied: {path}")]
99
+ PermissionDenied { path: String },
100
+
101
+ #[error("IO error")]
102
+ Io(#[from] std::io::Error),
103
+
104
+ #[error("deserialization failed")]
105
+ Parse(#[from] serde_json::Error),
106
+ }
107
+ ```
108
+
109
+ ### Error Handling: anyhow for Applications
110
+
111
+ Use `anyhow` in binaries and CLI tools. Attach context with `.context()`.
112
+
113
+ ```rust
114
+ use anyhow::{Context, Result};
115
+
116
+ fn load_config(path: &Path) -> Result<Config> {
117
+ let content = fs::read_to_string(path)
118
+ .with_context(|| format!("failed to read config from {}", path.display()))?;
119
+
120
+ let config: Config = toml::from_str(&content)
121
+ .context("failed to parse config TOML")?;
122
+
123
+ Ok(config)
124
+ }
125
+ ```
126
+
127
+ ### Async with Tokio
128
+
129
+ Use `tokio::spawn` for concurrent tasks. Use `tokio::select!` for racing futures.
130
+ Always use timeouts on network operations.
131
+
132
+ ```rust
133
+ use tokio::time::{timeout, Duration};
134
+
135
+ async fn fetch_with_timeout(url: &str) -> Result<String> {
136
+ let result = timeout(Duration::from_secs(10), reqwest::get(url))
137
+ .await
138
+ .context("request timed out")?
139
+ .context("request failed")?
140
+ .text()
141
+ .await
142
+ .context("failed to read body")?;
143
+ Ok(result)
144
+ }
145
+
146
+ async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
147
+ let handles: Vec<_> = urls.into_iter()
148
+ .map(|url| tokio::spawn(async move { fetch_with_timeout(&url).await }))
149
+ .collect();
150
+
151
+ let mut results = Vec::new();
152
+ for handle in handles {
153
+ results.push(handle.await.unwrap_or_else(|e| Err(e.into())));
154
+ }
155
+ results
156
+ }
157
+ ```
158
+
159
+ ### Serde
160
+
161
+ Derive `Serialize`/`Deserialize`. Use `#[serde(rename_all = "camelCase")]` for
162
+ JSON APIs. Use `#[serde(default)]` for optional fields with defaults.
163
+
164
+ ```rust
165
+ use serde::{Deserialize, Serialize};
166
+
167
+ #[derive(Debug, Serialize, Deserialize)]
168
+ #[serde(rename_all = "camelCase")]
169
+ struct ApiResponse {
170
+ request_id: String,
171
+ #[serde(default)]
172
+ items: Vec<Item>,
173
+ #[serde(skip_serializing_if = "Option::is_none")]
174
+ next_cursor: Option<String>,
175
+ }
176
+ ```
177
+
178
+ ## Examples
179
+
180
+ **Pattern: Builder for complex structs**
181
+ ```rust
182
+ struct Request {
183
+ url: String,
184
+ timeout: Duration,
185
+ headers: HashMap<String, String>,
186
+ }
187
+
188
+ struct RequestBuilder { inner: Request }
189
+
190
+ impl RequestBuilder {
191
+ fn new(url: impl Into<String>) -> Self {
192
+ Self { inner: Request { url: url.into(), timeout: Duration::from_secs(30), headers: HashMap::new() } }
193
+ }
194
+ fn timeout(mut self, d: Duration) -> Self { self.inner.timeout = d; self }
195
+ fn header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self { self.inner.headers.insert(k.into(), v.into()); self }
196
+ fn build(self) -> Request { self.inner }
197
+ }
198
+ ```
199
+
200
+ ## Checklist
201
+
202
+ - [ ] Borrow (`&T`) by default; take ownership only when storing or consuming
203
+ - [ ] Lifetime annotations only where the compiler requires them
204
+ - [ ] `thiserror` for library error types, `anyhow` for application error handling
205
+ - [ ] Every `?` operator has context attached via `.context()` or descriptive error variant
206
+ - [ ] Traits are small (1-3 methods), defined at the consumer
207
+ - [ ] `impl Trait` for static dispatch, `dyn Trait` only for heterogeneous collections
208
+ - [ ] Async code uses `tokio::timeout` on all network operations
209
+ - [ ] Serde types use `rename_all`, `default`, and `skip_serializing_if`
210
+ - [ ] `clippy` passes with zero warnings (`cargo clippy -- -D warnings`)
211
+ - [ ] Tests use `#[test]` or `#[tokio::test]`, assertions use `assert_eq!`/`assert!`