Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contributing

Thank you for your interest in contributing to this MQTT broker project!

Getting Started

  1. Fork the repository on GitHub
  2. Clone your fork:
    git clone https://github.com/yourusername/mqtt-broker.git
    cd mqtt-broker
    
  3. Set upstream remote:
    git remote add upstream https://github.com/originalowner/mqtt-broker.git
    
  4. Create a feature branch:
    git checkout -b feature/my-feature
    

Development Setup

Prerequisites

  • Rust 1.70 or later
  • Git
  • (Optional) Docker for integration testing

Build and Test

# Build
cargo build

# Run tests
cargo test

# Run with logging
RUST_LOG=debug cargo run --bin mqtt-broker

Useful Commands

# Format code
cargo fmt

# Run linter
cargo clippy

# Check without building
cargo check

# Update dependencies
cargo update

# Generate docs
cargo doc --open

Code Style

Formatting

Run cargo fmt before committing:

cargo fmt

We use the default rustfmt configuration.

Linting

All code must pass cargo clippy:

cargo clippy -- -D warnings

Fix warnings before submitting PRs.

Naming Conventions

TypeConventionExample
StructsPascalCaseTopicFilter
TraitsPascalCaseStorage
Functionssnake_caseencode_packet
Variablessnake_caseclient_id
ConstantsSCREAMING_SNAKEMAX_PACKET_SIZE
Modulessnake_casetopic_matcher

Documentation

All public items should have documentation:

#![allow(unused)]
fn main() {
/// Encodes an MQTT packet to bytes.
///
/// # Arguments
///
/// * `packet` - The packet to encode
/// * `buf` - Buffer to write encoded bytes to
///
/// # Returns
///
/// Returns `Ok(())` on success, or `CodecError` on failure.
///
/// # Example
///
/// ```rust
/// let packet = MqttPacket::Pingreq;
/// let mut buf = BytesMut::new();
/// MqttEncoder::encode(&packet, &mut buf)?;
/// ```
pub fn encode(packet: &MqttPacket, buf: &mut BytesMut) -> Result<(), CodecError> {
    // ...
}
}

Making Changes

Types of Contributions

  • πŸ› Bug fixes β€” Fix issues with tests
  • ✨ Features β€” Add new functionality
  • πŸ“š Documentation β€” Improve docs, examples
  • πŸ§ͺ Tests β€” Add missing tests
  • ⚑ Performance β€” Optimize code
  • πŸ”§ Refactoring β€” Improve code structure

Branch Naming

feature/description    # New features
fix/description        # Bug fixes
docs/description       # Documentation
test/description       # Test additions
refactor/description   # Code refactoring

Examples:

  • feature/qos2-support
  • fix/packet-decode-crash
  • docs/session-examples

Commit Messages

Follow Conventional Commits:

type(scope): description

[optional body]

[optional footer]

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • test: Tests
  • refactor: Code refactoring
  • perf: Performance improvement
  • chore: Maintenance

Examples:

feat(codec): add MQTT 5 property parsing

fix(session): prevent double-free on disconnect

docs(readme): add installation instructions

test(router): add wildcard matching edge cases

Pull Request Process

Before Submitting

  1. Update your branch:

    git fetch upstream
    git rebase upstream/main
    
  2. Run all checks:

    cargo fmt
    cargo clippy -- -D warnings
    cargo test
    
  3. Add tests for new functionality

  4. Update documentation if needed

PR Guidelines

  • One feature per PR β€” Keep PRs focused
  • Descriptive title β€” What does this PR do?
  • Link issues β€” Reference related issues
  • Include tests β€” All new code needs tests
  • Update CHANGELOG β€” Add entry under β€œUnreleased”

PR Template

## Description

Brief description of the changes.

## Related Issues

Closes #123

## Changes

- Added X
- Fixed Y
- Updated Z

## Testing

- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed

## Checklist

- [ ] Code formatted with `cargo fmt`
- [ ] No clippy warnings
- [ ] Documentation updated
- [ ] CHANGELOG updated

Testing Requirements

All PRs Must

  1. Pass existing tests β€” No regressions
  2. Add new tests β€” For new functionality
  3. Maintain coverage β€” Don’t decrease coverage

Test Guidelines

#![allow(unused)]
fn main() {
// Good: Descriptive name, tests one thing
#[test]
fn test_publish_with_empty_payload_succeeds() {
    let packet = PublishPacket {
        payload: vec![],
        // ...
    };
    assert!(encode(&packet).is_ok());
}

// Good: Tests error case
#[test]
fn test_invalid_topic_returns_error() {
    let result = TopicFilter::parse("invalid/#/topic");
    assert!(matches!(result, Err(TopicError::InvalidFilter(_))));
}
}

Module Structure

When adding new modules:

src/mymodule/
β”œβ”€β”€ mod.rs          # Public exports
β”œβ”€β”€ types.rs        # Type definitions
β”œβ”€β”€ implementation.rs
└── tests.rs        # Unit tests

Export from src/lib.rs:

#![allow(unused)]
fn main() {
pub mod mymodule;
}

Error Handling

Use the project’s error types:

#![allow(unused)]
fn main() {
// Good: Use existing error types
fn parse_topic(s: &str) -> Result<Topic, TopicError> {
    // ...
}

// Good: Add new variant if needed
#[derive(Debug, Error)]
pub enum TopicError {
    #[error("invalid topic: {0}")]
    InvalidTopic(String),
    
    #[error("topic too long: {length} > {max}")]
    TooLong { length: usize, max: usize },
}
}

Getting Help

  • Questions? Open a discussion on GitHub
  • Bug found? Open an issue with reproduction steps
  • Feature idea? Open an issue to discuss first

Code of Conduct

  • Be respectful and constructive
  • Focus on the code, not the person
  • Assume good intentions
  • Help others learn

Recognition

Contributors are recognized in:

  • CHANGELOG.md (for each release)
  • GitHub contributors page

Thank you for contributing! πŸŽ‰