Skip to content

Commit 7d6ff6d

Browse files
remove secret index, better parse (HCA-Healthcare#41)
* remove secret index, better parse * refactor tests * Add task for generating HL7.PathParser such that the parser is included pre-compiled obviating the need to have nimble_parsec as a dep for consumers. --------- Co-authored-by: Bryan Paxton <[email protected]>
1 parent 3faaf75 commit 7d6ff6d

8 files changed

+883
-179
lines changed

benchmark.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ list = msg |> Message.to_list()
88

99
Benchee.run(%{
1010
"raw" => fn -> msg |> Message.raw() end,
11-
"maps" => fn -> msg |> HL7.Maps.new() end,
11+
"new-map" => fn -> msg |> HL7.new!() end,
1212
"new" => fn -> msg |> Message.new() end,
1313
"new-copy" => fn -> msg |> Message.new(%{copy: true}) end,
1414
"new-alt" => fn -> alt |> Message.new() end,

lib/hl7.ex

+42-88
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
defmodule HL7 do
22
@moduledoc """
3-
Utility functions to load HL7 files as local streams.
3+
Functions to manipulate and query HL7 documents -- plus a way to handle HL7 file streams.
44
"""
55

66
defstruct segments: []
@@ -108,9 +108,17 @@ defmodule HL7 do
108108
@doc ~S"""
109109
Creates an HL7 struct from HL7 data (accepting text, lists or the deprecated `HL7.Message` struct).
110110
"""
111-
@spec new!(hl7_list_data() | String.t() | HL7.Message.t()) :: t()
111+
@spec new!(hl7_list_data() | String.t() | HL7.Message.t() | [segment()]) :: t()
112112
def new!(segments) when is_list(segments) do
113-
segments = Enum.map(segments, fn segment -> to_map(%{}, 0, segment) end)
113+
segments =
114+
Enum.map(
115+
segments,
116+
fn
117+
segment when is_list(segment) -> to_map(%{}, 0, segment)
118+
%{0 => _} = segment -> segment
119+
end
120+
)
121+
114122
%__MODULE__{segments: segments}
115123
end
116124

@@ -136,40 +144,31 @@ defmodule HL7 do
136144
end
137145

138146
def put(segment_data, %Path{} = path, value) do
139-
segment_data
140-
|> cap_nested_input_map()
141-
|> do_put(path, value)
142-
|> uncap_nested_output_map()
147+
segment_data |> do_put(path, value)
143148
end
144149

145150
def update(%HL7{segments: segments} = hl7, %Path{} = path, default, fun) do
146151
%HL7{hl7 | segments: update(segments, path, default, fun)}
147152
end
148153

149154
def update(segment_data, path, default, fun) do
150-
segment_data
151-
|> cap_nested_input_map()
152-
|> do_put(path, {default, fun})
153-
|> uncap_nested_output_map()
155+
segment_data |> do_put(path, {default, fun})
154156
end
155157

156158
def update!(%HL7{segments: segments} = hl7, %Path{} = path, fun) do
157159
%HL7{hl7 | segments: update!(segments, path, fun)}
158160
end
159161

160162
def update!(segment_data, path, fun) do
161-
segment_data
162-
|> cap_nested_input_map()
163-
|> do_put(path, {fun})
164-
|> uncap_nested_output_map()
163+
segment_data |> do_put(path, {fun})
165164
end
166165

167166
def get_segments(%HL7{segments: segments}) do
168-
uncap_nested_output_map(segments)
167+
segments
169168
end
170169

171170
def set_segments(%HL7{} = hl7, segments) do
172-
%HL7{hl7 | segments: cap_nested_input_map(segments)}
171+
%HL7{hl7 | segments: segments}
173172
end
174173

175174
@doc ~S"""
@@ -252,7 +251,6 @@ defmodule HL7 do
252251
data
253252
|> do_get(path)
254253
|> maybe_truncate(path)
255-
|> uncap_nested_output_map()
256254
end
257255

258256
@doc """
@@ -266,7 +264,6 @@ defmodule HL7 do
266264
@spec chunk_by_lead_segment(t() | [segment()], String.t(), Keyword.t()) :: [[segment()]]
267265
def chunk_by_lead_segment(segments, segment_name, options \\ []) do
268266
do_chunk_by_segment(segments, segment_name)
269-
|> uncap_nested_output_map()
270267
|> maybe_keep_prefix_segments(!!options[:keep_prefix_segments])
271268
end
272269

@@ -279,11 +276,11 @@ defmodule HL7 do
279276
end
280277

281278
def to_list(map_data) when is_list(map_data) do
282-
Enum.map(map_data, fn segment_map -> do_to_list(cap_nested_input_map(segment_map)) end)
279+
Enum.map(map_data, fn segment_map -> do_to_list(segment_map) end)
283280
end
284281

285282
def to_list(map_data) when is_map(map_data) do
286-
do_to_list(cap_nested_input_map(map_data))
283+
do_to_list(map_data)
287284
end
288285

289286
@doc """
@@ -325,46 +322,8 @@ defmodule HL7 do
325322

326323
# internals
327324

328-
defp get_max_index(%{__max_index__: max_index}) do
329-
max_index
330-
end
331-
332325
defp get_max_index(data) when is_map(data) do
333-
data |> Map.keys() |> Enum.max() |> max(1)
334-
end
335-
336-
defp cap_map(map, index) do
337-
Map.put(map, :__max_index__, max(map[:__max_index__] || 1, index))
338-
end
339-
340-
defp cap_nested_input_map(%{__max_index__: _} = data) do
341-
Map.new(data, fn {k, v} -> {k, cap_nested_input_map(v)} end)
342-
end
343-
344-
defp cap_nested_input_map(data) when is_map(data) do
345-
max_index = data |> Map.keys() |> Enum.max() |> max(1)
346-
347-
data
348-
|> Map.put(:__max_index__, max_index)
349-
|> Map.new(fn {k, v} -> {k, cap_nested_input_map(v)} end)
350-
end
351-
352-
defp cap_nested_input_map(data) do
353-
data
354-
end
355-
356-
defp uncap_nested_output_map(data) when is_map(data) do
357-
data
358-
|> Map.delete(:__max_index__)
359-
|> Map.new(fn {k, v} -> {k, uncap_nested_output_map(v)} end)
360-
end
361-
362-
defp uncap_nested_output_map(data) when is_list(data) do
363-
Enum.map(data, &uncap_nested_output_map/1)
364-
end
365-
366-
defp uncap_nested_output_map(data) do
367-
data
326+
data |> Map.keys() |> Enum.max() |> max(0)
368327
end
369328

370329
defp to_map(value) when is_binary(value) do
@@ -375,8 +334,8 @@ defmodule HL7 do
375334
to_map(%{}, 1, value)
376335
end
377336

378-
defp to_map(acc, index, []) do
379-
Map.put(acc, :__max_index__, index - 1)
337+
defp to_map(acc, index, [h]) do
338+
Map.put(acc, index, to_map(h))
380339
end
381340

382341
defp to_map(acc, index, [h | t]) do
@@ -392,7 +351,7 @@ defmodule HL7 do
392351
end
393352

394353
def do_to_list(hl7_map_data) do
395-
do_to_list([], hl7_map_data, hl7_map_data[:__max_index__])
354+
do_to_list([], hl7_map_data, get_max_index(hl7_map_data))
396355
end
397356

398357
defp do_to_list(acc, %{0 => _} = hl7_map_data, index) when index > -1 do
@@ -440,10 +399,6 @@ defmodule HL7 do
440399
|> Enum.reverse()
441400
end
442401

443-
defp get_value_at_index(%{__max_index__: max_index} = _segment_data, i) when i > max_index do
444-
nil
445-
end
446-
447402
defp get_value_at_index(nil, _) do
448403
nil
449404
end
@@ -461,16 +416,16 @@ defmodule HL7 do
461416
end
462417

463418
defp get_value_at_index(segment_data, i) do
464-
Map.get(segment_data, i, "")
419+
max_index = get_max_index(segment_data)
420+
if i > max_index, do: nil, else: Map.get(segment_data, i, "")
465421
end
466422

467-
defp ensure_map(data, index) when is_binary(data) or is_nil(data) do
423+
defp ensure_map(data) when is_binary(data) or is_nil(data) do
468424
%{1 => data}
469-
|> Map.put(:__max_index__, max(1, index))
470425
end
471426

472-
defp ensure_map(data, index) when is_map(data) do
473-
cap_map(data, index)
427+
defp ensure_map(data) when is_map(data) do
428+
data
474429
end
475430

476431
defp maybe_truncate(segment_data, %Path{truncate: true}) do
@@ -495,23 +450,23 @@ defmodule HL7 do
495450
end
496451

497452
defp resolve_placement_value(_field_data = nil, {default, _fun}, _path) do
498-
default |> cap_nested_input_map()
453+
default
499454
end
500455

501456
defp resolve_placement_value(field_data, {_default, fun}, _path) do
502-
fun.(field_data) |> cap_nested_input_map()
457+
fun.(field_data)
503458
end
504459

505460
defp resolve_placement_value(_field_data = nil, {_fun}, path) do
506461
raise KeyError, message: "HL7.Path #{inspect(path)} could not be found"
507462
end
508463

509464
defp resolve_placement_value(field_data, {fun}, _path) do
510-
fun.(field_data) |> cap_nested_input_map()
465+
fun.(field_data)
511466
end
512467

513468
defp resolve_placement_value(_field_data, value, _path) do
514-
value |> cap_nested_input_map()
469+
value
515470
end
516471

517472
defp do_get(%HL7{} = hl7, %Path{} = path) do
@@ -658,7 +613,6 @@ defmodule HL7 do
658613

659614
defp do_put_in_segment(segment_data, value, %{field: f} = path) do
660615
Map.put(segment_data, f, do_put_in_field(segment_data[f], value, path))
661-
|> cap_map(f)
662616
end
663617

664618
defp do_put_in_field(field_data, value, %{repetition: "*", component: nil} = path) do
@@ -668,38 +622,31 @@ defmodule HL7 do
668622
defp do_put_in_field(field_data, value, %{repetition: "*"} = path) do
669623
1..get_max_index(field_data)
670624
|> Map.new(fn i ->
671-
{i, do_put_in_repetition(ensure_map(field_data[i], i), value, path)}
625+
{i, do_put_in_repetition(ensure_map(field_data[i]), value, path)}
672626
end)
673-
|> Map.put(:__max_index__, field_data[:__max_index__])
674627
end
675628

676629
defp do_put_in_field(field_data, value, %{repetition: r} = path) do
677-
field_map = ensure_map(field_data, r)
678-
630+
field_map = ensure_map(field_data)
679631
Map.put(field_map, r, do_put_in_repetition(field_map[r], value, path))
680-
|> cap_map(r)
681632
end
682633

683634
defp do_put_in_repetition(repetition_data, value, %{component: nil} = path) do
684635
resolve_placement_value(repetition_data, value, path)
685636
end
686637

687638
defp do_put_in_repetition(repetition_data, value, %{component: c} = path) do
688-
repetition_map = ensure_map(repetition_data, c)
689-
639+
repetition_map = ensure_map(repetition_data)
690640
Map.put(repetition_map, c, do_put_in_component(repetition_map[c], value, path))
691-
|> cap_map(c)
692641
end
693642

694643
defp do_put_in_component(component_data, value, %{subcomponent: nil} = path) do
695644
resolve_placement_value(component_data, value, path)
696645
end
697646

698647
defp do_put_in_component(subcomponent_data, value, %{subcomponent: s} = path) do
699-
subcomponent_map = ensure_map(subcomponent_data, s)
700-
648+
subcomponent_map = ensure_map(subcomponent_data)
701649
Map.put(subcomponent_map, s, resolve_placement_value(subcomponent_map[s], value, path))
702-
|> cap_map(s)
703650
end
704651

705652
defp do_label(segment_data, %Path{} = output_param) do
@@ -772,3 +719,10 @@ defimpl Inspect, for: HL7 do
772719
end
773720
end
774721
end
722+
723+
defimpl String.Chars, for: HL7 do
724+
@spec to_string(HL7.t()) :: String.t()
725+
def to_string(%HL7{} = hl7) do
726+
hl7 |> HL7.to_list() |> HL7.Message.raw() |> Map.get(:raw)
727+
end
728+
end

0 commit comments

Comments
 (0)