# Contributing to Rust Diff Analyzer
Thank you for your interest in contributing to this project!
## Code Style & Standards
This project follows the [RustManifest](https://github.com/RAprogramm/RustManifest) coding standards. Please read it thoroughly before contributing.
Key points:
- Use `cargo +nightly fmt` for formatting
- No `unwrap()` or `expect()` in production code
- Documentation via Rustdoc only (no inline comments)
- Descriptive naming conventions
## Development Setup
### Prerequisites
- Rust nightly toolchain
- cargo-make (optional, for task automation)
- cargo-nextest (for running tests)
### Installation
```bash
git clone https://github.com/RAprogramm/rust-prod-diff-checker
cd rust-prod-diff-checker
# Install nightly toolchain
rustup toolchain install nightly
rustup component add rustfmt --toolchain nightly
rustup component add clippy
# Install test runner (optional but recommended)
cargo install cargo-nextest
```
### Pre-commit Checks
Before committing, ensure all checks pass:
```bash
# Format check
cargo +nightly fmt --all -- --check
# Linting
cargo clippy --all-targets --all-features -- -D warnings
# Tests
cargo test --all-features
# Or with nextest
cargo nextest run --all-features
```
## Git Workflow
### Branch Naming
Use issue number as branch name:
```
123
```
### Commit Messages
Format: `#<issue_number> <type>: <description>`
```
#123 feat: add new output format
#123 fix: correct line counting in parser
#45 docs: update API examples
#78 test: add property tests for extractor
#90 refactor: simplify config loading
```
Types:
- `feat` - new feature
- `fix` - bug fix
- `docs` - documentation
- `test` - tests
- `refactor` - code refactoring
- `chore` - maintenance tasks
### Pull Requests
1. Create branch from `main`
2. Make your changes
3. Ensure all CI checks pass
4. Create PR with descriptive title
5. Include `Closes #<issue>` in description
### Keep Production Code Changes Small
**Code review quality degrades with size.** What matters is *production code*, not total lines changed.
| < 100 lines | Thorough | Low |
| 100-300 lines | Moderate | Medium |
| 300+ lines | Superficial | High |
**Tests and benchmarks don't count the same way:**
- A PR with 50 lines in `src/` and 1000 lines of tests is **easy to review**
- A PR with 300 lines in `src/` and 0 tests is **hard to review and risky**
Use [rust-prod-diff-checker](https://github.com/RAprogramm/rust-prod-diff-checker) GitHub Action to automatically analyze PR size and separate production changes from tests/benchmarks.
**Rule of thumb:** If a reviewer can't understand your production code changes in 15 minutes, the PR is too big.
## Testing
### Test Organization
```
tests/
├── integration.rs # Integration tests
├── property.rs # Property-based tests
└── fixtures/ # Test data
```
### Writing Tests
- Cover all public API functions
- Test error paths, not just happy paths
- Use property-based testing for parsers
- No `unwrap()` in tests - use `?` with proper error types
```rust
#[test]
fn test_parse_diff() -> Result<(), Box<dyn std::error::Error>> {
let diff = include_str!("fixtures/sample.diff");
let result = parse_diff(diff)?;
assert_eq!(result.len(), 3);
Ok(())
}
```
### Running Tests
```bash
# All tests
cargo test --all-features
# With coverage
cargo llvm-cov nextest --all-features
# Specific test
cargo test test_parse_diff
# Property tests only
cargo test --test property
```
## CI/CD Pipeline
### Automated Checks
Every PR triggers:
| Format | `cargo +nightly fmt --check` |
| Clippy | `cargo clippy -D warnings` |
| Test | `cargo test --all-features` |
| Doc | `cargo doc --no-deps` |
| Coverage | Upload to Codecov |
| Benchmark | Compile benchmarks |
| Audit | Security vulnerability scan |
| REUSE | License compliance |
### Coverage Requirements
- Project target: auto (maintain current level)
- Patch target: 80% (new code must be well-tested)
## Architecture
### Module Structure
```
src/
├── lib.rs # Public API exports
├── error.rs # Error types (AppError)
├── config.rs # Configuration handling
├── analysis/
│ ├── mod.rs # Analysis module
│ ├── ast_visitor.rs # AST visitor for semantic extraction
│ ├── extractor.rs # AST extraction
│ └── mapper.rs # Change mapping (returns MapResult)
├── classifier/
│ └── ... # Code classification logic
├── git/
│ └── diff_parser.rs # Git diff parsing
├── types/
│ ├── change.rs # Change, Summary, AnalysisResult
│ ├── classification.rs # CodeType enum
│ ├── scope.rs # AnalysisScope, ExclusionReason
│ └── semantic_unit.rs # SemanticUnit with qualified_name()
└── output/
├── formatter.rs # Output formatting
├── comment.rs # PR comment format with tables
├── github.rs # GitHub Actions format
└── json.rs # JSON format
```
### Key Types
- `SemanticUnit` - Represents a code element with `qualified_name()` for impl context
- `Change` - A change to a semantic unit with classification and per-unit line counts
- `MapResult` - Result from `map_changes()` containing changes and `AnalysisScope`
- `AnalysisScope` - Tracks analyzed files, skipped files, and exclusion patterns
- `ExclusionReason` - Why a file was skipped (NonRust, IgnorePattern)
- `CodeType` - Classification: Production, Test, Benchmark, Example
- `Config` - Runtime configuration
## Adding Features
### New Output Format
1. Create `src/output/newformat.rs`
2. Implement `OutputFormatter` trait
3. Add to `OutputFormat` enum in `config.rs`
4. Register in `src/output/formatter.rs`
5. Add tests and documentation
### New Code Unit Type
1. Add variant to `UnitKind` in `src/analysis/extractor.rs`
2. Implement extraction in `extract_units()`
3. Add weight in `config.rs`
4. Update tests
## Release Process
Releases are automated via CI on tag push:
1. Update version in `Cargo.toml`
2. Commit: `chore(release): prepare v1.x.x`
3. Create and push tag:
```bash
git tag v1.x.x
git push origin v1.x.x
```
4. CI builds binaries for all platforms
5. GitHub Release is created automatically
6. Changelog is updated
### Versioning
Follow [Semantic Versioning](https://semver.org/):
- MAJOR: Breaking API changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes
## Documentation
### Code Documentation
All public items must have Rustdoc:
```rust
/// Parses a unified diff string into structured file diffs.
///
/// # Errors
///
/// Returns `AppError::DiffParse` if the diff format is invalid.
///
/// # Examples
///
/// ```
/// use rust_diff_analyzer::git::parse_diff;
///
/// let diff = "diff --git a/foo.rs b/foo.rs\n...";
/// let files = parse_diff(diff)?;
/// # Ok::<(), rust_diff_analyzer::AppError>(())
/// ```
pub fn parse_diff(input: &str) -> Result<Vec<FileDiff>, AppError> {
// ...
}
```
### README Updates
Update README.md when:
- Adding new CLI options
- Changing configuration format
- Adding new output formats
- Modifying GitHub Action inputs/outputs
## Getting Help
- Open an issue for bugs or feature requests
- Check existing issues before creating new ones
- Provide minimal reproduction for bugs
## License
By contributing, you agree that your contributions will be licensed under the MIT License.