oxlsx 0.1.0__tar.gz

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 (95) hide show
  1. oxlsx-0.1.0/.agents/skills/rust-coding/SKILL.md +440 -0
  2. oxlsx-0.1.0/.agents/skills/rust-coding/SKILL_EN.md +88 -0
  3. oxlsx-0.1.0/.agents/skills/rust-coding/SKILL_ZH.md +236 -0
  4. oxlsx-0.1.0/.agents/skills/rust-coding/agents/openai.yaml +4 -0
  5. oxlsx-0.1.0/.agents/skills/rust-performance/SKILL.md +382 -0
  6. oxlsx-0.1.0/.agents/skills/rust-performance/SKILL_EN.md +64 -0
  7. oxlsx-0.1.0/.agents/skills/rust-performance/SKILL_ZH.md +351 -0
  8. oxlsx-0.1.0/.agents/skills/rust-performance/agents/openai.yaml +4 -0
  9. oxlsx-0.1.0/.agents/skills/rust-type-driven/SKILL.md +431 -0
  10. oxlsx-0.1.0/.agents/skills/rust-type-driven/SKILL_EN.md +59 -0
  11. oxlsx-0.1.0/.agents/skills/rust-type-driven/SKILL_ZH.md +205 -0
  12. oxlsx-0.1.0/.agents/skills/rust-type-driven/agents/openai.yaml +4 -0
  13. oxlsx-0.1.0/.github/workflows/benchmarks.yml +74 -0
  14. oxlsx-0.1.0/.github/workflows/ci.yml +39 -0
  15. oxlsx-0.1.0/.github/workflows/opencode.yml +33 -0
  16. oxlsx-0.1.0/.github/workflows/release.yml +169 -0
  17. oxlsx-0.1.0/.gitignore +14 -0
  18. oxlsx-0.1.0/AGENTS.md +92 -0
  19. oxlsx-0.1.0/Cargo.lock +1174 -0
  20. oxlsx-0.1.0/Cargo.toml +46 -0
  21. oxlsx-0.1.0/LICENSE-APACHE +188 -0
  22. oxlsx-0.1.0/LICENSE-MIT +21 -0
  23. oxlsx-0.1.0/PKG-INFO +277 -0
  24. oxlsx-0.1.0/README.md +251 -0
  25. oxlsx-0.1.0/SPEC.md +162 -0
  26. oxlsx-0.1.0/benches/openpyxl_comparison.py +135 -0
  27. oxlsx-0.1.0/benches/read_benchmarks.rs +123 -0
  28. oxlsx-0.1.0/benches/write_benchmarks.rs +92 -0
  29. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase12b-comments/design.md +121 -0
  30. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase12b-comments/proposal.md +62 -0
  31. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase12b-comments/specs/worksheet-comments/spec.md +61 -0
  32. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase12b-comments/tasks.md +55 -0
  33. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase13-freeze-panes/tasks.md +41 -0
  34. oxlsx-0.1.0/openspec/changes/archive/2026-06-07-oxlsx-phase13-print-settings/tasks.md +39 -0
  35. oxlsx-0.1.0/openspec/changes/archive/2026-06-13-oxlsx-phase13-tables/design.md +70 -0
  36. oxlsx-0.1.0/openspec/changes/archive/2026-06-13-oxlsx-phase13-tables/proposal.md +69 -0
  37. oxlsx-0.1.0/openspec/changes/archive/2026-06-13-oxlsx-phase13-tables/specs/worksheet-tables/spec.md +109 -0
  38. oxlsx-0.1.0/openspec/changes/archive/2026-06-13-oxlsx-phase13-tables/tasks.md +59 -0
  39. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-phase12a-hyperlinks/design.md +88 -0
  40. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-phase12a-hyperlinks/proposal.md +70 -0
  41. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-phase12a-hyperlinks/specs/worksheet-hyperlinks/spec.md +111 -0
  42. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-phase12a-hyperlinks/tasks.md +141 -0
  43. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-table-lossless-rmw/design.md +99 -0
  44. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-table-lossless-rmw/proposal.md +76 -0
  45. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-table-lossless-rmw/specs/worksheet-tables/spec.md +87 -0
  46. oxlsx-0.1.0/openspec/changes/archive/2026-06-14-oxlsx-table-lossless-rmw/tasks.md +79 -0
  47. oxlsx-0.1.0/openspec/changes/oxlsx-phase4-write/tasks.md +54 -0
  48. oxlsx-0.1.0/openspec/specs/worksheet-comments/spec.md +55 -0
  49. oxlsx-0.1.0/openspec/specs/worksheet-hyperlinks/spec.md +111 -0
  50. oxlsx-0.1.0/openspec/specs/worksheet-print-settings/spec.md +101 -0
  51. oxlsx-0.1.0/openspec/specs/worksheet-tables/spec.md +166 -0
  52. oxlsx-0.1.0/pyproject.toml +37 -0
  53. oxlsx-0.1.0/samples/.DS_Store +0 -0
  54. oxlsx-0.1.0/samples/.atl/.skill-registry.cache.json +3 -0
  55. oxlsx-0.1.0/samples/.atl/skill-registry.md +118 -0
  56. oxlsx-0.1.0/samples/bench_10k.xlsx +0 -0
  57. oxlsx-0.1.0/samples/comments.xlsx +0 -0
  58. oxlsx-0.1.0/samples/hyperlinks.xlsx +0 -0
  59. oxlsx-0.1.0/samples/inline_strings_no_sst.xlsx +0 -0
  60. oxlsx-0.1.0/samples/multi_sheet.xlsx +0 -0
  61. oxlsx-0.1.0/samples/tables.xlsx +0 -0
  62. oxlsx-0.1.0/samples/tables_totals.xlsx +0 -0
  63. oxlsx-0.1.0/samples/test.xlsx +0 -0
  64. oxlsx-0.1.0/samples/unzipped/[Content_Types].xml +2 -0
  65. oxlsx-0.1.0/samples/unzipped/_rels/.rels +1 -0
  66. oxlsx-0.1.0/samples/unzipped/docProps/app.xml +1 -0
  67. oxlsx-0.1.0/samples/unzipped/docProps/core.xml +1 -0
  68. oxlsx-0.1.0/samples/unzipped/xl/_rels/workbook.xml.rels +1 -0
  69. oxlsx-0.1.0/samples/unzipped/xl/jsaProject.bin +1 -0
  70. oxlsx-0.1.0/samples/unzipped/xl/sharedStrings.xml +1 -0
  71. oxlsx-0.1.0/samples/unzipped/xl/styles.xml +1 -0
  72. oxlsx-0.1.0/samples/unzipped/xl/theme/theme.xml +1 -0
  73. oxlsx-0.1.0/samples/unzipped/xl/theme/theme1.xml +1 -0
  74. oxlsx-0.1.0/samples/unzipped/xl/workbook.xml +1 -0
  75. oxlsx-0.1.0/samples/unzipped/xl/worksheets/sheet1.xml +1 -0
  76. oxlsx-0.1.0/skills-lock.json +23 -0
  77. oxlsx-0.1.0/src/comments.rs +120 -0
  78. oxlsx-0.1.0/src/coords.rs +153 -0
  79. oxlsx-0.1.0/src/defined_names.rs +837 -0
  80. oxlsx-0.1.0/src/error.rs +32 -0
  81. oxlsx-0.1.0/src/hyperlink.rs +71 -0
  82. oxlsx-0.1.0/src/lib.rs +27 -0
  83. oxlsx-0.1.0/src/main.rs +3 -0
  84. oxlsx-0.1.0/src/python.rs +1511 -0
  85. oxlsx-0.1.0/src/reader.rs +1909 -0
  86. oxlsx-0.1.0/src/readonly.rs +560 -0
  87. oxlsx-0.1.0/src/shared_strings.rs +132 -0
  88. oxlsx-0.1.0/src/sheet_rels.rs +730 -0
  89. oxlsx-0.1.0/src/styles.rs +384 -0
  90. oxlsx-0.1.0/src/table.rs +541 -0
  91. oxlsx-0.1.0/src/workbook_rels.rs +161 -0
  92. oxlsx-0.1.0/src/writer.rs +1971 -0
  93. oxlsx-0.1.0/src/xml.rs +19 -0
  94. oxlsx-0.1.0/tests/integration_test.rs +2651 -0
  95. oxlsx-0.1.0/tests/test_python.py +1146 -0
