# 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` 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.