We welcome contributions to tomat! Here's a guide to help you get started.
Before submitting any changes:
- Formatting:
cargo fmt -- --check(MUST exit with code 0) - Linting:
cargo clippy --all-targets --all-features -- -D warnings(MUST exit with code 0, no warnings allowed) - Compilation:
cargo check(MUST pass) - Tests:
cargo test(all integration tests must pass)
-
Fork the repository.
-
Clone your fork:
git clone <your-fork-url>
-
Install prerequisites:
# Rust toolchain (specified in rust-toolchain.toml) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # ALSA development libraries (for audio) sudo apt-get install libasound2-dev # Ubuntu/Debian sudo dnf install alsa-lib-devel # Fedora/RHEL sudo pacman -S alsa-lib # Arch Linux
-
You might also want to install task for easier task management. The following
taskcommands assume you have it installed.
Always run commands from the repository root.
# Quick development check (recommended)
task dev
# Individual commands
cargo check # Check compilation without building
cargo test # Run all tests (19 integration tests)
cargo clippy --all-targets --all-features -- -D warnings # Lint
cargo fmt # Format code
cargo fmt -- --check # Check formatting
# Build commands
cargo build # Development build
cargo build --release # Release build# Build and start daemon for testing
cargo build
./target/debug/tomat daemon start
# Test with short durations for fast feedback
./target/debug/tomat start --work 0.1 --break 0.05 # 6s work, 3s break
./target/debug/tomat status
./target/debug/tomat toggle
# Stop daemon when done
./target/debug/tomat daemon stopAll code changes MUST pass these checks before commit:
- Zero clippy warnings:
cargo clippy --all-targets --all-features -- -D warnings - Proper formatting:
cargo fmt -- --check - All tests pass:
cargo test - Compilation success:
cargo check
- Error handling: Uses
Box<dyn std::error::Error>for simplicity - Comments: Only add comments when they match existing style or explain complex logic
- Dependencies: Use existing libraries when possible, avoid adding new dependencies unless absolutely necessary
- Commit style: Use
Conventional Commits (
feat:,fix:,docs:,test:,refactor:)
Tomat is designed as a small, focused Rust project with a client-server architecture.
src/main.rsMain entry point, command parsing, high-level flowsrc/cli.rsCLI argument parsing withclapsrc/config.rsConfiguration system with timer, sound, and notification settingssrc/server.rsUnix socket server, daemon lifecycle, client request handling, PID file managementsrc/timer.rsTimer state machine, phase transitions, status output formatting, desktop notificationssrc/audio.rsAudio playback utilitiestests/integration tests
Client CLI Commands
↓
Unix Socket ($XDG_RUNTIME_DIR/tomat.sock)
↓
Daemon Process (background)
↓
TimerState (Work/Break/LongBreak phases)
↓
JSON Status Output (optimized for waybar)
Client-Server Architecture:
- Single binary with subcommands
- Daemon listens on Unix socket at
$XDG_RUNTIME_DIR/tomat.sock - PID file tracking at
$XDG_RUNTIME_DIR/tomat.pidwith exclusive file locking - Line-delimited JSON protocol for communication
Timer State Machine:
- Phases: Work → Break → Work → ... → LongBreak (after N sessions)
- Two modes controlled by
--auto-advanceflag:false(default): Timer transitions to next phase but pauses, requiring manual resumetrue: Timer automatically continues through all phases
- Timer starts in paused work state
All tests use the TestDaemon helper struct for isolated testing:
// Start isolated test daemon with temporary socket
let daemon = TestDaemon::start()?;
// Send commands
daemon.send_command(&["start", "--work", "0.05", "--break", "0.05"])?;
// Get status
let status = daemon.get_status()?;
assert_eq!(status["class"], "work");
// Wait for timer completion
daemon.wait_for_completion(10)?;Key testing features:
- Isolated environments: Each test uses
tempfile::tempdir()with customXDG_RUNTIME_DIR - Fast execution: Fractional minutes (0.05 = 3 seconds) for rapid testing
- Notification suppression:
TOMAT_TESTING=1env var disables desktop notifications - Automatic cleanup:
TestDaemonDrop impl kills daemon process
- Auto-advance behavior: Verify
auto_advance=falsepauses after transitions,auto_advance=truecontinues automatically - Timer control: Toggle pause/resume, stop/start
- Daemon lifecycle: Start, stop, status, duplicate detection
- Edge cases: Manual skip, fractional minutes
- Configuration: Timer, sound, and notification configuration parsing
- Icon management: Embedded icon caching and different icon modes
- Add enum variant to
Commandsinsrc/main.rs - Add command handling in
handle_client()insrc/server.rs - Add match arm in
main()insrc/main.rs - Write integration tests in
tests/usingTestDaemon
- Update
TimerStatestruct insrc/timer.rsif new fields needed - Modify state machine logic in
next_phase(),start_work(), etc. - Update status output in
get_status_output() - Test both
auto_advance=trueandauto_advance=falsemodes
- Update appropriate config struct in
src/config.rs - Add default value functions
- Update
Defaultimplementation - Add comprehensive tests for new configuration options
- Update documentation and examples
- Daemon lifecycle: SIGTERM with 5-second timeout, then SIGKILL
- PID file locking: Uses
fs2::FileExt::try_lock_exclusive()to prevent race conditions - Socket cleanup: Automatic cleanup of socket and PID files on graceful shutdown
- Desktop notifications: Via
notify-rustwith embedded icon system - Icon caching: Embedded icon automatically cached to
~/.cache/tomat/icon.png - Mako compatibility: Default "auto" icon mode works with mako out of the box
- TOML-based: Configuration loaded from
~/.config/tomat/config.toml - Hierarchical: Built-in defaults → config file → CLI arguments
# Run daemon in foreground (see output directly)
cargo build && ./target/debug/tomat daemon run
# Test output (see println! statements)
cargo test -- --nocapture
# Check socket status
ss -lx | grep tomat
# Inspect PID file
cat $XDG_RUNTIME_DIR/tomat.pid && ps -p <PID>
# Check logs (if using systemd)
journalctl --user -u tomat.service -fWhen contributing, ensure:
- No breaking changes: Existing waybar configurations must continue to work
- CLI stability: Existing command-line interfaces are preserved
- Configuration compatibility: Existing config files remain valid
- API consistency: JSON output format remains stable for waybar integration
The project uses automated semantic versioning:
- Conventional Commits: Commit messages determine version bumps
- Automated CI: GitHub Actions handle testing and releases
- Semantic Versioning:
feat:→ minor,fix:→ patch,BREAKING CHANGE:→ major