lib/fussy/validators/date_time.ex

defmodule Fussy.Validators.DateTime do
  @behaviour Fussy.Validator

  defstruct format: :iso8601

  alias Fussy.ValidationError

  @opaque t :: %__MODULE__{}

  @spec new() :: __MODULE__.t()
  def new(), do: %__MODULE__{}

  def validate(%__MODULE__{} = v, term), do: validate(v, [], term)

  @impl true
  def validate(%__MODULE__{}, _path, %DateTime{} = dt) do
    {:ok, dt}
  end

  @impl true
  def validate(%__MODULE__{format: :iso8601}, path, term) when is_binary(term) do
    case DateTime.from_iso8601(term) do
      {:ok, dt, _} ->
        {:ok, dt}

      {:error, _} ->
        {:error,
         [
           %ValidationError{
             mod: __MODULE__,
             msg: "must be a DateTime or an ISO8601 string",
             term: term,
             path: path
           }
         ]}
    end
  end

  @impl true
  def validate(%__MODULE__{format: :iso8601}, path, term),
    do:
      {:error,
       [
         %ValidationError{
           mod: __MODULE__,
           msg: "must be a DateTime or an ISO8601 string",
           term: term,
           path: path
         }
       ]}
end