Editor for papermario-dx mods
1<!--
2SPDX-FileCopyrightText: 2026 Alex Bates <alex@bates64.com>
3
4SPDX-License-Identifier: AGPL-3.0-or-later
5-->
6
7# Contributing
8
9Enter the dev shell with `nix develop` (or use [direnv](https://direnv.net/)), then:
10
11```sh
12just run
13```
14
15## Dependencies
16
17- Dependencies that could feasibly be used by multiple crates should go in the workspace `Cargo.toml`.
18- When adding a new dependency, ensure it is up to date.
19
20## Formatting
21
22Run `just fmt` before committing. This formats code and adds
23[REUSE](https://reuse.software/)-compliant SPDX license headers to any new
24files, using your `git config user.name` and `git config user.email` as the
25copyright holder.
26
27If you contribute to an existing file, you may add your own
28`SPDX-FileCopyrightText` line alongside the existing one.
29
30## Style
31
32- Check your code against `just check` and `just test`.
33- Try to follow the [Rust API Guidelines Checklist](https://rust-lang.github.io/api-guidelines/checklist.html).
34- 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.
35- 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.
36- 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.
37- Avoid just `#[expect]` or `#[allow]`ing lines. The checks are there for a reason. For example, `as` should usually be `.into()`, or `.try_into()?`.
38
39## Error handling
40
41- The code should only panic if there is a bug in the code. User input should never cause a panic.
42- Use `thiserror` to validate user data and return errors.
43- Use `anyhow` to attach context to errors as they bubble up the stack.
44- Never ignore an error: either pass it on, or log it.
45- If a problem is recoverable, use `ka_log::warn!` and recover.
46- UI code should never panic. Do not use `#[expect(clippy::expect_used)]` etc. in UI code - warn and have a fallback.
47
48Strive 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).
49
50## Logging
51
52Use `tracing`, or, if a library isn't multithreaded, use `log`.
53
54- `error!` is for _unrecoverable_ problems. The program couldn't complete an operation.
55 - Libraries should ideally not log error, but instead return Err in a Result.
56- `warn!` is for when you recover from an error. The operation completed, but it couldn't do exactly what it was asked to do.
57 - Sometimes an `Err` is handled by logging it as a warning and then running some fallback code.
58 - Warnings are also used for things that might be errors, but could be intended.
59 - If data is lost, it is an error and not a warning.
60- `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.
61- `debug!` is a level you opt-in to debug. These are logged when high-level, important operations are performed.
62- `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.
63
64Use debug formatting `{:?}` in logs.