@@ -0,0 +1,440 @@
1
+ ---
2
+ name: rust-coding
3
+ description: Rust coding conventions expert covering naming, formatting, comments, clippy, rustfmt, lints, code style, best practices, and idiomatic patterns.
4
+ metadata:
5
+ triggers:
6
+ - coding convention
7
+ - naming
8
+ - formatting
9
+ - clippy
10
+ - rustfmt
11
+ - lint
12
+ - code style
13
+ - best practice
14
+ - idiomatic
15
+ - code review
16
+ ---
17
+
18
+
19
+ ## Naming Conventions (Rust-Specific)
20
+
21
+ | Rule | Correct | Incorrect |
22
+ |------|---------|-----------|
23
+ | No `get_` prefix for methods | `fn name(&self)` | `fn get_name(&self)` |
24
+ | Iterator methods | `iter()` / `iter_mut()` / `into_iter()` | `get_iter()` |
25
+ | Conversion naming | `as_` (cheap), `to_` (expensive), `into_` (ownership) | Mixed usage |
26
+ | `static` variables uppercase | `static CONFIG: Config` | `static config: Config` |
27
+ | `const` variables | `const BUFFER_SIZE: usize = 1024` | No restriction |
28
+
29
+ ### General Naming
30
+
31
+ ```rust
32
+ // Variables and functions: snake_case
33
+ let max_connections = 100;
34
+ fn process_data() { ... }
35
+
36
+ // Types and traits: CamelCase
37
+ struct UserSession;
38
+ trait Cacheable {}
39
+
40
+ // Constants: SCREAMING_SNAKE_CASE
41
+ const MAX_CONNECTIONS: usize = 100;
42
+ static CONFIG: once_cell::sync::Lazy<Config> = ...
43
+ ```
44
+
45
+
46
+ ## Solution Patterns
47
+
48
+ ### Pattern 1: Conversion Methods
49
+
50
+ ```rust
51
+ impl Buffer {
52
+ // as_ - cheap, view conversion
53
+ pub fn as_slice(&self) -> &[u8] {
54
+ &self.data
55
+ }
56
+
57
+ // to_ - expensive, allocating conversion
58
+ pub fn to_vec(&self) -> Vec<u8> {
59
+ self.data.clone()
60
+ }
61
+
62
+ // into_ - consuming, ownership transfer
63
+ pub fn into_vec(self) -> Vec<u8> {
64
+ self.data
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### Pattern 2: Newtype Pattern
70
+
71
+ ```rust
72
+ // ✅ Domain semantics with newtypes
73
+ struct Email(String);
74
+ struct UserId(u64);
75
+ struct Meters(f64);
76
+
77
+ impl Email {
78
+ pub fn new(s: impl Into<String>) -> Result<Self, EmailError> {
79
+ let email = s.into();
80
+ if email.contains('@') {
81
+ Ok(Self(email))
82
+ } else {
83
+ Err(EmailError::Invalid)
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### Pattern 3: Error Handling
90
+
91
+ ```rust
92
+ // ✅ Good: propagate errors
93
+ fn read_config() -> Result<Config, ConfigError> {
94
+ let content = std::fs::read_to_string("config.toml")
95
+ .map_err(ConfigError::from)?;
96
+ toml::from_str(&content)
97
+ .map_err(ConfigError::Parse)
98
+ }
99
+
100
+ // ❌ Avoid: panic in library code
101
+ fn read_config() -> Config {
102
+ std::fs::read_to_string("config.toml").unwrap() // panic!
103
+ }
104
+
105
+ // ✅ Use expect when invariant guaranteed
106
+ fn get_user(&self) -> &User {
107
+ self.user.as_ref()
108
+ .expect("user always initialized in constructor")
109
+ }
110
+ ```
111
+
112
+ ### Pattern 4: String Handling
113
+
114
+ ```rust
115
+ // ✅ Accept &str in APIs
116
+ fn greet(name: &str) {
117
+ println!("Hello, {}", name);
118
+ }
119
+
120
+ // ✅ Use Cow when might need owned
121
+ use std::borrow::Cow;
122
+
123
+ fn process(input: &str) -> Cow<str> {
124
+ if input.contains("special") {
125
+ Cow::Owned(input.replace("special", "normal"))
126
+ } else {
127
+ Cow::Borrowed(input)
128
+ }
129
+ }
130
+
131
+ // ✅ Pre-allocate when size known
132
+ let mut s = String::with_capacity(100);
133
+ ```
134
+
135
+
136
+ ## Data Type Guidelines
137
+
138
+ | Rule | Description | Example |
139
+ |------|-------------|---------|
140
+ | Use newtype | Domain semantics | `struct Email(String)` |
141
+ | Use slice patterns | Pattern matching | `if let [first, .., last] = slice` |
142
+ | Pre-allocate | Avoid reallocations | `Vec::with_capacity()` |
143
+ | Avoid Vec abuse | Fixed size → array | `let arr: [u8; 256]` |
144
+
145
+ ### String Guidelines
146
+
147
+ | Rule | Description |
148
+ |------|-------------|
149
+ | ASCII data use `bytes()` | `s.bytes()` faster than `s.chars()` |
150
+ | Might modify → `Cow<str>` | Borrow or owned |
151
+ | Use `format!` for concat | Better than `+` operator |
152
+ | Avoid nested `contains()` | O(n*m) complexity |
153
+
154
+
155
+ ## Error Handling Guidelines
156
+
157
+ | Rule | Description |
158
+ |------|-------------|
159
+ | Use `?` to propagate | Don't use `try!()` macro |
160
+ | `expect()` over `unwrap()` | When value guaranteed |
161
+ | Use `assert!` for invariants | At function entry |
162
+
163
+
164
+ ## Memory and Lifetimes
165
+
166
+ | Rule | Description |
167
+ |------|-------------|
168
+ | Meaningful lifetime names | `'src`, `'ctx` not just `'a` |
169
+ | `RefCell` use `try_borrow` | Avoid panics |
170
+ | Use shadowing for conversions | `let x = x.parse()?` |
171
+
172
+
173
+ ## Concurrency Guidelines
174
+
175
+ | Rule | Description |
176
+ |------|-------------|
177
+ | Define lock ordering | Prevent deadlocks |
178
+ | Atomics for primitives | Not `Mutex<bool>` |
179
+ | Choose memory ordering carefully | Relaxed/Acquire/Release/SeqCst |
180
+
181
+
182
+ ## Async Guidelines
183
+
184
+ | Rule | Description |
185
+ |------|-------------|
186
+ | CPU-bound → sync | Async for I/O |
187
+ | Don't hold locks across await | Use scoped guards |
188
+
189
+
190
+ ## Macro Guidelines
191
+
192
+ | Rule | Description |
193
+ |------|-------------|
194
+ | Avoid macros (unless necessary) | Prefer functions/generics |
195
+ | Macro input like Rust | Readability first |
196
+
197
+
198
+ ## Deprecated Patterns → Modern
199
+
200
+ | Deprecated | Modern | Version |
201
+ |-----------|---------|---------|
202
+ | `lazy_static!` | `std::sync::OnceLock` | 1.70 |
203
+ | `once_cell::Lazy` | `std::sync::LazyLock` | 1.80 |
204
+ | `std::sync::mpsc` | `crossbeam::channel` | - |
205
+ | `std::sync::Mutex` | `parking_lot::Mutex` | - |
206
+ | `failure`/`error-chain` | `thiserror`/`anyhow` | - |
207
+ | `try!()` | `?` operator | 2018 |
208
+
209
+
210
+ ## Clippy Configuration
211
+
212
+ ```toml
213
+ [package]
214
+ edition = "2024"
215
+ rust-version = "1.85"
216
+
217
+ [lints.rust]
218
+ unsafe_code = "warn"
219
+
220
+ [lints.clippy]
221
+ all = "warn"
222
+ pedantic = "warn"
223
+ ```
224
+
225
+ ### Common Clippy Lints
226
+
227
+ | Lint | Description |
228
+ |------|-------------|
229
+ | `clippy::all` | Enable all warnings |
230
+ | `clippy::pedantic` | Stricter checks |
231
+ | `clippy::unwrap_used` | Avoid unwrap |
232
+ | `clippy::expect_used` | Prefer expect |
233
+ | `clippy::clone_on_ref_ptr` | Avoid cloning Arc |
234
+
235
+
236
+ ## Formatting (rustfmt)
237
+
238
+ ```bash
239
+ # Use default config
240
+ rustfmt src/lib.rs
241
+
242
+ # Check formatting
243
+ rustfmt --check src/lib.rs
244
+
245
+ # Config file: .rustfmt.toml
246
+ max_width = 100
247
+ tab_spaces = 4
248
+ edition = "2024"
249
+ ```
250
+
251
+
252
+ ## Documentation Guidelines
253
+
254
+ ```rust
255
+ /// Module documentation
256
+ //! This module handles user authentication...
257
+
258
+ /// Struct documentation
259
+ ///
260
+ /// # Examples
261
+ /// ```
262
+ /// let user = User::new("name");
263
+ /// ```
264
+ pub struct User { ... }
265
+
266
+ /// Method documentation
267
+ ///
268
+ /// # Arguments
269
+ ///
270
+ /// * `name` - User name
271
+ ///
272
+ /// # Returns
273
+ ///
274
+ /// Initialized user instance
275
+ ///
276
+ /// # Panics
277
+ ///
278
+ /// Panics when name is empty
279
+ pub fn new(name: &str) -> Self { ... }
280
+ ```
281
+
282
+
283
+ ## Workflow
284
+
285
+ ### Step 1: Name Things Properly
286
+
287
+ ```
288
+ Choosing a name?
289
+ → Function/variable? snake_case
290
+ → Type/trait? CamelCase
291
+ → Constant? SCREAMING_SNAKE_CASE
292
+ → Conversion method?
293
+ - Cheap view? as_foo()
294
+ - Expensive? to_foo()
295
+ - Consuming? into_foo()
296
+ ```
297
+
298
+ ### Step 2: Format Code
299
+
300
+ ```bash
301
+ # Run rustfmt
302
+ cargo fmt
303
+
304
+ # Check formatting in CI
305
+ cargo fmt --check
306
+
307
+ # Fix clippy warnings
308
+ cargo clippy --fix
309
+ ```
310
+
311
+ ### Step 3: Review Idioms
312
+
313
+ ```
314
+ Check:
315
+ → No unnecessary clone()
316
+ → Use ? not unwrap()
317
+ → &str in function parameters
318
+ → Iterator methods not index loops
319
+ → Meaningful error types
320
+ ```
321
+
322
+
323
+ ## Quick Reference
324
+
325
+ ```
326
+ Naming: snake_case (fn/var), CamelCase (type), SCREAMING_SNAKE_CASE (const)
327
+ Format: rustfmt (just use it)
328
+ Docs: /// for public items, //! for module docs
329
+ Lint: #![warn(clippy::all)]
330
+ ```
331
+
332
+
333
+ ## Review Checklist
334
+
335
+ When reviewing code:
336
+
337
+ - [ ] Naming follows Rust conventions
338
+ - [ ] Using `?` instead of `unwrap()`
339
+ - [ ] Avoiding unnecessary `clone()`
340
+ - [ ] `unsafe` blocks have SAFETY comments
341
+ - [ ] Public APIs have doc comments
342
+ - [ ] Ran `cargo clippy`
343
+ - [ ] Ran `cargo fmt`
344
+ - [ ] No `get_` prefix on accessor methods
345
+ - [ ] Conversion methods named correctly (as/to/into)
346
+ - [ ] String parameters use `&str` when possible
347
+
348
+
349
+ ## Verification Commands
350
+
351
+ ```bash
352
+ # Format check
353
+ cargo fmt --check
354
+
355
+ # Lint check
356
+ cargo clippy -- -D warnings
357
+
358
+ # Documentation check
359
+ cargo doc --no-deps --open
360
+
361
+ # Run tests
362
+ cargo test
363
+
364
+ # Check naming conventions
365
+ cargo clippy -- -W clippy::wrong_self_convention
366
+ ```
367
+
368
+
369
+ ## Common Pitfalls
370
+
371
+ ### 1. Wrong Method Naming
372
+
373
+ **Symptom**: Clippy warning `wrong_self_convention`
374
+
375
+ ```rust
376
+ // ❌ Bad: unnecessary get_ prefix
377
+ impl User {
378
+ fn get_name(&self) -> &str { &self.name }
379
+ }
380
+
381
+ // ✅ Good: direct accessor
382
+ impl User {
383
+ fn name(&self) -> &str { &self.name }
384
+ }
385
+ ```
386
+
387
+ ### 2. String Type Misuse
388
+
389
+ **Symptom**: Unnecessary allocations
390
+
391
+ ```rust
392
+ // ❌ Bad: forces allocation
393
+ fn greet(name: String) {
394
+ println!("Hello, {}", name);
395
+ }
396
+
397
+ // ✅ Good: accepts borrowed or owned
398
+ fn greet(name: &str) {
399
+ println!("Hello, {}", name);
400
+ }
401
+
402
+ // Both work now:
403
+ greet("Alice"); // &str
404
+ greet(&owned_string); // &String → &str
405
+ ```
406
+
407
+ ### 3. Index Loops
408
+
409
+ **Symptom**: Less idiomatic, error-prone
410
+
411
+ ```rust
412
+ // ❌ Bad: manual indexing
413
+ for i in 0..items.len() {
414
+ println!("{}: {}", i, items[i]);
415
+ }
416
+
417
+ // ✅ Good: iterator
418
+ for item in &items {
419
+ println!("{}", item);
420
+ }
421
+
422
+ // ✅ Good: with index
423
+ for (i, item) in items.iter().enumerate() {
424
+ println!("{}: {}", i, item);
425
+ }
426
+ ```
427
+
428
+
429
+ ## Related Skills
430
+
431
+ - **rust-anti-pattern** - What not to do
432
+ - **rust-error** - Error handling patterns
433
+ - **rust-performance** - Performance idioms
434
+ - **rust-async** - Async conventions
435
+ - **rust-unsafe** - SAFETY comment style
436
+
437
+
438
+ ## Localized Reference
439
+
440
+ - **Chinese version**: [SKILL_ZH.md](./SKILL_ZH.md) - 完整中文版本,包含所有内容
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: rust-coding
3
+ description: "Rust coding standards skill for API ergonomics, module design, naming conventions, testability, and maintainability in production codebases."
4
+ ---
5
+
6
+ # Rust Coding Standards Skill
7
+
8
+ ## Core Question
9
+
10
+ **How do we keep Rust code easy to change, easy to review, and hard to misuse?**
11
+
12
+ ## Coding Principles
13
+
14
+ - Design APIs to make invalid states hard to represent.
15
+ - Prefer small, composable functions over large multi-purpose routines.
16
+ - Keep ownership and error behavior explicit at boundaries.
17
+ - Optimize readability first, then optimize hot paths with evidence.
18
+
19
+ ## Project Structure Guidelines
20
+
21
+ Recommended layering:
22
+ - `domain`: core business types and invariants.
23
+ - `service`: use-case orchestration.
24
+ - `infra`: external integrations (DB, HTTP, cache).
25
+ - `interface`: handler/controller/CLI entry points.
26
+
27
+ Module rules:
28
+ - Avoid giant `mod.rs` files; split by behavior.
29
+ - Keep public surface minimal (`pub(crate)` by default).
30
+ - Re-export intentionally to shape stable APIs.
31
+
32
+ ## API Ergonomics Patterns
33
+
34
+ - Accept `&str` instead of `String` when ownership is not required.
35
+ - Accept slices (`&[T]`) instead of `Vec<T>` in read-only APIs.
36
+ - Use builder patterns for complex constructors.
37
+ - Prefer domain-specific types over primitive obsession.
38
+
39
+ ```rust
40
+ pub struct UserId(String);
41
+
42
+ impl UserId {
43
+ pub fn parse(value: &str) -> Result<Self, &'static str> {
44
+ if value.is_empty() { return Err("empty user id"); }
45
+ Ok(Self(value.to_owned()))
46
+ }
47
+ }
48
+ ```
49
+
50
+ ## Error and Logging Conventions
51
+
52
+ - Return typed errors in reusable modules.
53
+ - Add context at boundary crossings (I/O, parsing, RPC).
54
+ - Log once at boundary layers; avoid duplicate logs in deep internals.
55
+
56
+ ## Review Checklist
57
+
58
+ - [ ] Public API contracts are clear and minimal.
59
+ - [ ] Naming follows domain language and Rust conventions.
60
+ - [ ] Functions have single, testable responsibilities.
61
+ - [ ] Errors include actionable context.
62
+ - [ ] Tests cover critical behavior and edge cases.
63
+
64
+ ## Common Pitfalls
65
+
66
+ - Excessive `.clone()` to bypass ownership design.
67
+ - Large enums/modules with mixed responsibilities.
68
+ - `unwrap` usage in production paths.
69
+ - Hidden side effects in “helper” utilities.
70
+
71
+ ## Verification Commands
72
+
73
+ ```bash
74
+ cargo fmt --check
75
+ cargo clippy -- -D warnings
76
+ cargo test
77
+ cargo doc --no-deps
78
+ ```
79
+
80
+ ## Related Skills
81
+
82
+ - `rust-anti-pattern`
83
+ - `rust-error`
84
+ - `rust-type-driven`
85
+
86
+ ## Localized Reference
87
+
88
+ - Original Chinese version is preserved in `SKILL_ZH.md`.