# Rust Conventions

If a project-level `rust-guidelines` ruleset or repo-local `CONTRIBUTING.md` exists, **it wins**. This file is the fallback.

## Style guide

Official Rust Style Guide (rustwiki.org/en/style-guide/), enforced by `rustfmt`. Set the edition in `rustfmt.toml`:

```toml
style_edition = "2024"
```

## Naming (RFC 430)

| Element | Convention | Example |
|---|---|---|
| Crate / module | `snake_case` | `my_crate`, `parser` |
| Type / trait / enum variant | `UpperCamelCase` | `HttpClient`, `Error::Timeout` |
| Function / method / variable | `snake_case` | `send_request`, `retry_count` |
| Const / static | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES` |
| Lifetime | short lowercase | `'a`, `'src` |
| Type parameter | single uppercase | `T`, `E` |

## Formatting

- Indent: **4 spaces**.
- Max line length: **100** (`rustfmt` default).
- Always run `cargo fmt` before commit.

## Ownership & borrowing

- Prefer borrowing (`&T`) over cloning. `.clone()` is a code smell unless you can name the reason in one sentence.
- Return owned types from constructors and "convert" methods; accept borrowed slices (`&str`, `&[T]`) as parameters.
- `&mut` only when you must mutate. Two `&` borrows are usually better than one `&mut`.
- `Cow<'_, str>` when a function sometimes allocates and sometimes does not.

## Error handling

- **Library** crates: define a typed error with `thiserror`. One `Error` enum per crate, one variant per failure mode.
- **Application** crates: `anyhow::Result<T>` at the top level is fine; convert to typed errors at module boundaries.
- **No `unwrap()` / `expect()` in production paths.** Acceptable in:
  - Tests.
  - `main` for setup that genuinely cannot fail.
  - `expect("invariant: ...")` when the message documents the invariant.
- Use the `?` operator. Do not write `match`-on-`Result` ladders.

## Idioms

- Iterators (`map`, `filter`, `collect`, `fold`, `find`) over indexed loops.
- `if let` / `let else` over `match` with a single arm.
- `#[derive(Debug, Clone, ...)]` aggressively. Add `PartialEq` / `Eq` / `Hash` when the type lands in a collection.
- Newtype small primitives that carry meaning: `struct UserId(u64);`, not raw `u64`.
- `mut` and `pub` only when needed. Start private, widen on demand.
- Use `From` / `TryFrom` for conversions, not `parse_x_from_y` helpers.
- Pattern-match deeply (`if let Some(User { id, .. }) = user`) rather than chained `.unwrap().unwrap()`.

## Async / Tokio

- Pick **one** runtime per binary (`tokio` is standard). Never mix `tokio` and `async-std`.
- Never `block_on` inside an async function.
- Spawn tasks deliberately; remember `JoinHandle`s and `await` them.
- `tokio::select!` for races; never poll futures by hand.
- Long CPU work goes in `tokio::task::spawn_blocking`.
- Cancellation: use `CancellationToken` (tokio-util) for cooperative shutdown.

## Unsafe

- Every `unsafe` block needs a comment documenting **every** invariant the caller must uphold.
- New `unsafe` requires a second pair of eyes on review.
- Default to safe abstractions (`bytes`, `parking_lot`, `crossbeam`) before reaching for `unsafe`.

## Module layout

- Public API at the crate root via `pub use`; implementation in private modules.
- One concept per file. Split a module before it passes ~500 lines.
- Unit tests in `mod tests { use super::*; ... }` at the bottom of the file.
- Integration tests in the `tests/` directory.

## Tests

- Unit: `#[test]` inside `mod tests`. Use `assert_eq!`, `assert!`, `assert_matches!`.
- Async: `#[tokio::test]` or `#[test]` with an explicit `tokio::runtime::Runtime` when you need control.
- Property: `proptest` or `quickcheck` for pure transformations.
- **Doc tests**: runnable examples in doc comments are tests — keep them green.
- Use `pretty_assertions::assert_eq!` for readable diffs on large structs.

### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)

Rust's trait-based DI makes self-mocking rarer than in JVM/JS, but it still happens:

- Using `mockall::mock!` (or `automock`) to generate a mock of the **same struct under test**, then asserting on its own methods. The unit under test must be the real impl; mock the trait it depends on, not the trait it *is*.
- Splitting a behavior into a helper trait *only so* the test can stub it, then expecting `expect_helper().returning(...)` to be the real assertion. The test now proves the SUT calls the helper, not that the behavior works.
- Reaching into privates via `pub(crate)` widening, `#[cfg(test)] pub` shortcuts, or test-only modules that expose internal state to assert on.
- A test that constructs the SUT, replaces one of its trait-object dependencies with a mock whose `returning(...)` mirrors the very thing being tested.

What's fine: `mockall` mocks of injected trait dependencies (`MockHttpClient`, `MockUserRepository`), `unwrap()` in tests for setup that genuinely cannot fail, `assert_eq!` on returned values, `assert_matches!` on returned `Result` / `Option`.

## Required tooling

Run before any commit:

- `cargo fmt --all` — formatting.
- `cargo clippy --all-targets --all-features -- -D warnings` — lint (warnings are errors).
- `cargo check --all-targets` — fast type check.
- `cargo test --all` — tests.
