lib/hl7/path.ex

defmodule HL7.Path do
  @derive Inspect

  defstruct [:data]

  @typep segment :: String.t()
  @typep indices :: [integer, ...]
  @type t :: %__MODULE__{
          data: {segment, indices} | indices
        }

  @doc """
  Builds an HL7.Path

  Field paths are used in `HL7.Query` functions to find specific fields in an `HL7.Message`.
  """
  @doc since: "0.8.0"
  @spec new(String.t()) :: t()
  def new(schema) when is_binary(schema) do
    use_repeat = String.contains?(schema, "[")
    use_segment = String.contains?(schema, "-")
    use_component = String.contains?(schema, ".")
    chunks = chunk_schema(schema)
    [head | tail] = chunks

    data =
      case use_segment do
        true ->
          [<<segment::binary-size(3)>> | indices] =
            case use_component && !use_repeat do
              false ->
                [head | tail |> Enum.map(&String.to_integer/1)]

              true ->
                [head | tail |> Enum.map(&String.to_integer/1) |> List.insert_at(1, 1)]
            end
            |> Enum.take(5)
            |> Enum.with_index()
            |> Enum.map(fn {v, i} -> if i > 1, do: v - 1, else: v end)

          {segment, indices}

        false ->
          case use_component && !use_repeat do
            false ->
              chunks |> Enum.map(&String.to_integer/1)

            true ->
              chunks |> Enum.map(&String.to_integer/1) |> List.insert_at(1, 1)
          end
          |> Enum.take(4)
          |> Enum.with_index()
          |> Enum.map(fn {v, i} -> if i > 0, do: v - 1, else: v end)
      end

    %__MODULE__{data: data}
  end

  @spec chunk_schema(String.t()) :: [String.t()]
  defp chunk_schema(schema) do
    Regex.split(~r{(\.|\-|\[|\]|\s)}, schema, include_captures: false, trim: true)
  end
end