Contributing
dart_mutant is open source and welcomes contributions!
Ways to Contribute
Report Issues
Found a bug or have a feature request? Open an issue with:
- dart_mutant version (
dart_mutant --version) - Dart version (
dart --version) - Steps to reproduce
- Expected vs actual behavior
Improve Documentation
Documentation lives in the same repository. Improvements welcome:
- Fix typos or unclear explanations
- Add examples
- Translate to other languages
Submit Code
Pull requests are welcome for:
- Bug fixes
- New mutation operators
- Performance improvements
- Report format enhancements
Development Setup
Prerequisites
Clone and Build
git clone https://github.com/user/dart_mutant
cd dart_mutant
cargo build
Run Tests
# Run all tests
cargo test
# Run specific test
cargo test test_arithmetic_mutations
# Run with output
cargo test -- --nocapture
Run Lints
# Clippy (strictest settings)
cargo clippy
# Format check
cargo fmt --check
Code Style
Rust Guidelines
dart_mutant follows strict Rust conventions:
- No
unwrap()/expect()in production code - use?operator - No
unsafecode - Pure functions where possible
- Small, focused functions (max ~100 lines)
- Descriptive names - no single-letter variables except closures
Error Handling
Use anyhow::Result for error propagation:
use anyhow::{Context, Result};
fn parse_file(path: &Path) -> Result<Mutations> {
let content = std::fs::read_to_string(path)
.context("Failed to read file")?;
parse_dart(&content)
.context("Failed to parse Dart code")
}
Documentation
Public APIs must have doc comments:
/// Finds all arithmetic mutations in the given AST.
///
/// # Arguments
/// * `ast` - The parsed Dart AST
///
/// # Returns
/// A vector of mutations for arithmetic operators
pub fn find_arithmetic_mutations(ast: &Tree) -> Vec<Mutation> {
// ...
}
Project Structure
src/
├── main.rs # CLI entry point
├── cli/ # Argument parsing
├── parser/ # tree-sitter parsing
├── mutation/ # Mutation types and operators
├── runner/ # Test execution
├── report/ # Report generation
└── ai/ # AI integration
tests/
├── integration_*.rs # Integration tests
└── fixtures/ # Test Dart projects
Adding a Mutation Operator
- Define the operator in
src/mutation/operators.rs:
#[derive(Debug, Clone, PartialEq)]
pub enum MutationOperator {
// ... existing operators
MyNewOperator,
}
- Implement detection in
src/parser/mod.rs:
fn find_my_new_mutations(cursor: &mut TreeCursor, source: &str) -> Vec<Mutation> {
// Walk AST and find mutation candidates
}
- Add tests in
tests/integration_mutation.rs:
#[test]
fn test_my_new_mutation() {
let source = r#"
void test() {
// code that should be mutated
}
"#;
let mutations = find_mutations(source);
assert!(mutations.iter().any(|m| m.operator == MutationOperator::MyNewOperator));
}
- Update documentation in
website/src/docs/operators.md
Pull Request Process
- Fork the repository
- Create a branch:
git checkout -b feature/my-feature - Make changes with tests
- Run checks:
cargo test && cargo clippy && cargo fmt --check - Commit with descriptive message
- Push and open PR
PR Checklist
- [ ] Tests pass (
cargo test) - [ ] No clippy warnings (
cargo clippy) - [ ] Code formatted (
cargo fmt) - [ ] Documentation updated if needed
- [ ] CHANGELOG updated for user-facing changes
Release Process
Releases are automated via GitHub Actions:
- Update version in
Cargo.toml - Update
CHANGELOG.md - Create git tag:
git tag v0.2.0 - Push tag:
git push origin v0.2.0
Binaries are built for:
- Linux (x86_64, aarch64)
- macOS (x86_64, aarch64)
- Windows (x86_64)
Code of Conduct
Be respectful. We're all here to make better software.
Questions?
Thank you for contributing!