Contributing
Welcome to SomaFM Player! We're excited to have you contribute to this project.
Getting Started
Prerequisites
Before you begin, ensure you have the following installed:
-
Rust (1.70 or later)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
System Dependencies (Linux only)
# Ubuntu/Debian sudo apt-get install libasound2-dev pkg-config # Fedora/CentOS/RHEL sudo dnf install alsa-lib-devel pkg-config # Arch Linux sudo pacman -S alsa-lib pkg-config
-
Git
git --version
Development Setup
-
Fork the repository on GitHub
-
Clone your fork
git clone https://github.com/yourusername/soma-play.git cd soma-play
-
Add upstream remote
git remote add upstream https://github.com/mpuccini/soma-play.git
-
Install development tools
# Code formatting rustup component add rustfmt # Linting rustup component add clippy # Documentation cargo install mdbook # Security auditing cargo install cargo-audit # Test coverage cargo install cargo-tarpaulin
-
Build and test
cargo build cargo test cargo run
Development Workflow
1. Create a Feature Branch
git checkout -b feature/your-feature-name
Branch Naming Conventions:
feature/
- New featuresfix/
- Bug fixesdocs/
- Documentation updatesrefactor/
- Code refactoringtest/
- Test improvements
2. Make Your Changes
Follow our coding standards and guidelines (see below).
3. Test Your Changes
# Run all tests
cargo test
# Run tests with output
cargo test -- --nocapture
# Run specific test module
cargo test config::tests
# Check code formatting
cargo fmt --check
# Run linter
cargo clippy -- -D warnings
# Generate test coverage
cargo tarpaulin --out Html
4. Commit Your Changes
Commit Message Format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat
- New featurefix
- Bug fixdocs
- Documentationstyle
- Code style changesrefactor
- Code refactoringtest
- Test additions/improvementschore
- Maintenance tasks
Examples:
git commit -m "feat(audio): add volume fade transitions"
git commit -m "fix(ui): resolve channel list scrolling issue"
git commit -m "docs(api): update configuration examples"
5. Push and Create Pull Request
git push origin feature/your-feature-name
Then create a pull request on GitHub.
Coding Standards
Rust Code Style
-
Use
cargo fmt
for consistent formatting -
Follow Rust naming conventions
snake_case
for functions and variablesPascalCase
for types and structsSCREAMING_SNAKE_CASE
for constants
-
Add documentation comments
#![allow(unused)] fn main() { /// Plays audio from the specified stream URL. /// /// # Arguments /// * `url` - The stream URL to play from /// /// # Examples /// ``` /// let mut player = AudioPlayer::new()?; /// player.play("http://stream.somafm.com/groovesalad".to_string())?; /// ``` pub fn play(&mut self, url: String) -> Result<()> { // Implementation } }
-
Handle errors properly
#![allow(unused)] fn main() { // Good: Use Result types and proper error handling fn load_config() -> Result<Config, ConfigError> { let content = fs::read_to_string(&path) .map_err(ConfigError::FileRead)?; toml::from_str(&content) .map_err(ConfigError::Parse) } // Avoid: Using unwrap() or expect() in library code }
-
Use meaningful variable names
#![allow(unused)] fn main() { // Good let selected_channel_index = 0; let stream_url = "http://..."; // Avoid let i = 0; let url = "http://..."; }
Module Organization
- Keep modules focused - Each module should have a single responsibility
- Use clear imports - Prefer explicit imports over glob imports
- Organize by feature - Group related functionality together
#![allow(unused)] fn main() { // Good: Explicit imports use crate::config::Config; use crate::models::{Channel, PlayerState}; // Avoid: Glob imports (in most cases) use crate::models::*; }
Error Handling
- Use custom error types with
thiserror
- Provide context for errors
- Convert errors at module boundaries
#![allow(unused)] fn main() { #[derive(Debug, thiserror::Error)] pub enum AudioError { #[error("Failed to initialize audio device: {0}")] DeviceInit(String), #[error("Stream error: {0}")] Stream(#[from] StreamError), #[error("Invalid volume level: {level} (must be 0-100)")] InvalidVolume { level: u8 }, } }
Testing Guidelines
- Write unit tests for all public functions
- Use integration tests for complex workflows
- Mock external dependencies in tests
- Test error conditions as well as success cases
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; use tempfile::tempdir; #[test] fn test_config_save_and_load() { let dir = tempdir().unwrap(); let config_path = dir.path().join("config.toml"); let original_config = Config { volume: 75, last_channel_id: Some("groovesalad".to_string()), auto_start: true, }; // Test save original_config.save_to_path(&config_path).unwrap(); // Test load let loaded_config = Config::load_from_path(&config_path).unwrap(); assert_eq!(original_config.volume, loaded_config.volume); assert_eq!(original_config.last_channel_id, loaded_config.last_channel_id); assert_eq!(original_config.auto_start, loaded_config.auto_start); } #[test] fn test_invalid_volume_returns_error() { let mut player = AudioPlayer::new().unwrap(); let result = player.set_volume(150); // Invalid volume > 100 assert!(result.is_err()); } } }
Documentation
Code Documentation
- Document all public APIs with examples
- Include usage examples in documentation
- Document error conditions and return types
- Keep documentation up to date with code changes
User Documentation
- Update relevant guide sections when adding features
- Include configuration examples for new settings
- Add troubleshooting entries for known issues
- Update the changelog with user-facing changes
Building Documentation
# Generate API documentation
cargo doc --open
# Build user guide
cd docs && mdbook serve --open
# Build all documentation
./build-docs.sh
Feature Development
Adding New Features
- Create an issue describing the feature
- Discuss the approach with maintainers
- Start with tests (TDD approach when possible)
- Implement incrementally with small, focused commits
- Update documentation before marking as complete
Feature Guidelines
- Maintain backward compatibility when possible
- Add configuration options for user preferences
- Consider performance impact of new features
- Ensure cross-platform compatibility
Example: Adding a New Feature
#![allow(unused)] fn main() { // 1. Define the feature interface pub trait Equalizer { fn set_bands(&mut self, bands: Vec<f32>) -> Result<()>; fn get_bands(&self) -> Vec<f32>; } // 2. Add configuration support #[derive(Serialize, Deserialize)] pub struct EqualizerConfig { pub enabled: bool, pub bands: Vec<f32>, } // 3. Implement the feature pub struct GraphicEqualizer { bands: Vec<f32>, enabled: bool, } // 4. Add tests #[cfg(test)] mod tests { #[test] fn test_equalizer_bands() { let mut eq = GraphicEqualizer::new(); let bands = vec![0.0, 2.0, 4.0, 2.0, 0.0]; eq.set_bands(bands.clone()).unwrap(); assert_eq!(eq.get_bands(), bands); } } // 5. Update UI integration impl App { pub fn toggle_equalizer(&mut self) -> Result<()> { // Implementation } } }
Bug Fixes
Finding and Reporting Bugs
- Search existing issues before creating new ones
- Provide detailed reproduction steps
- Include system information (OS, Rust version, etc.)
- Add logs or error messages when applicable
Fixing Bugs
- Write a test that reproduces the bug
- Fix the issue with minimal changes
- Verify the test passes with your fix
- Check for similar issues in related code
Performance Considerations
Optimization Guidelines
- Profile before optimizing - Measure, don't guess
- Focus on hot paths - Audio playback, UI rendering
- Consider memory usage - Avoid unnecessary allocations
- Benchmark changes - Ensure improvements are real
Performance Testing
# Run benchmarks (if available)
cargo bench
# Profile memory usage
cargo run --release -- --log-level=debug
# Monitor resource usage during development
htop # or similar system monitor
Security
Security Guidelines
- Validate all inputs from external sources
- Use secure HTTP for API communications
- Handle credentials safely (if any)
- Audit dependencies regularly
# Run security audit
cargo audit
# Check for known vulnerabilities
cargo audit --database advisory
Release Process
Preparing a Release
- Update version numbers in
Cargo.toml
- Update CHANGELOG.md with new features and fixes
- Run full test suite with
cargo test
- Generate and review documentation
- Test on target platforms
Release Checklist
- All tests pass
- Documentation is up to date
- Changelog is updated
- Version numbers are bumped
- No security vulnerabilities
- Performance regressions checked
- Cross-platform compatibility verified
Community Guidelines
Communication
- Be respectful and inclusive
- Ask questions when you're unsure
- Provide constructive feedback in reviews
- Help newcomers get started
Code Reviews
- Review for correctness and style
- Suggest improvements rather than just pointing out problems
- Acknowledge good work and clever solutions
- Be open to feedback on your own code
Issue Management
- Provide clear descriptions for issues and PRs
- Link related issues and PRs
- Update status as work progresses
- Close issues when resolved
Getting Help
Resources
- Documentation: User Guide and API Reference
- Source Code: Well-commented and documented
- Issues: Check existing issues for similar problems
- Discussions: GitHub Discussions for questions and ideas
Contact
- GitHub Issues: For bugs and feature requests
- GitHub Discussions: For questions and general discussion
- Pull Requests: For code contributions and reviews
Thank you for contributing to SomaFM Player! Your efforts help make this project better for everyone. 🎵