doc: Add project design, dev plan, coding standards, and use cases.
This commit is contained in:
121
docs/CODING_STANDARDS.md
Normal file
121
docs/CODING_STANDARDS.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user