Thank you for considering contributing to masterror. This document outlines the development process, coding standards, and quality requirements.
- Getting Started
- Development Environment
- Coding Standards
- Testing Requirements
- Pull Request Process
- Commit Message Format
- Code Review Guidelines
- Performance Benchmarking
- Documentation Standards
- Issue Reporting
- Rust: MSRV 1.90 (stable and nightly toolchains recommended)
- Tools:
cargo-audit,cargo-deny,cargo-tarpaulinorcargo-llvm-cov(for coverage) - GitHub CLI:
gh(optional but recommended for workflow management)
git clone https://github.com/RAprogramm/masterror.git
cd masterror
rustup toolchain install stable nightly
rustup component add clippy rustfmt --toolchain nightly
cargo build --all-features
cargo test --all-featuresmasterror/
├── src/ # Core library
├── masterror-derive/ # Procedural macros
├── masterror-template/ # Template parser
├── tests/ # Integration tests
├── benches/ # Criterion benchmarks
├── examples/ # Usage examples
├── docs/ # Extended documentation
└── .github/ # CI/CD workflows
Configure your editor to:
- Use 4 spaces for indentation
- Maximum line length: 99 characters
- Trim trailing whitespace
- Insert final newline
VS Code:
- rust-analyzer
- Even Better TOML
- crates
IntelliJ IDEA:
- Rust plugin
This project adheres to the RustManifest guidelines, which define comprehensive standards for professional Rust development.
We follow Rust standard formatting with project-specific overrides in .rustfmt.toml based on RustManifest .rustfmt.toml reference:
cargo +nightly fmt --allKey principles:
- Line limit: 99 characters
- Imports: grouped (std, external, internal)
- No
::operator except inusestatements - Avoid
unwrap()andexpect()in library code (tests are exempt) - Minimize
clone()usage - Meaningful variable names; constants in
SCREAMING_SNAKE_CASE
Every public item SHALL have:
- Summary: One-line description
- Details: Behavior explanation
- Parameters: Document all parameters
- Return value: Document return semantics
- Errors: Document error conditions
- Examples: At least one doctest
- Safety: Document unsafe code (if any)
Example:
/// Converts a domain error into an application error with telemetry.
///
/// This function attaches structured metadata fields to the error context,
/// applies the specified redaction policy, and maps the error kind to the
/// appropriate transport-level representation.
///
/// # Parameters
///
/// - `source`: The source error to convert
/// - `metadata`: Structured telemetry fields to attach
///
/// # Returns
///
/// Returns an `AppError` with the source error, metadata, and appropriate
/// error kind classification.
///
/// # Examples
///
/// ```
/// use masterror::{AppError, field};
///
/// let err = AppError::internal("db connection lost")
/// .with_field(field::str("table", "users"));
/// assert_eq!(err.metadata().len(), 1);
/// ```
pub fn with_field(self, field: Field) -> Self {
// implementation
}- All fallible operations return
Result<T, E> - Use
masterror::AppErrorfor application-level errors - Preserve error source chains
- Attach relevant context via metadata
- No panics except in tests and
unreachable!()after exhaustive matches
- Prefer
&stroverStringwhen ownership is not required - Use
&[T]instead of&Vec<T>in function parameters - Use
Vec::with_capacity()when size is known - Avoid unnecessary trait bounds on struct definitions
Minimum: 95% | Target: 100%
Located in #[cfg(test)] mod tests blocks within source files.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_creation_attaches_metadata() {
let err = AppError::internal("test")
.with_field(field::str("key", "value"));
assert_eq!(err.metadata().len(), 1);
}
#[test]
fn invalid_input_returns_bad_request() {
let result = validate_input("");
assert!(matches!(
result.unwrap_err().kind,
AppErrorKind::BadRequest
));
}
}Located in tests/ directory.
use masterror::prelude::*;
#[test]
fn axum_integration_converts_app_error_to_response() {
let err = AppError::not_found("resource missing");
let response = axum::response::IntoResponse::into_response(err);
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}Every public API example SHALL compile and run:
/// ```
/// use masterror::AppError;
/// let err = AppError::unauthorized("token expired");
/// assert_eq!(err.kind, masterror::AppErrorKind::Unauthorized);
/// ```
- Deterministic: No time, network, or environment dependencies
- Isolated: Tests do not share mutable state
- Fast: Unit tests complete in milliseconds
- Comprehensive: Cover happy paths, error paths, edge cases
Every error variant SHALL be tested:
#[test]
fn constraint_violation_maps_to_conflict() {
let db_err = simulate_unique_violation();
let app_err: AppError = db_err.into();
assert!(matches!(app_err.kind, AppErrorKind::Conflict));
}cargo test --all-features
cargo test --no-default-features
cargo test --test integration_http
cargo test --doc
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri testBefore implementing features or fixes:
- Search existing issues to avoid duplicates
- Create issue describing problem/feature
- Wait for maintainer acknowledgment (for large changes)
Branch name SHALL be the issue number without prefixes:
git checkout -b 123Make changes following coding standards. Commit frequently with descriptive messages.
Before submitting, verify:
cargo +nightly fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo bench --no-run --features benchmarks
cargo doc --no-deps --all-featuresBefore pushing, cancel in-progress workflows to save CI resources:
gh run list --branch $(git branch --show-current) --status in_progress \
--json databaseId --jq '.[].databaseId' | xargs -I {} gh run cancel {}git push -u origin 123
gh pr create --title "123" --body "$(cat <<'EOF'
## Summary
Brief description of changes.
## Changes
- Added X
- Fixed Y
- Updated Z
## Test Plan
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Benchmarks show no regression
- [ ] Documentation updated
Closes #123
EOF
)"- Respond to feedback promptly
- Push additional commits to address comments
- Request re-review after changes
- Do not force-push after initial review
Maintainers will merge after:
- All CI checks pass
- At least one approval
- No unresolved conversations
#<issue> <type>: <description>
[optional body]
[optional footer]
feat: New featurefix: Bug fixperf: Performance improvementrefactor: Code restructuring without behavior changetest: Adding or updating testsdocs: Documentation changesci: CI/CD changeschore: Maintenance tasks
#42 feat: add async handler support
Implements async error handlers for Axum and Actix-web frameworks.
Handlers can now return async closures that resolve to AppError.
#89 fix: correct gRPC status mapping for timeout errors
Previously mapped to UNAVAILABLE, now correctly maps to DEADLINE_EXCEEDED
per gRPC specification.
#120 perf: optimize metadata serialization path
Reduces allocations in ProblemJson conversion by 40%.
Benchmark results show 200ns improvement on average.
- Keep PRs focused (one issue per PR)
- Respond to feedback within 48 hours
- Accept constructive criticism professionally
- Ask questions if feedback is unclear
Review for:
- Correctness: Does it solve the problem?
- Testing: Adequate test coverage?
- Performance: Any obvious inefficiencies?
- Documentation: Public APIs documented?
- Style: Follows project conventions?
- Safety: No unsafe code without justification?
Use these labels:
- "looks good to me" (LGTM): Approve
- "request changes": Blocking issues
- "comment": Non-blocking suggestions
cargo bench --features benchmarks --bench error_paths
cargo bench --features benchmarks --bench error_paths -- --save-baseline mainPlace criterion benchmarks in benches/:
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use masterror::AppError;
fn benchmark_error_creation(c: &mut Criterion) {
c.bench_function("create_error_with_metadata", |b| {
b.iter(|| {
black_box(AppError::internal("test")
.with_field(field::str("key", "value")))
});
});
}
criterion_group!(benches, benchmark_error_creation);
criterion_main!(benches);CI will fail if benchmarks show >10% performance regression. If regression is necessary:
- Document rationale in PR description
- Add
[perf-regression-justified]label - Require maintainer approval
Update README.md when adding:
- New features requiring examples
- Breaking changes requiring migration
- New feature flags
Add entries to CHANGELOG.md under "Unreleased" section:
## [Unreleased]
### Added
- Support for async error handlers (#42)
### Fixed
- Incorrect gRPC status mapping for timeouts (#89)
### Changed
- BREAKING: Renamed `ErrorContext` to `Context` (#105)
### Performance
- Reduced metadata serialization allocations by 40% (#120)Extended documentation goes in wiki:
- Step-by-step tutorials
- Migration guides
- Comparison with alternatives
- Troubleshooting recipes
Use the bug report template and include:
- Minimal reproducible example
- Expected vs actual behavior
- Environment (Rust version, OS, features enabled)
- Error messages and stack traces
Use the feature request template and include:
- Use case description
- Proposed API (if applicable)
- Alternatives considered
- Impact assessment
For usage questions:
- Check existing documentation and issues first
- Use GitHub Discussions (if enabled) or issues
- Provide context and code samples
(For maintainers)
- Update CHANGELOG.md with version number and date
- Update version in all
Cargo.tomlfiles - Run full CI locally
- Create git tag:
git tag -a v0.x.y -m "Release v0.x.y" - Push tag:
git push origin v0.x.y - GitHub Actions will publish to crates.io
- Create GitHub release with CHANGELOG excerpt
- Documentation: https://docs.rs/masterror
- Issues: https://github.com/RAprogramm/masterror/issues
- Email: andrey.rozanov.vl@gmail.com
By contributing, you agree that your contributions will be dual-licensed under MIT OR Apache-2.0.