Current section

Files

Jump to
hl7v2 lib hl7v2 validation field_rules.ex
Raw

lib/hl7v2/validation/field_rules.ex

defmodule HL7v2.Validation.FieldRules do
@moduledoc """
Field-level validation rules for HL7v2 typed segments.
For each typed segment struct, checks:
- Required fields (`:r` optionality) are not nil
- Repeating fields with bounded `max_reps` do not exceed the limit
"""
@doc """
Returns a list of field-level validation errors/warnings for a single segment.
Skips validation for:
- `HL7v2.Segment.ZXX` (site-defined, no typed fields)
- Raw tuples `{name, fields}` (not typed)
"""
@spec check(struct() | {binary(), list()}) :: [map()]
def check(%HL7v2.Segment.ZXX{}), do: []
def check({name, _fields}) when is_binary(name), do: []
def check(%{__struct__: module} = segment) do
location = module.segment_id()
field_defs = module.fields()
Enum.flat_map(field_defs, fn {_seq, name, _type, optionality, max_reps} ->
value = Map.get(segment, name)
required_errors(location, name, optionality, value) ++
max_reps_warnings(location, name, max_reps, value)
end)
end
defp required_errors(location, name, :r, value) do
if semantic_blank?(value) do
[
%{
level: :error,
location: location,
field: name,
message: "required field #{name} is missing"
}
]
else
[]
end
end
defp required_errors(_location, _name, _optionality, _value), do: []
# A value is semantically blank if it's nil, an empty list, or a struct
# where every field is nil (e.g., %XPN{} with all nil fields).
defp semantic_blank?(nil), do: true
defp semantic_blank?([]), do: true
defp semantic_blank?(list) when is_list(list) do
Enum.all?(list, &semantic_blank?/1)
end
defp semantic_blank?(struct) when is_struct(struct) do
struct
|> Map.from_struct()
|> Map.values()
|> Enum.all?(&is_nil/1)
end
defp semantic_blank?(_), do: false
defp max_reps_warnings(location, name, max_reps, value)
when is_integer(max_reps) and max_reps > 1 and is_list(value) do
if length(value) > max_reps do
[
%{
level: :warning,
location: location,
field: name,
message: "field #{name} has #{length(value)} repetitions, max allowed is #{max_reps}"
}
]
else
[]
end
end
defp max_reps_warnings(_location, _name, _max_reps, _value), do: []
end