Contributing¶
Contributions to pitop are welcome. This page covers the coding standards, architecture rules, and process for submitting changes.
Coding standards¶
Error handling¶
- No
unwrap()orexpect()in any code path that can reach production - Use
anyhow::Result,Option, or.unwrap_or_default() - CI enforces this via
cargo clippy -- -D clippy::unwrap_used -D clippy::expect_used unwrap()is acceptable in test code only
Subprocess calls¶
- No
std::process::Command-- always usetokio::process::Command - All vcgencmd calls go through the centralized
VcgencmdRunnerinsrc/util/vcgencmd.rs
Hardware paths¶
- No hardcoded hwmon numbers (e.g.,
hwmon2). Always discover by enumerating/sys/class/hwmon/and matching by thenamefile content - hwmon numbers change across reboots, so hardcoded values will break
sysfs access¶
- All sysfs/procfs reads must handle
ENOENT(file not found) andEACCES(permission denied) gracefully - Return
None,Ok(default), or an appropriate error -- never panic - Do not shell out for data available via sysfs (temperature, CPU frequency, etc.)
Code quality¶
cargo clippymust pass with zero warningscargo fmtmust be applied before every commit- No GUI dependencies -- this is a terminal-only application
Architecture rules¶
Direct data collection¶
pitop reads system data directly from /proc/ and /sys/ files. Do not use the sysinfo crate or any system-info abstraction library. The only exception is vcgencmd, which is called as a subprocess for data not available via sysfs.
Board detection¶
Board detection reads /proc/device-tree/compatible at startup. Unknown boards run with generic Linux collectors only. Every Pi-specific feature must have a fallback for when the hardware is not present.
Lazy tab refresh¶
Only the active tab's expensive collectors run each tick. The Overview tab's metrics (CPU, memory, thermal, network, fan, GPU, throttle) always run. This is important for keeping CPU usage low on the Pi Zero 2W.
Ring buffers¶
All sparkline history uses fixed-size ring buffers from src/util/ring_buffer.rs. The default size is 60 samples, configurable via the history_size config option (range: 10-600).
Project structure¶
See the Architecture page for the full module structure and design details.
Setting up for development¶
# Clone the repository
git clone https://github.com/wu-hongjun/pitop.git
cd pitop
# Build and run
cargo run
# Run with verbose output for debugging
cargo run -- -v
# Run tests
cargo test
# Check lints
cargo clippy
Pull request process¶
- Fork the repository and create a feature branch
- Make your changes following the coding standards above
- Ensure all tests pass:
cargo test - Ensure no lint warnings:
cargo clippy - Format your code:
cargo fmt - Write a clear commit message describing the change
- Open a pull request against
main
PR checklist¶
- [ ]
cargo testpasses - [ ]
cargo clippypasses with zero warnings - [ ]
cargo fmtapplied - [ ] No
unwrap()orexpect()in production code - [ ] No hardcoded hwmon numbers
- [ ] No
std::process::Command(usetokio::process::Command) - [ ] Pi-specific features degrade gracefully on non-Pi hardware
- [ ] New collectors include unit tests with fixture data
Adding a new collector¶
- Create
src/collectors/new_thing.rswith a struct andcollect()method - Add the data struct to hold parsed values (with
Defaultimpl) - Add the collector and data fields to
Appinsrc/app.rs - Wire up the collector in
App::tick()(decide: always-on or tab-specific?) - Create or update the corresponding
src/ui/*.rsmodule to render the data - Add unit tests with fixture data
- Export the module in
src/collectors/mod.rs
Adding a new UI tab¶
- Create
src/ui/new_tab.rswith adraw(f: &mut Frame, app: &App, area: Rect)function - Add the module declaration to
src/ui/mod.rs - Add a match arm in
draw_active_tab()insrc/ui/mod.rs - Update
TAB_COUNTandTAB_NAMESinsrc/app.rs - Update key handling in
src/event.rsif needed - Use colors from
app.theme-- never hardcodeColor::*values