defmodule Mongo.Ecto.Regex do
  @moduledoc """
  An Ecto type to represent MongoDB's Regex objects

  Both MongoDB and Elixir use PCRE (Pearl Compatible Regular Expressions),
  so the syntax is similar for both.

  ## Using in queries

  MongoDB supports two ways to use regexes.

  One is to use regex object:

      regex = Mongo.Ecto.Helpers.regex("elixir", "i")
      from p in Post, where: fragment(title: ^regex)

  The other one is to use the `$regex` operator:

      from p in Post, where: fragment(title: ["$regex": "elixir", "$options": "i"])

  For more information about the differences between the two syntaxes please
  refer to the MongoDB documentation:
  http://docs.mongodb.org/manual/reference/operator/query/regex/#syntax-restrictions

  ## Options

    * `i` - case insensitive
    * `m` - causes ^ and $ to mark the beginning and end of each line
    * `x` - whitespace characters are ignored except when escaped
      and allow # to delimit comments, requires using `$regex` operator together
      with the `$option` operator
    * `s` - causes dot to match newlines, requires using `$regex` operator together
      with the `$option` operator
  """

  @behaviour Ecto.Type

  defstruct BSON.Regex |> Map.from_struct() |> Enum.to_list()
  @type t :: %__MODULE__{pattern: String.t(), options: String.t()}

  @doc """
  The Ecto primitive type.
  """
  def type, do: :any

  @doc """
  Casts to database format
  """
  def cast(%BSON.Regex{} = js), do: {:ok, js}
  def cast(_), do: :error

  @doc """
  Converts a `Mongo.Ecto.Regex` into `BSON.Regex`
  """
  def dump(%__MODULE__{} = js), do: {:ok, Map.put(js, :__struct__, BSON.Regex)}
  def dump(_), do: :error

  @doc """
  Converts a `BSON.Regex` into `Mongo.Ecto.Regex`
  """
  def load(%BSON.Regex{} = js), do: {:ok, Map.put(js, :__struct__, __MODULE__)}
  def load(_), do: :error

  def embed_as(_), do: :dump

  def equal?(a, b), do: a == b
end
