Packages

ISO/HL7 FHIR Application Server

Current section

Files

Jump to
hl7 lib validation ref.ex
Raw

lib/validation/ref.ex

defmodule Xema.Ref do
@moduledoc """
This module contains a struct and functions to represent and handle
references.
"""
alias Xema.{Ref, Schema, Validator}
require Logger
@typedoc """
A reference contains a `pointer` and an optional `uri`.
"""
@type t :: %__MODULE__{
pointer: String.t(),
uri: URI.t() | nil
}
# @derive {Inspect, optional: [:pointer, :uri]}
defstruct pointer: nil,
uri: nil
@compile {:inline, fetch_from_opts!: 2, fetch_by_key!: 3}
@doc """
Creates a new reference from the given `pointer`.
"""
@spec new(String.t()) :: Ref.t()
def new(pointer), do: %Ref{pointer: pointer}
@doc """
Creates a new reference from the given `pointer` and `uri`.
"""
@spec new(String.t(), URI.t() | nil) :: Ref.t()
def new(%Xema.Ref{
pointer: pointer,
uri: _uri
}, _), do: new(pointer)
def new("#" <> _ = pointer, _uri), do: new(pointer)
def new(pointer, uri) when is_binary(pointer),
do: %Ref{
pointer: pointer,
uri: Xema.Behaviour.update_uri(uri, pointer)
}
@doc """
Validates the given value with the referenced schema.
"""
@spec validate(Ref.t(), any, keyword) ::
:ok | {:error, map}
def validate(ref, value, opts) do
{schema, opts} = fetch_from_opts!(ref, opts)
Validator.validate(schema, value, opts)
end
@doc """
Returns the schema and the root for the given `ref` and `xema`.
"""
@spec fetch!(Ref.t(), struct, struct | nil) :: {struct | atom, struct}
def fetch!(ref, master, root) do
case fetch_by_key!(key(ref), master, root) do
{%Schema{}, _root} = schema ->
schema
{:root, root} ->
{%Schema{}, root} # 5HT
{xema, root} ->
case fragment(ref) do
nil ->
{xema, root}
fragment ->
{Map.fetch!(xema.refs, fragment), xema}
end
_ ->
:io.format 'Undhandled Case: ~p~n', [{key(ref)}]
end
end
@doc """
Returns the reference key for a `Ref` or an `URI`.
"""
@spec key(ref :: Ref.t() | URI.t()) :: String.t()
def key(%Ref{pointer: pointer, uri: nil}), do: pointer
def key(%Ref{uri: uri}), do: key(uri)
def key(%URI{} = uri), do: uri |> Map.put(:fragment, nil) |> URI.to_string()
def fragment(%Ref{uri: nil}), do: nil
def fragment(%Ref{uri: %URI{fragment: nil}}), do: nil
def fragment(%Ref{uri: %URI{fragment: ""}}), do: nil
def fragment(%Ref{uri: %URI{fragment: fragment}}), do: "##{fragment}"
defp fetch_from_opts!(%Ref{pointer: "#", uri: nil}, opts),
do: {opts[:root], opts}
defp fetch_from_opts!(%Ref{pointer: pointer, uri: nil}, opts),
do: {Map.fetch!(opts[:root].refs, pointer), opts}
defp fetch_from_opts!(%Ref{} = ref, opts) do
case fetch!(ref, opts[:master], opts[:root]) do
{:root, root} ->
{root, Keyword.put(opts, :root, root)}
{%Schema{} = schema, root} ->
{schema, Keyword.put(opts, :root, root)}
{xema, _} ->
{xema, Keyword.put(opts, :root, xema)}
end
end
defp fetch_by_key!("#", master, nil), do: {master, master}
defp fetch_by_key!("#", _master, root), do: {root, root}
defp fetch_by_key!(key, master, nil) do
{Map.fetch!(master.refs, key), master}
end
defp fetch_by_key!(key, master, root) do
case Map.get(root.refs, key) do
nil ->
nk = normalize(key)
case Map.get(master.refs, nk) do
nil ->
%{schema: schema3} = HL7.Loader.loadSchema(nk)
{Map.fetch!(schema3.definitions, nk), root}
_ -> {Map.fetch!(master.refs, key), root}
end
schema -> {schema, root}
end
end
def normalize(key) do
:erlang.iolist_to_binary(hd(:string.tokens(:erlang.binary_to_list(:filename.basename(key)),'.')))
end
end