# Contributing Enter the dev shell with `nix develop` (or use [direnv](https://direnv.net/)), then: ```sh just run ``` ## Dependencies - Dependencies that could feasibly be used by multiple crates should go in the workspace `Cargo.toml`. - When adding a new dependency, ensure it is up to date. ## Formatting Run `just fmt` before committing. This formats code and adds [REUSE](https://reuse.software/)-compliant SPDX license headers to any new files, using your `git config user.name` and `git config user.email` as the copyright holder. If you contribute to an existing file, you may add your own `SPDX-FileCopyrightText` line alongside the existing one. ## Style - Check your code against `just check` and `just test`. - Try to follow the [Rust API Guidelines Checklist](https://rust-lang.github.io/api-guidelines/checklist.html). - Write unit tests for all public functions containing business logic, data transformations, or state management. They go in a `#[cfg(test)] mod tests` at the bottom of each file. GUI/rendering code does not require unit tests. - Write integration tests using `egui_kittest` for GUI code. Binary crates (like `kammy`) cannot use the `tests/` directory at the crate root because there is no library target to import. Instead, use a `src/tests.rs` module gated behind `#[cfg(test)]`, with submodules in `src/tests/` organized by feature (e.g. `src/tests/undo.rs`). Shared test utilities go in `src/tests.rs`. Library crates should use the standard `tests/` directory at the crate root. - Prefer module-level inner doc comments (`//!`) at the top of a file over outer doc comments (`///`) on the `mod` declaration. This keeps the documentation next to the code it describes. - Avoid just `#[expect]` or `#[allow]`ing lines. The checks are there for a reason. For example, `as` should usually be `.into()`, or `.try_into()?`. ## Error handling - The code should only panic if there is a bug in the code. User input should never cause a panic. - Use `thiserror` to validate user data and return errors. - Use `anyhow` to attach context to errors as they bubble up the stack. - Never ignore an error: either pass it on, or log it. - If a problem is recoverable, use `ka_log::warn!` and recover. - UI code should never panic. Do not use `#[expect(clippy::expect_used)]` etc. in UI code - warn and have a fallback. Strive to encode code invariants and contracts in the type system as much as possible. [Parse, don't validate.](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) If you can't enforce a contract in the type system, enforce them using `assert` and in documentation (if its part of a public API). ## Logging Use `tracing`, or, if a library isn't multithreaded, use `log`. - `error!` is for _unrecoverable_ problems. The program couldn't complete an operation. - Libraries should ideally not log error, but instead return Err in a Result. - `warn!` is for when you recover from an error. The operation completed, but it couldn't do exactly what it was asked to do. - Sometimes an `Err` is handled by logging it as a warning and then running some fallback code. - Warnings are also used for things that might be errors, but could be intended. - If data is lost, it is an error and not a warning. - `info!` should be used by application code to write interesting and rare things to the user. For instance, you might log that a file was saved to a specific path. - `debug!` is a level you opt-in to debug. These are logged when high-level, important operations are performed. - `trace!` is a last resort and is mostly for spam. If it generates a lot of continuous logging (e.g. `ui` function called every frame), it should go to trace. Use debug formatting `{:?}` in logs.