Packages
hl7v2
3.9.0
3.10.1
3.9.0
3.8.0
3.7.0
3.6.0
3.5.0
3.4.0
3.3.6
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.0
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
2.11.0
2.10.0
2.9.1
2.9.0
2.8.2
2.8.1
2.8.0
2.7.1
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.3
2.1.2
2.1.1
2.1.0
1.4.6
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
0.6.0
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.1.0
Pure Elixir HL7 v2.x toolkit — schema-driven parsing, typed segments, message builder, MLLP transport
Current section
Files
Jump to
Current section
Files
lib/hl7v2.ex
defmodule HL7v2 do
@moduledoc """
Pure Elixir HL7 v2.x toolkit.
Schema-driven parsing, typed segment structs, programmatic message
building, validation, and integrated MLLP transport.
## Parsing
# Raw mode — canonical round-trip, delimiter-based
{:ok, raw} = HL7v2.parse(text)
# Typed mode — segments become structs
{:ok, msg} = HL7v2.parse(text, mode: :typed)
## Building
msg =
HL7v2.new("ADT", "A01", sending_application: "PHAOS")
|> HL7v2.Message.add_segment(%HL7v2.Segment.PID{...})
text = HL7v2.encode(msg)
## Acknowledgments
{:ok, typed} = HL7v2.parse(wire, mode: :typed)
msh = hd(typed.segments)
{ack_msh, msa} = HL7v2.ack(msh)
## MLLP Transport
{:ok, _} = HL7v2.MLLP.Listener.start_link(port: 2575, handler: MyHandler)
"""
alias HL7v2.{Parser, Telemetry}
@doc """
Parses an HL7v2 message from a binary string.
## Options
- `:mode` — `:raw` (default) or `:typed`
- `:validate` — `true` to validate after parsing (default `false`).
Requires `mode: :typed`; silently ignored in `:raw` mode.
- `:copy` — `true` to copy all parsed binaries (prevents GC reference to original
message binary). Use when storing parsed messages long-term. Default `false`.
## Examples
{:ok, msg} = HL7v2.parse("MSH|^~\\\\&|...")
{:ok, msg} = HL7v2.parse(text, mode: :typed, validate: true)
{:ok, msg} = HL7v2.parse(text, copy: true)
"""
@spec parse(binary(), keyword()) :: {:ok, term()} | {:ok, term(), [map()]} | {:error, term()}
def parse(text, opts \\ [])
def parse(text, _opts) when not is_binary(text), do: {:error, :invalid_input}
def parse(text, opts) do
mode = Keyword.get(opts, :mode, :raw)
validate? = Keyword.get(opts, :validate, false)
Telemetry.span(:parse, %{mode: mode}, fn ->
with {:ok, msg} <- Parser.parse(text, opts) do
if validate? and mode == :typed do
validate_opts = Keyword.take(opts, [:mode, :validate_tables])
case HL7v2.Validation.validate(msg, validate_opts) do
:ok -> {:ok, msg}
{:ok, warnings} -> {:ok, msg, warnings}
{:error, _} = err -> err
end
else
{:ok, msg}
end
end
end)
end
@doc """
Encodes an HL7v2 message to wire format.
Accepts:
- `%HL7v2.RawMessage{}` — encodes directly
- `%HL7v2.Message{}` — converts to raw via `Message.encode/1`
- `%HL7v2.TypedMessage{}` — converts to raw via `TypedParser.to_raw/1`, then encodes
## Examples
wire = HL7v2.encode(raw_message)
wire = HL7v2.encode(builder_message)
wire = HL7v2.encode(typed_message)
"""
@spec encode(HL7v2.RawMessage.t() | HL7v2.Message.t() | HL7v2.TypedMessage.t()) :: binary()
def encode(%HL7v2.RawMessage{} = message) do
Telemetry.span(:encode, %{type: :raw}, fn ->
HL7v2.Encoder.encode(message)
end)
end
def encode(%HL7v2.Message{} = message) do
Telemetry.span(:encode, %{type: :message}, fn ->
HL7v2.Message.encode(message)
end)
end
def encode(%HL7v2.TypedMessage{} = message) do
Telemetry.span(:encode, %{type: :typed}, fn ->
message
|> HL7v2.TypedParser.to_raw()
|> HL7v2.Encoder.encode()
end)
end
@doc """
Validates an HL7v2 typed message.
Returns `:ok` when all validation rules pass, or `{:error, errors}` with
a list of error/warning maps. Requires a `HL7v2.TypedMessage` -- raw messages
return `{:error, :not_a_typed_message}`.
Version-aware rules (v2.7+ B-field exemptions, etc.) are driven by MSH-12
(`version_id`) by default. Pass `:version` to override this — useful for
validating messages that mis-declare their version or when cross-checking
a v2.5.1 payload against v2.7 conformance rules.
## Options
- `:mode` -- `:lenient` (default) or `:strict`
- `:validate_tables` -- `true` to check coded fields against HL7 tables
(default `false`)
- `:version` -- explicit HL7 version override (e.g. `"2.7"`). When provided,
version-specific rules use this value instead of the one extracted from
MSH-12. Invalid or unrecognized versions fall back to the MSH-12 value.
- `:profile` -- a `HL7v2.Profile` struct (or list of profiles) to validate
against in addition to the base schema. Profiles express organization-
specific constraints: required segments, required fields beyond the base
spec, table bindings, cardinality, value predicates, and custom rules.
Profile errors include a `:rule` (which rule type fired) and `:profile`
(profile name) for traceability.
## Examples
{:ok, msg} = HL7v2.parse(text, mode: :typed)
:ok = HL7v2.validate(msg)
# With table validation
{:error, errors} = HL7v2.validate(msg, validate_tables: true)
# Override the version read from MSH-12 and apply v2.7 rules instead
:ok = HL7v2.validate(msg, version: "2.7")
# Validate against a conformance profile
profile =
HL7v2.Profile.new("Hospital_ADT_A01", message_type: {"ADT", "A01"})
|> HL7v2.Profile.require_field("PID", 18)
|> HL7v2.Profile.require_cardinality("OBX", min: 1, max: 10)
HL7v2.validate(msg, profile: profile)
"""
@spec validate(term(), keyword()) ::
:ok | {:ok, [map()]} | {:error, [map()] | :not_a_typed_message}
def validate(message, opts \\ [])
def validate(%HL7v2.TypedMessage{} = message, opts) do
HL7v2.Validation.validate(message, opts)
end
def validate(_message, _opts) do
{:error, :not_a_typed_message}
end
@doc """
Builds a new HL7v2 message. Shortcut for `HL7v2.Message.new/3`.
## Options
* `:sending_application` — string or `%HD{}`
* `:sending_facility` — string or `%HD{}`
* `:receiving_application` — string or `%HD{}`
* `:receiving_facility` — string or `%HD{}`
* `:message_control_id` — string (default: auto-generated)
* `:processing_id` — string (default: `"P"`)
* `:version_id` — string (default: `"2.5.1"`)
## Examples
msg = HL7v2.new("ADT", "A01", sending_application: "PHAOS")
"""
@spec new(binary(), binary(), keyword()) :: HL7v2.Message.t()
def new(code, event, opts \\ []) do
HL7v2.Message.new(code, event, opts)
end
@doc "Gets a value from a typed message using a path string like `\"PID-5\"` or a `%HL7v2.Path{}` struct."
@spec get(HL7v2.TypedMessage.t(), binary() | HL7v2.Path.t()) :: term()
defdelegate get(msg, path), to: HL7v2.Access
@doc "Gets a value from a typed message with a default."
@spec get(HL7v2.TypedMessage.t(), binary() | HL7v2.Path.t(), term()) :: term()
defdelegate get(msg, path, default), to: HL7v2.Access
@doc """
Converts a `%HL7v2.RawMessage{}` to a `%HL7v2.TypedMessage{}`.
Shortcut for `HL7v2.TypedParser.convert/1`.
## Examples
{:ok, raw} = HL7v2.parse(wire)
{:ok, typed} = HL7v2.type(raw)
"""
@spec type(HL7v2.RawMessage.t()) :: {:ok, HL7v2.TypedMessage.t()} | {:error, term()}
def type(%HL7v2.RawMessage{} = raw) do
HL7v2.TypedParser.convert(raw)
end
@doc "Fetches a value, returning `{:ok, value}` or `{:error, reason}`."
@spec fetch(HL7v2.TypedMessage.t(), binary() | HL7v2.Path.t()) ::
{:ok, term()} | {:error, atom()}
defdelegate fetch(msg, path), to: HL7v2.Access
@doc """
Builds an ACK (Application Accept) for the given MSH segment.
Shortcut for `HL7v2.Ack.accept/2`.
## Options
* `:text` — optional text message for MSA-3
* `:message_control_id` — override the generated ACK message control ID
## Examples
{ack_msh, msa} = HL7v2.ack(original_msh)
"""
@spec ack(HL7v2.Segment.MSH.t(), keyword()) :: {HL7v2.Segment.MSH.t(), HL7v2.Segment.MSA.t()}
def ack(msh, opts \\ []) do
HL7v2.Ack.accept(msh, opts)
end
end