Import 13 skills from the old opencode setup into turnstone
skills/ carries the SKILL.md files; scripts/import_skills.py runs inside the VM via turnstone's own storage layer (SKILL.md parser + risk scanner) so rows match what the console admin API would write. Server must be restarted after import (stale WAL snapshot).
This commit is contained in:
1 parent
93fa05bdcc
commit
18fdff7d8c
15 files changed
+1973
No files matched your search
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: rust-best-practices
|
||||
description: >
|
||||
Best-practices for Rust software. Load into a workstream whenever you need to read or write a Rust (.rs) file.
|
||||
---
|
||||
|
||||
# Best practices for programming Rust
|
||||
- Run `cargo check`, `cargo clippy`, and `cargo clippy` before comitting to git.
|
||||
- Don't ignore warnings from cargo.
|
||||
- Don't supress warnings using e.g. `#[allow(warning)]`
|
||||
- Never use `unsafe` without asking the user for explicit permission.
|
||||
|
||||
When setting up a new rust project, enable these lints:
|
||||
|
||||
```toml
|
||||
[lints.clippy]
|
||||
allow_attributes = "warn"
|
||||
as_ptr_cast_mut = "warn"
|
||||
as_underscore = "warn"
|
||||
borrow_as_ptr = "warn"
|
||||
implicit_clone = "warn"
|
||||
undocumented_unsafe_blocks = "warn"
|
||||
unicode_not_nfc = "warn"
|
||||
unused_async = "deny"
|
||||
wildcard_dependencies = "deny"
|
||||
|
||||
[lints.rust]
|
||||
absolute_paths_not_starting_with_crate = "deny"
|
||||
explicit_outlives_requirements = "warn"
|
||||
macro_use_extern_crate = "deny"
|
||||
missing_abi = "deny"
|
||||
non_ascii_idents = "forbid"
|
||||
rust_2018_idioms = { level = "deny", priority = -1 }
|
||||
single_use_lifetimes = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unused_macro_rules = "warn"
|
||||
```
|
||||
|
||||
## Newtypes and "Parse, Don't Validate"
|
||||
|
||||
Use newtypes to make invalid states unrepresentable. Instead of passing raw strings and validating at every use site, parse once into a validated type.
|
||||
|
||||
### Pattern
|
||||
|
||||
```rust
|
||||
pub struct Email(String); // private field prevents construction without validation
|
||||
|
||||
impl Email {
|
||||
pub fn parse(s: &str) -> Result<Self, ParseError> {
|
||||
if !s.contains('@') {
|
||||
return Err(ParseError::InvalidEmail);
|
||||
}
|
||||
Ok(Self(s.to_owned()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Guidelines
|
||||
|
||||
- **Private fields**: Force construction through `parse()` only
|
||||
- **Parse at boundaries**: Validate at system edges (API handlers, CLI args, config loading)
|
||||
- **Use the type in signatures**: `fn create_user(email: Email)` not `fn create_user(email: &str)`
|
||||
- **No re-validation**: Once parsed, the type guarantees validity
|
||||
|
||||
### When to Apply
|
||||
|
||||
- Email addresses, passwords, phone numbers
|
||||
- File paths, URLs, identifiers
|
||||
- Any domain value with invariants
|
||||
- Configuration values requiring validation
|
||||
|
||||
### Anti-pattern to Avoid
|
||||
|
||||
```rust
|
||||
// BAD: validation scattered, easy to forget
|
||||
fn handler(email: &str) {
|
||||
if !validate_email(email) { return Err(...); }
|
||||
create_user(email) // still just &str, could be invalid
|
||||
}
|
||||
```
|
||||
Reference in new issue
Block a user