Help

Support and contributing

Where to file, what to include, and what a change has to satisfy before it can land. Reports that quote a spec section number get fixed fastest.

Where to file

HostUse it for
GitHubThe primary home. Issues live here.
GitLabMirror.
CodebergMirror.

The workspace is github.com/hl7-rust/hl7-rust, and the organization around it — including the archived predecessor repositories — is at github.com/hl7-rust. The ER7 encoding layer is elsewhere again: er7-rust/er7-rust, its own organization, so file encoding-level bugs there rather than here.

File against the workspace, not against a crate's archived former repository — those still carry the old hl7-v2* names and are kept only so their history stays reachable.

What to include in a report

  1. The crate and its version. cargo tree -p hl7-2 if you are not sure what resolved.
  2. A minimal message that reproduces it, redacted — see below. One segment is often enough.
  3. What you expected and what you got. The output of hl7-v2 --paths on the message is usually the clearest way to show both.
  4. The spec section, if you can find it. Each crate's spec/index.md is numbered specifically so it can be cited — “§4.2 says positional names are used here, but the output has …” turns a discussion into a fix.
  5. Your Rust version, if it is a build failure.

Do not paste patient data

HL7 messages are clinical records. An issue tracker is public and permanent, and a message pasted into one cannot be unpasted.

Redact identifying values; keep the structure

er7
MSH|^~\&|LAB|ACME|EHR|CLINIC|20260814080000||ORU^R01|MSG00042|P|2.5
PID|1||REDACTED^^^ACME&1.2.3.4&ISO^MR||REDACTED^REDACTED||REDACTED|F
OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|<200|N|||F

Structure is what reproduces a parsing bug — the delimiters, the field positions, the repetition separators, the component depth. Names, identifiers, dates of birth, and addresses are not. Replace them and the report still works.

If a bug genuinely depends on a specific byte sequence in a value — an unusual escape, a non-ASCII character set — describe the byte sequence rather than the record it appeared in.

How the repository is laid out

text
Cargo.toml           the workspace: [workspace] members, nothing else
Cargo.lock           one, at the root — never one inside a member
spec/                workspace-wide specs (the MSRV policy)
<crate>/
  Cargo.toml         the member's own manifest
  README.md          a tour
  spec/index.md      the normative specification, where behavior is normative
  AGENTS.md          conventions and required checks for changing this crate
  CLAUDE.md          points at AGENTS.md
  LICENSE.md         the same five-way boilerplate, byte for byte
  samples/           example inputs, where the crate has them

Most crate directories are former standalone repositories, merged in with git subtree so their commit history survived the move — which means git log <crate>/ reaches back before the workspace existed.

Member crates depend on each other by relative path, and the flat one-directory-per-crate layout was kept specifically so those paths did not have to change.

Conventions a change has to meet

Read the crate's own AGENTS.md before working in it — the workspace-level AGENTS.md covers only workspace-wide concerns. The checks every crate expects:

sh
cargo test                                # unit and integration tests
cargo clippy --all-targets -- -D warnings # lint-clean
cargo fmt --check                         # formatting
cargo rustdoc --lib -- -W missing-docs    # every public item documented

# And against the minimum supported Rust version
cargo +1.95 check --workspace --all-targets
  • One Cargo.lock, at the root. Do not add one inside a member directory.
  • No [workspace.package] inheritance or shared [workspace.dependencies] without discussion — either would touch every member's manifest at once.
  • MSRV is current stable minus three. Raising the floor is a breaking change and belongs in a release allowed to break.
  • Licensing boilerplate is byte-for-byte identical in every crate. Do not invent different text for a new one.
  • A change that spans crates updates every affected crate's AGENTS.md and spec/index.md in the same change.
  • Behavior changes go in the spec first. The spec is the source of truth, so a code change that contradicts it is either a bug fix or an unstated spec change.

Adding dictionary coverage

Dictionary gaps are the most useful contribution, and the cheapest: filling one means editing one JSON file under hl7-2/schemas/ and adding a test.

Add coverage when a real message motivates it, rather than speculatively — the crate's own spec says so, and a table filled in from the standard without a message behind it is a table nobody can check.

Before filing, consider whether it is a gap at all. An unmodelled difference between releases shows up as a positional name rather than a typed one, or as a missing warning — never as a rejected message or a lost value. That is honest incompleteness, and the spec says which differences each release currently claims.

Fixing this website

This site lives in the hl7-rust.github.io directory of the same workspace, built with SvelteKit's static adapter and the Lily Design System headless Svelte components. It deploys to GitHub Pages on every push to main.

Nothing here is normative — the site summarises the crates' READMEs and specs and links back to them. So a correction here is a documentation fix; a correction to the underlying behavior belongs against the crate.

Licensing your contribution

Everything in the workspace is offered under MIT, Apache-2.0, BSD-3-Clause, GPL-2.0-only, or GPL-3.0-only, at the user's option. A contribution is offered on the same terms, so that the choice stays available to everyone downstream.

Contact: joel@joelparkerhenderson.com.