lib/gcode/option.ex

defmodule Gcode.Option do
  @moduledoc """
  A helper which represents an optional type.
  """

  @spec __using__(any) :: Macro.t()
  defmacro __using__(_) do
    quote do
      alias Gcode.Option
      require Gcode.Option
      import Gcode.Option, only: [some: 1, none: 0]
    end
  end

  @type t :: t(any)
  @type t(value) :: some(value) | opt_none
  @type some(t) :: {:ok, t}
  @type opt_none :: :error

  @doc "Is the value a none?"
  @spec none?(t(any)) :: boolean
  def none?(:error), do: true
  def none?({:ok, _}), do: false

  @doc "Is the value a some?"
  @spec some?(t(any)) :: boolean
  def some?(:error), do: false
  def some?({:ok, _}), do: true

  @doc "Create or match a none"
  @spec none :: Macro.t()
  defmacro none do
    quote do
      :error
    end
  end

  @doc "Create or match a some"
  @spec some(any) :: Macro.t()
  defmacro some(pattern) do
    quote do
      {:ok, unquote(pattern)}
    end
  end

  @doc "Attempt to unwrap an option.  Raises an error if the option is a none"
  @spec unwrap!(t) :: any | no_return
  def unwrap!({:ok, result}), do: result
  def unwrap!(:error), do: raise("Attempt to unwrap a none")
end