Current section
Files
Jump to
Current section
Files
lib/parser/remote_ref.ex
defmodule Quenya.Parser.RemoteRef do
@moduledoc """
Update remote ref
"""
require Logger
alias Quenya.Parser.{Validator, Util}
@ref_table :ref_table
@spec update(map(), String.t()) :: {:ok, map()} | {:error, String.t()}
@doc """
Iterate the map, update:
1. inline update for file ref without path.
2. merge the remote file to current file for file ref with path. Update path as a local path.
"""
def update(data, dir) do
if :ets.whereis(@ref_table) == :undefined do
:ets.new(@ref_table, [:named_table, :set, :protected])
end
result =
Enum.reduce_while(data, %{}, fn {k, v}, acc ->
result =
Util.update_map(
dir,
v,
false,
fn context, path, recursive -> do_extend_ref(context, path, recursive) end
)
case result do
{:error, msg} -> {:halt, {:error, msg}}
_v -> {:cont, Map.put(acc, k, result)}
end
end)
case result do
{:error, msg} ->
:ets.delete(@ref_table)
{:error, msg}
_v ->
merged =
Enum.reduce(:ets.tab2list(@ref_table), result, fn {_k, v}, acc ->
DeepMerge.deep_merge(acc, v)
end)
:ets.delete(@ref_table)
{:ok, merged}
end
end
defp do_extend_ref(dir, path, _recursive) do
case String.split(path, "#") do
# reference whole file
[filename] ->
load_file(Path.join(dir, filename), false)
# reference local components
["", _p] ->
%{"$ref" => path}
# reference remote file with a path
[filename, p] ->
case load_file(Path.join(dir, filename), true) do
{:error, msg} -> {:error, msg}
_ -> %{"$ref" => "##{p}"}
end
_ ->
msg = "Unknown path: #{path}"
Logger.warn(msg)
{:error, msg}
end
end
defp load_file(filename, store?) do
case :ets.lookup(@ref_table, filename) do
[{^filename, data}] ->
data
_ ->
case Validator.validate(filename) do
{:ok, content} -> load_content(filename, content, store?)
{:error, _} -> {:error, :invalid_ref_file}
end
end
end
defp load_content(key, content, store?) do
case YamlElixir.read_from_string(content) do
{:ok, data} ->
if store? do
:ets.insert(@ref_table, {key, data})
end
data
{:error, _} ->
{:error, :invalid_ref_content}
end
end
end