defmodule Kadena.Pact.Command.YamlReader do @moduledoc """ Reads YAML file to create a command. """ @type path :: String.t() @type root_path :: String.t() @type map_result :: map() @type processed_map :: {:ok, map_result()} | {:error, Keyword.t() | struct()} @spec read(path :: path()) :: processed_map() def read(path) do root_path = Path.dirname(path) with {:ok, map_result} <- YamlElixir.read_from_file(path, atoms: true), {:ok, map_result} <- process_code(map_result, root_path) do process_data(map_result, root_path) end end @spec process_code(map_result :: map_result(), root_path :: root_path()) :: processed_map() defp process_code(%{"codeFile" => code_file} = map_result, root_path) when is_binary(code_file) do case String.match?(code_file, ~r{^.*\.(pact)$}) do true -> map_result |> Map.put("code", File.read!(root_path <> "/#{code_file}")) |> Map.delete("codeFile") |> (&{:ok, &1}).() false -> {:error, [code_file: :not_a_pact_file]} end end defp process_code(%{"codeFile" => nil} = map_result, _root_path), do: {:ok, Map.delete(map_result, "codeFile")} defp process_code(%{"code" => code} = map_result, _root_path) when is_binary(code), do: {:ok, map_result} defp process_code(%{"code" => nil} = map_result, _root_path), do: {:ok, Map.delete(map_result, "code")} defp process_code(map_result, _root_path), do: {:ok, map_result} @spec process_data(map_result :: map_result(), root_path :: root_path()) :: processed_map() defp process_data(%{"dataFile" => data_file} = yaml, root_path) when is_binary(data_file) do case String.match?(data_file, ~r{^.*\.(json)$}) do true -> (root_path <> "/#{data_file}") |> File.read!() |> Jason.decode!() |> (&Map.put(yaml, "data", &1)).() |> Map.delete("dataFile") |> (&{:ok, &1}).() false -> {:error, [data_file: :not_a_json_file]} end end defp process_data(%{"dataFile" => nil} = map_result, _root_path), do: {:ok, Map.delete(map_result, "dataFile")} defp process_data(%{"data" => %{}} = map_result, _root_path), do: {:ok, map_result} defp process_data(%{"data" => nil} = map_result, _root_path), do: {:ok, Map.delete(map_result, "data")} defp process_data(map_result, _root_path), do: {:ok, map_result} end