# Contributing to qrcode-rs
Thanks for your interest in improving `qrcode-rs`! This guide covers the
development setup, the quality bar every change must meet, and the commit/PR
conventions used in this repository.
## Getting started
- **Rust toolchain**: stable **1.85 or newer** (the crate uses edition 2024;
declared via `rust-version` in `Cargo.toml`).
- **Build & test**:
```bash
cargo build --all-features
cargo test --all-features
```
## Project layout
```
src/
lib.rs # qrcode-rs facade: QrCode, builder, streams, re-exports
bin/ # qrencodes CLI (behind the `cli` feature)
crates/
qrcode-core/ # encoding core, traits, module views, plugin contracts
qrcode-render/# shared render traits, text/unicode/ansi/image helpers
qrcode-parse/ # WiFi/vCard/GS1 payload parsers
qrcode-decode/# decoder traits, grayscale views, rqrr adapter, SA parser
qrcode-*/ # individual backend crates: svg, eps, pic, html, pdf
benches/ # criterion benchmarks (encoding, rendering)
examples/ # runnable examples
tests/ # integration tests
docs/ # roadmap & planning (local; see below)
```
## Feature flags
The **library is zero-dependency by default** — the core encoder pulls in no
external crates. Optional capabilities are feature-gated:
| `image` | PNG/JPEG/… rendering via the `image` crate (includes the PNG codec) |
| `svg` | SVG renderer |
| `eps` | Encapsulated PostScript renderer |
| `pic` | troff PIC renderer |
| `html` | HTML table / CSS Grid renderer |
| `pdf` | vector PDF renderer (no external dep) |
| `async` | Tokio-backed async rendering helpers |
| `decode-rqrr` | decoder adapter through `rqrr` |
| `compat-1x` | transitional 1.x facade compatibility |
| `cli` | the `qrencodes` binary (`clap`-based) |
`default = ["std", "image", "svg", "pic", "eps", "html", "pdf"]`. When adding
a new dependency, prefer a feature gate or a hand-written implementation to
honor the zero-dependency principle.
## Quality bar
Every PR must pass:
```bash
cargo test --workspace --all-features
cargo test --workspace --no-default-features
cargo test --test proptest --all-features
cargo test --test differential --all-features
cargo clippy --workspace --all-features --all-targets -- -D warnings
cargo fmt --all --check
cargo doc --all-features --no-deps # warning/error-free (#![deny(missing_docs)] is enforced)
```
All public API must be documented (`#![deny(missing_docs)]` is set in
`src/lib.rs`). New features should include unit tests and/or doctests, and an
example where useful.
## Property, differential, and fuzz testing
The v2 test architecture includes three layers beyond ordinary integration
tests:
```bash
cargo test --test proptest --all-features
cargo test --test proptest --no-default-features
cargo test --test differential --all-features
cargo test --test differential --no-default-features
```
The `fuzz/` workspace is a `cargo-fuzz` project with targets for core encoding,
fixed-version encoding, SVG rendering, structured payload parsing, and
structured append. Use a compile check for quick local validation:
```bash
cargo check --manifest-path fuzz/Cargo.toml --bins
```
For a short local fuzz smoke run, install `cargo-fuzz` and run a bounded target:
```bash
cargo install cargo-fuzz --locked
cargo fuzz run encode -- -runs=100
cargo fuzz run render_svg -- -runs=100
```
## Commit conventions
- Use **conventional-commit** prefixes: `feat:`, `fix:`, `perf:`, `docs:`,
`refactor:`, `test:`, `style:`, `chore:`, optionally scoped
(`feat(v0.5.0): …`).
- Make **small, focused commits** — one logical change each.
- End every commit message with exactly this trailer and no other
`Co-Authored-By`:
```
Co-Authored-By: heihutu <heihutu@gmail.com>
```
## Pull requests
1. Open a PR against `main` (this project develops on `main`).
2. Ensure the quality bar above is green.
3. Update `CHANGELOG.md` and, where relevant, the roadmap docs.
4. For breaking changes (only on a major-version bump), describe the migration
path.
## Releases
Releases follow [SemVer](https://semver.org). The flow:
1. Update `version` in `Cargo.toml`.
For a major workspace release, keep `qrcode-rs` and all publishable split
crates on the same version and update every path dependency version
constraint.
2. Add a `## [X.Y.Z]` section to `CHANGELOG.md` (group by Added / Changed /
Fixed / Notes, list deferrals explicitly).
3. Update crate READMEs and the root README when a published crate boundary,
feature flag, or install snippet changes.
4. Tick the relevant roadmap checklist (`docs/roadmap/v*.md`) and update
`docs/roadmap.md` (current version + status).
5. Verify the quality bar, then check package file lists:
```bash
cargo package --workspace --allow-dirty --no-verify --list
```
A full `cargo package --workspace` before anything has been published will
fail for crates that depend on newly split workspace crates, because Cargo
resolves publishable path dependencies through the registry. Use the file-list
check before publishing, then publish in dependency order.
6. Publish split crates before the facade, skip workspace-local crates such as
`qrcode-compat`, and wait for each version to appear in the crates.io index
before publishing crates that depend on it:
```bash
version=2.0.0
for crate in \
qrcode-core qrcode-render qrcode-parse qrcode-decode \
qrcode-svg qrcode-eps qrcode-pic qrcode-html qrcode-pdf qrcode-rs
do
cargo publish --registry crates-io --package "$crate"
until cargo info "${crate}@${version}" --registry crates-io >/dev/null 2>&1
do
sleep 10
done
sleep 300
done
```
## Roadmap & local docs
The roadmap lives in `docs/roadmap/`. Note that `docs/*` (except
`docs/images/` and `docs/index.md`) are **gitignored and kept locally** —
planning notes and per-version implementation plans live there and are not
version-controlled. Trust `Cargo.toml`'s `version` and `CHANGELOG.md` for the
true release state, not the roadmap status markers (which can lag).