Files
pikl/docs/CODING_STANDARDS.md

122 lines
4.2 KiB
Markdown

# Coding Standards
Rules of the road for pikl-menu. If something's not covered
here, use good judgement and keep it consistent with the rest
of the codebase.
## Error Handling
- **No `anyhow`.** Every error type is a proper enum. No
stringly-typed errors, no `Box<dyn Error>` as a crutch.
- Use `thiserror` where it helps (derive `Error` +
`Display`), but it's not mandatory. Hand-written impls
are fine too.
- Use `Result` and `?` everywhere. Convert between error
types with `.map_err(YourVariant)`. Keep it simple, no
closures when a variant constructor will do.
- It's fine to stringify an error when crossing thread/async
boundaries (e.g. `JoinError` to `Io`), but always wrap
it in a typed variant with context.
## No Panics
- **No `unwrap()`, `expect()`, or `panic!()` in library or
binary code.** CI lints enforce this.
- We know IO and FFI can't be made perfectly safe. The goal
is to not *add* unsafe situations. Handle failures, don't
crash through them.
## Clippy & Lints
- Deny `clippy::unwrap_used`, `clippy::expect_used`,
`clippy::panic` workspace-wide. These are the guardrails
for the no-panic rule.
- Deny `clippy::dbg_macro`, `clippy::print_stdout`,
`clippy::print_stderr`. Use `tracing` instead.
- Enable `clippy::must_use_candidate`. If a function returns
a `Result`, `Option`, or any value the caller shouldn't
silently ignore, it gets `#[must_use]`.
- Run `cargo clippy -- -D warnings` in CI. Warnings are
errors.
## Unsafe Code
- `unsafe` blocks need a `// SAFETY:` comment explaining
why it's sound.
- Keep unsafe surface area minimal. If there's a safe
alternative that's not meaningfully worse, use it.
## Logging & Diagnostics
- **All logging goes through `tracing` and
`tracing-subscriber`.** No `println!`, `eprintln!`,
`dbg!()`, or manual log files.
- No temp files for logs. No `/tmp/pikl-debug.log`. None
of that.
- We'll eventually build a debug subscriber tool (think
tokio-console style). Design logging with structured
events in mind.
## Async Conventions
- Tokio is the async runtime. No mixing in other runtimes.
- Prefer structured concurrency with `JoinSet` over
fire-and-forget `tokio::spawn`. If you spawn a task, you
should be able to cancel it and know when it's done.
- Be explicit about cancellation safety. If a future holds
state that would be lost on drop, document it. This
matters for hooks and IPC especially.
- Use `tokio::select!` carefully. Every branch should be
cancellation-safe or documented as not.
## Public API Surface
- `pikl-core` is an embeddable library. Treat its public
API like a contract.
- Don't make things `pub` unless they need to be. Default
to `pub(crate)` and open up intentionally.
- Re-export the public interface from `lib.rs`. Consumers
shouldn't need to reach into submodules.
- Breaking changes to the public API should be deliberate,
not accidental side effects of refactoring.
## Performance
- **Avoid cloning** where possible. Cheap copies (small
`Copy` types, `Arc::clone`) are fine. Cloning strings
and vecs as a convenience? Try harder.
- **Use zero-copy** patterns: borrowed slices, `Cow`, views
into existing data.
- **Prefer iterators** over collecting into intermediate
`Vec`s. Chain, filter, map. Only collect when you
actually need the collected result.
## Testing
- Every module in `pikl-core` gets unit tests. Every filter,
hook lifecycle event, I/O format.
- Integration tests exercise the real binary with real
pipes. No mocked IO at that level.
- Cross-platform: tests must pass on Linux and macOS. Gate
platform-specific tests with `#[cfg]`.
- When the GUI frontend lands, add snapshot/visual
regression tests.
- Build toward an e2e framework that can drive the full
tool.
## Dependencies
- `pikl-core` stays free of rendering, terminal, and GUI
deps. It's an embeddable library.
- Prefer pure-Rust deps. C deps behind feature flags only
(e.g. `pcre2`).
- Be intentional about what you pull in. If the standard
library does it, use the standard library.
## Import Ordering
- Group imports in this order: `std`, external crates,
workspace crates (`pikl_*`), then `self`/`super`/`crate`.
- Blank line between each group.
- `rustfmt` handles the rest. Don't fight it.