red64-cli 0.5.0 → 0.6.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.
- package/README.md +64 -58
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +2 -2
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/services/AgentInvoker.js +4 -4
- package/dist/services/AgentInvoker.js.map +1 -1
- package/dist/services/ClaudeHealthCheck.d.ts +5 -0
- package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
- package/dist/services/ClaudeHealthCheck.js +43 -5
- package/dist/services/ClaudeHealthCheck.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/framework/stacks/c/code-quality.md +326 -0
- package/framework/stacks/c/coding-style.md +347 -0
- package/framework/stacks/c/conventions.md +513 -0
- package/framework/stacks/c/error-handling.md +350 -0
- package/framework/stacks/c/feedback.md +158 -0
- package/framework/stacks/c/memory-safety.md +408 -0
- package/framework/stacks/c/tech.md +122 -0
- package/framework/stacks/c/testing.md +472 -0
- package/framework/stacks/cpp/code-quality.md +282 -0
- package/framework/stacks/cpp/coding-style.md +363 -0
- package/framework/stacks/cpp/conventions.md +420 -0
- package/framework/stacks/cpp/error-handling.md +264 -0
- package/framework/stacks/cpp/feedback.md +104 -0
- package/framework/stacks/cpp/memory-safety.md +351 -0
- package/framework/stacks/cpp/tech.md +160 -0
- package/framework/stacks/cpp/testing.md +323 -0
- package/framework/stacks/java/code-quality.md +357 -0
- package/framework/stacks/java/coding-style.md +400 -0
- package/framework/stacks/java/conventions.md +437 -0
- package/framework/stacks/java/error-handling.md +408 -0
- package/framework/stacks/java/feedback.md +180 -0
- package/framework/stacks/java/tech.md +126 -0
- package/framework/stacks/java/testing.md +485 -0
- package/framework/stacks/javascript/async-patterns.md +216 -0
- package/framework/stacks/javascript/code-quality.md +182 -0
- package/framework/stacks/javascript/coding-style.md +293 -0
- package/framework/stacks/javascript/conventions.md +268 -0
- package/framework/stacks/javascript/error-handling.md +216 -0
- package/framework/stacks/javascript/feedback.md +80 -0
- package/framework/stacks/javascript/tech.md +114 -0
- package/framework/stacks/javascript/testing.md +209 -0
- package/framework/stacks/loco/code-quality.md +156 -0
- package/framework/stacks/loco/coding-style.md +247 -0
- package/framework/stacks/loco/error-handling.md +225 -0
- package/framework/stacks/loco/feedback.md +35 -0
- package/framework/stacks/loco/loco.md +342 -0
- package/framework/stacks/loco/structure.md +193 -0
- package/framework/stacks/loco/tech.md +129 -0
- package/framework/stacks/loco/testing.md +211 -0
- package/framework/stacks/rust/code-quality.md +370 -0
- package/framework/stacks/rust/coding-style.md +475 -0
- package/framework/stacks/rust/conventions.md +430 -0
- package/framework/stacks/rust/error-handling.md +399 -0
- package/framework/stacks/rust/feedback.md +152 -0
- package/framework/stacks/rust/memory-safety.md +398 -0
- package/framework/stacks/rust/tech.md +121 -0
- package/framework/stacks/rust/testing.md +528 -0
- package/package.json +14 -2
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# Rust Coding Style
|
|
2
|
+
|
|
3
|
+
Coding style conventions for idiomatic, expressive Rust. Opinionated patterns that go beyond what `cargo fmt` enforces automatically.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Leverage the type system**: Encode invariants in types, not runtime checks
|
|
10
|
+
- **Iterator chains over loops**: Functional composition is clearer and often faster
|
|
11
|
+
- **Clippy is law**: If Clippy warns, fix it or explicitly allow with justification
|
|
12
|
+
- **Explicitness over cleverness**: Prefer readable code; the compiler optimizes for you
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Naming Conventions
|
|
17
|
+
|
|
18
|
+
### Standard Rust Naming
|
|
19
|
+
|
|
20
|
+
| Element | Convention | Example |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| Variables, functions | `snake_case` | `user_count`, `get_user` |
|
|
23
|
+
| Types, traits, enums | `PascalCase` | `UserService`, `Serialize` |
|
|
24
|
+
| Constants, statics | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_PORT` |
|
|
25
|
+
| Modules, crates | `snake_case` | `user_service`, `auth_utils` |
|
|
26
|
+
| Type parameters | Single uppercase or `PascalCase` | `T`, `E`, `Item` |
|
|
27
|
+
| Lifetimes | Short lowercase with `'` | `'a`, `'ctx`, `'conn` |
|
|
28
|
+
| Macros | `snake_case!` | `vec!`, `println!`, `query!` |
|
|
29
|
+
| Feature flags | `kebab-case` | `full`, `serde-support` |
|
|
30
|
+
|
|
31
|
+
### Naming Rules
|
|
32
|
+
|
|
33
|
+
```rust
|
|
34
|
+
// GOOD: Descriptive, reveals intent
|
|
35
|
+
let active_user_count = users.iter().filter(|u| u.is_active).count();
|
|
36
|
+
let is_authenticated = token.is_some();
|
|
37
|
+
const MAX_RETRY_ATTEMPTS: u32 = 3;
|
|
38
|
+
|
|
39
|
+
fn find_user_by_email(email: &str) -> Option<User> {
|
|
40
|
+
// ...
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
struct PaymentProcessingError;
|
|
44
|
+
|
|
45
|
+
// BAD: Abbreviated, unclear
|
|
46
|
+
let uc = users.iter().filter(|u| u.is_active).count();
|
|
47
|
+
let auth = token.is_some();
|
|
48
|
+
const N: u32 = 3;
|
|
49
|
+
|
|
50
|
+
fn find_u(e: &str) -> Option<User> {
|
|
51
|
+
// ...
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Boolean Naming
|
|
56
|
+
|
|
57
|
+
Prefix with `is_`, `has_`, `can_`, `should_`:
|
|
58
|
+
|
|
59
|
+
```rust
|
|
60
|
+
struct User {
|
|
61
|
+
is_active: bool,
|
|
62
|
+
has_verified_email: bool,
|
|
63
|
+
can_publish: bool,
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Conversion Method Naming
|
|
68
|
+
|
|
69
|
+
Follow the standard library conventions:
|
|
70
|
+
|
|
71
|
+
| Pattern | Ownership | Example |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `as_*` | Borrowed -> borrowed (cheap) | `as_str()`, `as_bytes()` |
|
|
74
|
+
| `to_*` | Borrowed -> owned (expensive) | `to_string()`, `to_vec()` |
|
|
75
|
+
| `into_*` | Owned -> owned (consuming) | `into_inner()`, `into_bytes()` |
|
|
76
|
+
| `from_*` | Associated function constructor | `from_str()`, `from_parts()` |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Function Design
|
|
81
|
+
|
|
82
|
+
### Size Limits
|
|
83
|
+
|
|
84
|
+
- **Target**: Under 25 lines of logic
|
|
85
|
+
- **Maximum**: 50 lines (extract if longer)
|
|
86
|
+
- **Parameters**: Maximum 5; use a builder or config struct for more
|
|
87
|
+
|
|
88
|
+
```rust
|
|
89
|
+
// GOOD: Small, focused
|
|
90
|
+
async fn create_user(pool: &PgPool, data: &CreateUserRequest) -> Result<User> {
|
|
91
|
+
validate_email_available(pool, &data.email).await?;
|
|
92
|
+
let password_hash = hash_password(&data.password)?;
|
|
93
|
+
let user = sqlx::query_as!(
|
|
94
|
+
User,
|
|
95
|
+
"INSERT INTO users (email, name, password_hash) VALUES ($1, $2, $3) RETURNING *",
|
|
96
|
+
data.email,
|
|
97
|
+
data.name,
|
|
98
|
+
password_hash,
|
|
99
|
+
)
|
|
100
|
+
.fetch_one(pool)
|
|
101
|
+
.await?;
|
|
102
|
+
Ok(user)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// BAD: Too many parameters, too many responsibilities
|
|
106
|
+
async fn create_user(
|
|
107
|
+
pool: &PgPool,
|
|
108
|
+
email: &str,
|
|
109
|
+
name: &str,
|
|
110
|
+
password: &str,
|
|
111
|
+
role: &str,
|
|
112
|
+
bio: Option<&str>,
|
|
113
|
+
avatar_url: Option<&str>,
|
|
114
|
+
send_email: bool,
|
|
115
|
+
) -> Result<User> {
|
|
116
|
+
// 80+ lines doing validation, hashing, saving, emailing...
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Prefer `&str` Over `String` in Parameters
|
|
121
|
+
|
|
122
|
+
```rust
|
|
123
|
+
// GOOD: Accepts both &str and &String
|
|
124
|
+
fn validate_email(email: &str) -> bool {
|
|
125
|
+
email.contains('@')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// BAD: Forces caller to allocate
|
|
129
|
+
fn validate_email(email: String) -> bool {
|
|
130
|
+
email.contains('@')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// GOOD: Use String when ownership is needed
|
|
134
|
+
fn set_name(&mut self, name: String) {
|
|
135
|
+
self.name = name;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Early Returns with `?`
|
|
140
|
+
|
|
141
|
+
```rust
|
|
142
|
+
// GOOD: Flat with ? operator
|
|
143
|
+
async fn publish_post(pool: &PgPool, post_id: i64, user_id: i64) -> Result<Post> {
|
|
144
|
+
let post = get_post(pool, post_id).await?.ok_or(AppError::NotFound("Post"))?;
|
|
145
|
+
if post.user_id != user_id {
|
|
146
|
+
return Err(AppError::Forbidden("Cannot publish another user's post"));
|
|
147
|
+
}
|
|
148
|
+
if post.status == PostStatus::Published {
|
|
149
|
+
return Err(AppError::Conflict("Post is already published"));
|
|
150
|
+
}
|
|
151
|
+
update_post_status(pool, post_id, PostStatus::Published).await
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// BAD: Deeply nested
|
|
155
|
+
async fn publish_post(pool: &PgPool, post_id: i64, user_id: i64) -> Result<Post> {
|
|
156
|
+
if let Some(post) = get_post(pool, post_id).await? {
|
|
157
|
+
if post.user_id == user_id {
|
|
158
|
+
if post.status != PostStatus::Published {
|
|
159
|
+
update_post_status(pool, post_id, PostStatus::Published).await
|
|
160
|
+
} else {
|
|
161
|
+
Err(AppError::Conflict("Already published"))
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
Err(AppError::Forbidden("Not your post"))
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
Err(AppError::NotFound("Post"))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Iterator Chains Over Loops
|
|
175
|
+
|
|
176
|
+
### Prefer Functional Style
|
|
177
|
+
|
|
178
|
+
```rust
|
|
179
|
+
// GOOD: Iterator chain
|
|
180
|
+
let active_emails: Vec<String> = users
|
|
181
|
+
.iter()
|
|
182
|
+
.filter(|u| u.is_active)
|
|
183
|
+
.map(|u| u.email.clone())
|
|
184
|
+
.collect();
|
|
185
|
+
|
|
186
|
+
// BAD: Imperative loop
|
|
187
|
+
let mut active_emails = Vec::new();
|
|
188
|
+
for user in &users {
|
|
189
|
+
if user.is_active {
|
|
190
|
+
active_emails.push(user.email.clone());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// GOOD: Complex chain with intermediate operations
|
|
195
|
+
let report: Vec<CategoryReport> = orders
|
|
196
|
+
.iter()
|
|
197
|
+
.filter(|o| o.status == OrderStatus::Completed)
|
|
198
|
+
.fold(HashMap::new(), |mut acc, o| {
|
|
199
|
+
*acc.entry(&o.category).or_insert(Decimal::ZERO) += o.amount;
|
|
200
|
+
acc
|
|
201
|
+
})
|
|
202
|
+
.into_iter()
|
|
203
|
+
.map(|(category, total)| CategoryReport { category: category.clone(), total })
|
|
204
|
+
.collect();
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### When Loops Are Fine
|
|
208
|
+
|
|
209
|
+
Use loops when the body has complex control flow (early returns, multiple mutations, error handling with side effects):
|
|
210
|
+
|
|
211
|
+
```rust
|
|
212
|
+
// Loop is clearer here due to error handling with context
|
|
213
|
+
for (i, item) in items.iter().enumerate() {
|
|
214
|
+
match process_item(item).await {
|
|
215
|
+
Ok(result) => results.push(result),
|
|
216
|
+
Err(e) => {
|
|
217
|
+
tracing::warn!(index = i, error = %e, "Skipping failed item");
|
|
218
|
+
failures.push((i, e));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Type State Pattern
|
|
227
|
+
|
|
228
|
+
Encode state transitions in the type system so invalid states are unrepresentable:
|
|
229
|
+
|
|
230
|
+
```rust
|
|
231
|
+
// GOOD: Type state pattern
|
|
232
|
+
use std::marker::PhantomData;
|
|
233
|
+
|
|
234
|
+
struct Order<S: OrderState> {
|
|
235
|
+
id: i64,
|
|
236
|
+
items: Vec<OrderItem>,
|
|
237
|
+
_state: PhantomData<S>,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
struct Draft;
|
|
241
|
+
struct Confirmed;
|
|
242
|
+
struct Shipped;
|
|
243
|
+
|
|
244
|
+
trait OrderState {}
|
|
245
|
+
impl OrderState for Draft {}
|
|
246
|
+
impl OrderState for Confirmed {}
|
|
247
|
+
impl OrderState for Shipped {}
|
|
248
|
+
|
|
249
|
+
impl Order<Draft> {
|
|
250
|
+
fn confirm(self, payment: Payment) -> Result<Order<Confirmed>> {
|
|
251
|
+
// Only drafts can be confirmed
|
|
252
|
+
Ok(Order {
|
|
253
|
+
id: self.id,
|
|
254
|
+
items: self.items,
|
|
255
|
+
_state: PhantomData,
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
impl Order<Confirmed> {
|
|
261
|
+
fn ship(self, tracking: &str) -> Order<Shipped> {
|
|
262
|
+
// Only confirmed orders can be shipped
|
|
263
|
+
Order {
|
|
264
|
+
id: self.id,
|
|
265
|
+
items: self.items,
|
|
266
|
+
_state: PhantomData,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Compile error: cannot ship a draft
|
|
272
|
+
// let shipped = draft_order.ship("TRACK123"); // ERROR: method not found
|
|
273
|
+
|
|
274
|
+
// BAD: Runtime state check
|
|
275
|
+
struct OrderBad {
|
|
276
|
+
status: OrderStatus,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
impl OrderBad {
|
|
280
|
+
fn ship(&mut self, tracking: &str) -> Result<()> {
|
|
281
|
+
if self.status != OrderStatus::Confirmed {
|
|
282
|
+
return Err(anyhow!("Cannot ship order in {:?} state", self.status));
|
|
283
|
+
}
|
|
284
|
+
self.status = OrderStatus::Shipped;
|
|
285
|
+
Ok(())
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Builder Pattern
|
|
293
|
+
|
|
294
|
+
Use builders for constructing complex types with many optional fields:
|
|
295
|
+
|
|
296
|
+
```rust
|
|
297
|
+
// GOOD: Builder pattern
|
|
298
|
+
#[derive(Default)]
|
|
299
|
+
struct QueryBuilder {
|
|
300
|
+
filter: Option<String>,
|
|
301
|
+
sort_by: Option<String>,
|
|
302
|
+
page: u32,
|
|
303
|
+
per_page: u32,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
impl QueryBuilder {
|
|
307
|
+
fn new() -> Self {
|
|
308
|
+
Self {
|
|
309
|
+
page: 1,
|
|
310
|
+
per_page: 20,
|
|
311
|
+
..Default::default()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
fn filter(mut self, filter: impl Into<String>) -> Self {
|
|
316
|
+
self.filter = Some(filter.into());
|
|
317
|
+
self
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn sort_by(mut self, field: impl Into<String>) -> Self {
|
|
321
|
+
self.sort_by = Some(field.into());
|
|
322
|
+
self
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
fn page(mut self, page: u32) -> Self {
|
|
326
|
+
self.page = page;
|
|
327
|
+
self
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fn build(self) -> Query {
|
|
331
|
+
Query {
|
|
332
|
+
filter: self.filter,
|
|
333
|
+
sort_by: self.sort_by,
|
|
334
|
+
page: self.page,
|
|
335
|
+
per_page: self.per_page,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Usage
|
|
341
|
+
let query = QueryBuilder::new()
|
|
342
|
+
.filter("active")
|
|
343
|
+
.sort_by("created_at")
|
|
344
|
+
.page(2)
|
|
345
|
+
.build();
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Newtype Pattern
|
|
351
|
+
|
|
352
|
+
Wrap primitive types to add type safety and domain meaning:
|
|
353
|
+
|
|
354
|
+
```rust
|
|
355
|
+
// GOOD: Newtype prevents mixing up IDs
|
|
356
|
+
struct UserId(i64);
|
|
357
|
+
struct PostId(i64);
|
|
358
|
+
|
|
359
|
+
fn get_post(user_id: UserId, post_id: PostId) -> Result<Post> {
|
|
360
|
+
// Cannot accidentally swap user_id and post_id
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// BAD: Easy to mix up bare i64 parameters
|
|
364
|
+
fn get_post_bad(user_id: i64, post_id: i64) -> Result<Post> {
|
|
365
|
+
// Caller might swap arguments -- compiles fine, fails at runtime
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Newtype with validation
|
|
369
|
+
struct Email(String);
|
|
370
|
+
|
|
371
|
+
impl Email {
|
|
372
|
+
fn new(value: impl Into<String>) -> Result<Self> {
|
|
373
|
+
let value = value.into();
|
|
374
|
+
if !value.contains('@') {
|
|
375
|
+
return Err(anyhow!("Invalid email: {}", value));
|
|
376
|
+
}
|
|
377
|
+
Ok(Self(value))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
fn as_str(&self) -> &str {
|
|
381
|
+
&self.0
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Turbofish Syntax
|
|
389
|
+
|
|
390
|
+
Use turbofish (`::<Type>`) when the compiler cannot infer types:
|
|
391
|
+
|
|
392
|
+
```rust
|
|
393
|
+
// GOOD: Turbofish for parse
|
|
394
|
+
let port = "8080".parse::<u16>()?;
|
|
395
|
+
|
|
396
|
+
// GOOD: Turbofish for collect
|
|
397
|
+
let ids = raw_ids.iter().map(|s| s.parse::<i64>()).collect::<Result<Vec<_>, _>>()?;
|
|
398
|
+
|
|
399
|
+
// GOOD: No turbofish needed -- type annotation on binding
|
|
400
|
+
let port: u16 = "8080".parse()?;
|
|
401
|
+
|
|
402
|
+
// BAD: Ambiguous without turbofish or annotation
|
|
403
|
+
// let port = "8080".parse()?; // ERROR: cannot infer type
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Module Organization
|
|
409
|
+
|
|
410
|
+
```rust
|
|
411
|
+
// GOOD: Flat, explicit re-exports
|
|
412
|
+
// src/models/mod.rs
|
|
413
|
+
mod user;
|
|
414
|
+
mod post;
|
|
415
|
+
mod comment;
|
|
416
|
+
|
|
417
|
+
pub use user::User;
|
|
418
|
+
pub use post::Post;
|
|
419
|
+
pub use comment::Comment;
|
|
420
|
+
|
|
421
|
+
// GOOD: File-based modules (Rust 2024 preferred)
|
|
422
|
+
// src/models.rs (instead of src/models/mod.rs for simple cases)
|
|
423
|
+
|
|
424
|
+
// BAD: Deep nesting with re-exports at every level
|
|
425
|
+
// src/domain/models/entities/user/mod.rs
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## File and Type Size
|
|
431
|
+
|
|
432
|
+
### Guidelines
|
|
433
|
+
|
|
434
|
+
| Element | Guideline |
|
|
435
|
+
|---|---|
|
|
436
|
+
| Function | Under 25 lines of logic, max 50 |
|
|
437
|
+
| Struct impl block | Under 200 lines, max 300 |
|
|
438
|
+
| Module file | Under 300 lines, max 500 |
|
|
439
|
+
| Parameters | Max 5 per function; use struct for more |
|
|
440
|
+
|
|
441
|
+
When a file exceeds limits, extract:
|
|
442
|
+
- Helper functions into a submodule
|
|
443
|
+
- Trait implementations into separate impl blocks
|
|
444
|
+
- Related types into their own module
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Anti-Patterns
|
|
449
|
+
|
|
450
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
451
|
+
|---|---|---|
|
|
452
|
+
| `.unwrap()` in production | Panics on `None`/`Err` | Use `?`, `.ok_or()`, `.unwrap_or_default()` |
|
|
453
|
+
| `clone()` everywhere | Hidden performance cost | Borrow with `&` or use `Arc` for shared ownership |
|
|
454
|
+
| Stringly-typed APIs | No compile-time safety | Use enums or newtype pattern |
|
|
455
|
+
| Massive match arms | Hard to read, easy to miss cases | Extract into methods, use trait dispatch |
|
|
456
|
+
| `pub` on everything | Leaks implementation details | Default to `pub(crate)`, expose minimal API |
|
|
457
|
+
| Ignoring Clippy lints | Misses idiomatic patterns | Run `cargo clippy -- -D warnings` in CI |
|
|
458
|
+
| `Box<dyn Error>` in libraries | Loses type info for callers | Use `thiserror` custom error enums |
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Formatting (Handled by `cargo fmt`)
|
|
463
|
+
|
|
464
|
+
These are automated -- do not worry about them manually:
|
|
465
|
+
|
|
466
|
+
- Line length: 100 characters (configurable in `rustfmt.toml`)
|
|
467
|
+
- Indentation: 4 spaces
|
|
468
|
+
- Trailing commas in multi-line constructs
|
|
469
|
+
- Brace style: same line for functions, next line for control flow only if multi-line
|
|
470
|
+
|
|
471
|
+
Run `cargo fmt` and move on.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
_The Rust compiler is your strictest reviewer. Lean into its type system to make illegal states unrepresentable, and let Clippy handle the rest._
|