lib/type_spec_helpers.ex

defmodule Patreon.TypeSpecHelpers do
  defp create_option_list_ast(struct_ast, keys_to_drop) do
    {_, _, struct_atoms} = struct_ast

    struct =
      Enum.join([:Elixir | struct_atoms], ".")
      |> String.to_atom()
      |> Kernel.struct()

      [last, prev | rest] =
        Map.drop(struct, [:__struct__])
        |> Map.drop(keys_to_drop)
        |> Map.keys()
        |> Enum.reverse()

      Enum.reduce(rest, {:|, [], [prev, last]}, &{:|, [], [&1, &2]})
  end

  defmacro struct_to_option_list(_name, struct_ast, keys_to_drop) do
    type = create_option_list_ast(struct_ast, keys_to_drop)
    quote do
      [unquote(type)]
    end
  end

  defmacro struct_to_allowed_option_type(name, struct_ast, keys_to_drop) do
    type = create_option_list_ast(struct_ast, keys_to_drop)

    quote do
      @type unquote(name) :: unquote(type)
    end
  end

end