lib/uptight.ex

defmodule Uptight do
  @moduledoc """
  Facilities for tightening loosey goosey types.
  """
  alias Uptight.Text, as: T
  alias Uptight.Base, as: B
  alias Uptight.Binary
  alias Uptight.Result

  import Witchcraft.Functor

  @spec tighten(any) :: any
  def tighten(x) when is_binary(x) do
    candidate = T.new(x)

    candidate =
      if Result.is_err?(candidate) do
        %Result.Ok{ok: Binary.new!(x)}
      else
        candidate
      end

    candidate.ok
  end

  def tighten([x]) do
    [tighten(x)]
  end

  def tighten([x | rest]) do
    [tighten(x) | tighten(rest)]
  end

  def tighten({t, x}) do
    {t, tighten(x)}
  end

  def tighten(%{} = kv) do
    map(kv, &tighten/1)
  end

  def tighten(x), do: x

  @spec into_text_or_urlsafe(binary) :: T.t() | B.Urlsafe.t()
  def into_text_or_urlsafe(x) do
    case tighten(x) do
      x = %Binary{} -> B.binary_to_urlsafe(x)
      x = %T{} -> x
    end
  end
end