The arguments are either name@version, fetched from the FHIR package registry into the local
package store along with the package's dependencies, or a folder holding an unpacked package (a
package/ subfolder or the files directly).
The first report is US Core 6.1.0 → 9.0.0. It compares 71 profiles and extensions: 12 added, 1 removed, 47 changed, with 66 breaking changes.
How it works
- Every StructureDefinition of both packages is converted to FHIR Schema.
- Each profile is merged over its whole base chain, down to the core type, into an effective element tree. A change inherited from a base profile counts as much as one the profile makes itself. The diff never trusts a differential on its own.
- The two trees of each profile (matched by canonical URL) are compared element by element,
slices included (
Patient.identifier:mrn.system). - Every change is given a rule and a severity.
Choice elements are compared as one element whatever form the profile uses to narrow them.
effective[x] restricted to dateTime and a constraint on effectiveDateTime compare equal.
Reference targets that move from a core type to a profile of the same type
(Practitioner → us-core-practitioner) are reported once as target-narrowed.
Rules
| Severity | Rules |
|---|---|
| breaking: an instance valid against the old profile may be invalid against the new one, or implementers gain an obligation | profile-removed, min-increased (including a new required slice), max-decreased, type-removed, target-removed, must-support-added, binding-stronger (to extensible or required), binding-added (required or extensible), invariant-added (error), fixed-added, fixed-changed, slicing-closed |
| warning: may break, depending on content the diff does not see | value-set-changed (under a required or extensible binding), invariant-changed, invariant-warning-added, target-narrowed, base-changed, slice-removed, modifier-changed |
| info: relaxations and additions | profile-added, min-decreased, max-increased, type-added, target-added, must-support-removed, binding-stronger-advisory (example → preferred), binding-weaker, invariant-removed, fixed-removed, slicing-opened, slice-added |
Options
| Option | |
|---|---|
--format text|markdown|json|sarif |
Text for a terminal or CI log (the default), Markdown for people, JSON for tools, SARIF 2.1.0 for code scanning |
--min-severity info|warning|breaking |
Report only changes at least this severe |
--fail-on never|warning|breaking |
Exit 1 when a change at least this severe is found (exit 2 on errors) |
--profile URL|NAME |
Compare only these profiles (repeatable) |
-o FILE |
Write the report to a file |
GitHub Actions
- run: fhir-diff hl7.fhir.us.core@9.0.0 ./output --fail-on breaking --format sarif -o fhir-diff.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: fhir-diff.sarif
SARIF results point at package/StructureDefinition-*.json. Their logical location is
<profile url>#<element path>.
Accuracy
cargo run --example selfcheck -- <package> compares each effective tree with the snapshot
the package publishes, on min, max and mustSupport of every element:
| Package | Elements | Agree |
|---|---|---|
hl7.fhir.us.core@9.0.0 |
2,455 | 99.4% |
hl7.fhir.us.core@6.1.0 |
2,133 | 99.3% |
Known gaps:
- Bases are looked for in the package's dependencies, transitively, while one is still missing;
value set and code system packages (
hl7.terminology.*, VSAC, PHIN VADS) are never read. When no dependency defines a base, it is taken from the other package's dependencies when it has one with the same canonical URL, and the profile is compared over the core type otherwise. The report says which under Notes. - mustSupport set on a choice through a type slice (
value[x]:valueCodeableConcept) shows on the slice, not on the choice. - Value set content is not compared. A different value set under a required binding is a warning, not an expansion diff.
Library
[]
= { = "0.1", = false }
Without default features the crate reads packages from folders or from definitions you hand it
(Loaded), and needs neither a network stack nor a package store. Features:
registry:load("name@version")installs from the FHIR package registry into the local package store (octofhir-canonical-manager).cli(default): thefhir-diffcommand; enablesregistry.
let = ;
let = pair;
let report = diff;
println!;
Install
Building
License
MIT OR Apache-2.0.