lib/kvasir/type/uri.ex

defmodule Kvasir.Type.URI do
  @moduledoc ~S"""
  Any valid URI.

  The URI validation can enforce specific `host` or `scheme` values,
  by default `host` and `scheme` can not be `nil`.

  The following type of checks are allow:

    - `nil`, everything but `nil` allowed.
    - `binary`, any string to match.
    - `regex`, any regex to match.
    - `list`, any list of (string) values.
  """
  use Kvasir.Type

  @impl Kvasir.Type
  def parse(value, opts \\ [])

  def parse(uri = %URI{host: host, scheme: scheme}, opts) do
    cond do
      not eq?(host, opts[:host]) -> {:error, :invalid_uri_host}
      not eq?(scheme, opts[:scheme]) -> {:error, :invalid_uri_scheme}
      true -> {:ok, uri}
    end
  end

  def parse(value, opts) when is_binary(value), do: value |> Elixir.URI.parse() |> parse(opts)
  def parse(_value, _opts), do: {:error, :invalid_uri_format}

  @impl Kvasir.Type
  def dump(value, _opts \\ []), do: {:ok, to_string(value)}

  @impl Kvasir.Type
  def obfuscate(value, opts \\ []), do: value |> to_string |> Kvasir.Type.String.obfuscate(opts)

  @spec eq?(term, nil | String.t() | Regex.t() | list) :: boolean
  defp eq?(nil, _), do: false
  defp eq?(_, nil), do: true
  defp eq?(value, match) when is_binary(match), do: value == match
  defp eq?(value, match = %Regex{}), do: value =~ match
  defp eq?(value, match) when is_list(match), do: value in match
end