lib/pact/command/yaml_reader.ex

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