|
| 1 | +# Contributing to parallel-disk-usage |
| 2 | + |
| 3 | +## Commit Message Convention |
| 4 | + |
| 5 | +This project uses [Conventional Commits](https://www.conventionalcommits.org/). |
| 6 | + |
| 7 | +### Format |
| 8 | + |
| 9 | +``` |
| 10 | +type(scope): lowercase description |
| 11 | +``` |
| 12 | + |
| 13 | +### Rules |
| 14 | + |
| 15 | +- **Types:** `feat`, `fix`, `refactor`, `perf`, `docs`, `style`, `chore`, `ci`, `test`, `lint` |
| 16 | +- **Scopes** (optional): `cli`, `api`, `deps`, `readme`, `benchmark`, `toolchain`, `test`, or other relevant area |
| 17 | +- **Description:** always lowercase after the colon, no trailing period, brief (3-7 words preferred) |
| 18 | +- **Breaking changes:** append `!` before the colon (e.g. `feat(cli)!: remove deprecated flag`) |
| 19 | +- **Code identifiers** in descriptions should be wrapped in backticks (e.g. `` chore(deps): update `rand` ``) |
| 20 | + |
| 21 | +### Exception: Version Releases |
| 22 | + |
| 23 | +Version release commits use **only** the version number as the message — no type prefix: |
| 24 | + |
| 25 | +``` |
| 26 | +0.21.1 |
| 27 | +``` |
| 28 | + |
| 29 | +## Code Style |
| 30 | + |
| 31 | +Automated tools enforce formatting (`cargo fmt`) and linting (`cargo clippy`). The following conventions are **not** enforced by those tools and must be followed manually. |
| 32 | + |
| 33 | +### Import Organization |
| 34 | + |
| 35 | +Prefer **merged imports** — combine multiple items from the same crate or module into a single `use` statement with braces rather than separate `use` lines. |
| 36 | + |
| 37 | +Imports are grouped in this order, separated by blank lines: |
| 38 | + |
| 39 | +1. `use super::...` or `use crate::...` (internal) |
| 40 | +2. External crate imports (alphabetical) |
| 41 | +3. `use std::...` (standard library) |
| 42 | + |
| 43 | +Within each group, items are ordered alphabetically. Platform-specific imports (`#[cfg(unix)]`) go in a separate block after the main imports. |
| 44 | + |
| 45 | +```rust |
| 46 | +use crate::{ |
| 47 | + args::{Args, Quantity, Threads}, |
| 48 | + bytes_format::BytesFormat, |
| 49 | + size, |
| 50 | +}; |
| 51 | +use clap::Parser; |
| 52 | +use pipe_trait::Pipe; |
| 53 | +use std::{io::stdin, time::Duration}; |
| 54 | + |
| 55 | +#[cfg(unix)] |
| 56 | +use crate::get_size::{GetBlockCount, GetBlockSize}; |
| 57 | +``` |
| 58 | + |
| 59 | +### Module Organization |
| 60 | + |
| 61 | +- Use the flat file pattern (`module.rs`) rather than `module/mod.rs` for submodules. |
| 62 | +- List `pub mod` declarations first, then `pub use` re-exports, then private imports and items. |
| 63 | +- Use `pub use` to re-export key types at the module level for convenience. |
| 64 | + |
| 65 | +```rust |
| 66 | +pub mod error_only_reporter; |
| 67 | +pub mod error_report; |
| 68 | +pub mod event; |
| 69 | + |
| 70 | +pub use error_only_reporter::ErrorOnlyReporter; |
| 71 | +pub use error_report::ErrorReport; |
| 72 | +pub use event::Event; |
| 73 | +``` |
| 74 | + |
| 75 | +- Type aliases using `pub use ... as ...` are used to provide semantic alternative names: |
| 76 | + |
| 77 | +```rust |
| 78 | +pub use Reflection as DataTreeReflection; |
| 79 | +``` |
| 80 | + |
| 81 | +### Derive Macro Ordering |
| 82 | + |
| 83 | +When deriving multiple traits, use this order and split across multiple `#[derive(...)]` lines for readability: |
| 84 | + |
| 85 | +1. **Standard traits:** `Debug`, `Default`, `Clone`, `Copy` |
| 86 | +2. **Comparison traits:** `PartialEq`, `Eq`, `PartialOrd`, `Ord` |
| 87 | +3. **Hash** |
| 88 | +4. **`derive_more` traits:** `Display`, `From`, `Into`, `Add`, `AddAssign`, etc. |
| 89 | +5. **Feature-gated derives** on a separate `#[cfg_attr(...)]` line |
| 90 | + |
| 91 | +```rust |
| 92 | +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
| 93 | +#[derive(From, Into, Add, AddAssign, Sub, SubAssign, Sum)] |
| 94 | +#[cfg_attr(feature = "json", derive(Deserialize, Serialize))] |
| 95 | +pub struct Bytes(u64); |
| 96 | +``` |
| 97 | + |
| 98 | +### Generic Parameter Naming |
| 99 | + |
| 100 | +Use **descriptive names** for type parameters, not single letters: |
| 101 | + |
| 102 | +- `Size`, `Name`, `SizeGetter`, `HardlinksRecorder`, `Report` |
| 103 | + |
| 104 | +Single-letter generics are acceptable only in very short, self-contained trait impls. |
| 105 | + |
| 106 | +### Trait Bounds |
| 107 | + |
| 108 | +Prefer `where` clauses over inline bounds when there are multiple constraints: |
| 109 | + |
| 110 | +```rust |
| 111 | +impl<Size, SizeGetter, HardlinksRecorder, Report> |
| 112 | + From<FsTreeBuilder<'a, Size, SizeGetter, HardlinksRecorder, Report>> |
| 113 | + for DataTree<OsStringDisplay, Size> |
| 114 | +where |
| 115 | + Report: Reporter<Size> + Sync + ?Sized, |
| 116 | + Size: size::Size + Send + Sync, |
| 117 | + SizeGetter: GetSize<Size = Size> + Sync, |
| 118 | + HardlinksRecorder: RecordHardlinks<Size, Report> + Sync + ?Sized, |
| 119 | +``` |
| 120 | + |
| 121 | +### Visibility |
| 122 | + |
| 123 | +- Use `pub` for the public API surface. |
| 124 | +- Use `pub(crate)` for items shared within the crate but not exposed externally. |
| 125 | +- Default to private for everything else. |
| 126 | + |
| 127 | +### Error Handling |
| 128 | + |
| 129 | +- Define custom error enums with `#[derive(Debug, Display, Error)]` from `derive_more`. |
| 130 | +- Mark error enums as `#[non_exhaustive]`. |
| 131 | +- Minimize `unwrap()` in non-test code — use proper error propagation. `unwrap()` is acceptable in tests and for provably infallible operations (with a comment explaining why). When deliberately ignoring an error, use `.ok()` with a comment explaining why. |
| 132 | + |
| 133 | +```rust |
| 134 | +#[derive(Debug, Display, Error)] |
| 135 | +#[non_exhaustive] |
| 136 | +pub enum RuntimeError { |
| 137 | + #[display("SerializationFailure: {_0}")] |
| 138 | + SerializationFailure(serde_json::Error), |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +### Documentation Comments |
| 143 | + |
| 144 | +- Use `///` doc comments for all public types, traits, functions, and fields. |
| 145 | +- Use `//!` module-level doc comments at the top of `lib.rs` and significant modules. |
| 146 | +- Include usage examples with `/// ```no_run` blocks for key public APIs. |
| 147 | +- Reference related types with `[`backtick links`](crate::path)` syntax. |
| 148 | + |
| 149 | +### Feature Gating |
| 150 | + |
| 151 | +- Use `#[cfg(feature = "...")]` for optional functionality (e.g., `json`, `cli`). |
| 152 | +- Use `#[cfg(unix)]` for POSIX-specific code. |
| 153 | +- Use `#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]` for conditional derives. |
| 154 | + |
| 155 | +### Pattern Matching |
| 156 | + |
| 157 | +Use exhaustive matching. When mapping enum variants to values, prefer the concise wrapping style: |
| 158 | + |
| 159 | +```rust |
| 160 | +ExitCode::from(match self { |
| 161 | + RuntimeError::SerializationFailure(_) => 2, |
| 162 | + RuntimeError::DeserializationFailure(_) => 3, |
| 163 | +}) |
| 164 | +``` |
| 165 | + |
| 166 | +### Struct Field Ordering |
| 167 | + |
| 168 | +Order fields logically by purpose, not alphabetically. Group related fields together. Document every public field with `///` comments. |
| 169 | + |
| 170 | +### Macros |
| 171 | + |
| 172 | +Use macros to reduce boilerplate for repetitive patterns (e.g. newtype wrappers, trait impls for multiple numeric types). Keep macros well-scoped and documented. |
| 173 | + |
| 174 | +### Warnings Policy |
| 175 | + |
| 176 | +The crate uses `#![deny(warnings)]` — all warnings are treated as errors. Code must compile warning-free. |
| 177 | + |
| 178 | +## Setup |
| 179 | + |
| 180 | +Install the required Rust toolchain and components before running any checks: |
| 181 | + |
| 182 | +```sh |
| 183 | +rustup toolchain install "$(< rust-toolchain)" |
| 184 | +rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy |
| 185 | +``` |
| 186 | + |
| 187 | +## Automated Checks |
| 188 | + |
| 189 | +Before submitting, ensure: |
| 190 | + |
| 191 | +- `cargo fmt -- --check` passes |
| 192 | +- `cargo clippy` passes (on all feature combinations) |
| 193 | +- `cargo test` passes |
| 194 | +- The project builds with no default features, default features, and all features |
| 195 | + |
| 196 | +The CI script `test.sh` runs all of these across 5 feature combinations. You can run it locally with: |
| 197 | + |
| 198 | +```sh |
| 199 | +FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh |
| 200 | +``` |
0 commit comments