defmodule Spec do

  @doc false
  defmacro __using__(_) do
    quote do
      import Spec
      import Spec.Fn
      import Spec.Defspec
      import Spec.Key
      import Spec.Regex
      import Spec.Kernel
    end
  end

  @spec conformer(Spec.Conformer.fun, Spec.Conformer.fun) :: Spec.Transformer.t
  def conformer(conformer, unformer \\ fn x -> x end) do
    %Spec.Transform{conformer: conformer, unformer: unformer}
  end

  @spec unform(Spec.Conformer.quoted, any) :: Spec.Conformer.result
  def unform(spec, value) do
    Spec.Transformer.unform(spec, value)
  end

  @spec conform(Spec.Conformer.quoted, any) :: Spec.Conformer.result
  defmacro conform(spec, value) do
    spec = Spec.Quoted.spec(spec)
    quote bind_quoted: [spec: spec, value: value] do
      Spec.Transformer.conform(spec, value)
    end
  end

  @spec conform!(Spec.Conformer.quoted, any) :: any
  defmacro conform!(spec, value) do
    quote do
      unquote(__MODULE__).conform(unquote(spec), unquote(value))
      |> case do
        {:ok, conformed} -> conformed
        {:error, mismatch = %Spec.Mismatch{}} -> raise mismatch
      end
    end
  end

  @spec valid?(Spec.Conformer.quoted, any) :: boolean
  defmacro valid?(spec, value) do
    quote do
      unquote(__MODULE__).conform(unquote(spec), unquote(value))
      |> Spec.Kernel.ok?
    end
  end

end
