Packages
croma
0.1.3
0.13.0
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Elixir macro utilities to make type-based programming easier
Current section
Files
Jump to
Current section
Files
lib/croma/subtype.ex
import Croma.Defun
alias Croma.Result, as: R
defmodule Croma.SubtypeOfInt do
defmacro __using__(opts) do
quote do
@min unquote(opts[:min])
@max unquote(opts[:max])
if !is_nil(@min) && !is_integer(@min), do: raise ":min must be either nil or integer"
if !is_nil(@max) && !is_integer(@max), do: raise ":max must be either nil or integer"
if is_nil(@min) && is_nil(@max) , do: raise ":min and/or :max must be given"
if @min && @max && @max < @min , do: raise ":min must be smaller than :max"
cond do
is_nil(@min) ->
cond do
@max <= -1 -> @type t :: neg_integer
true -> @type t :: integer
end
defun validate(term: any) :: R.t(t) do
i when is_integer(i) and i <= @max -> {:ok, i}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
is_nil(@max) ->
cond do
1 <= @min -> @type t :: pos_integer
0 == @min -> @type t :: non_neg_integer
true -> @type t :: integer
end
defun validate(term: any) :: R.t(t) do
i when is_integer(i) and @min <= i -> {:ok, i}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
true ->
@type t :: unquote(opts[:min]) .. unquote(opts[:max])
defun validate(term: any) :: R.t(t) do
i when is_integer(i) and @min <= i and i <= @max -> {:ok, i}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
end
@default unquote(opts[:default])
if @default do
if !is_integer(@default) , do: raise ":default must be an integer"
if !is_nil(@min) && @default < @min, do: raise ":default must be a valid value"
if !is_nil(@max) && @max < @default, do: raise ":default must be a valid value"
defun default :: t, do: @default
end
end
end
end
defmodule Croma.SubtypeOfFloat do
defmacro __using__(opts) do
quote do
@min unquote(opts[:min])
@max unquote(opts[:max])
if !is_nil(@min) && !is_float(@min), do: raise ":min must be either nil or float"
if !is_nil(@max) && !is_float(@max), do: raise ":max must be either nil or float"
if is_nil(@min) && is_nil(@max) , do: raise ":min and/or :max must be given"
if @min && @max && @max < @min , do: raise ":min must be smaller than :max"
@type t :: float
cond do
is_nil(@min) ->
defun validate(term: any) :: R.t(t) do
f when is_float(f) and f <= @max -> {:ok, f}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
is_nil(@max) ->
defun validate(term: any) :: R.t(t) do
f when is_float(f) and @min <= f -> {:ok, f}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
true ->
defun validate(term: any) :: R.t(t) do
f when is_float(f) and @min <= f and f <= @max -> {:ok, f}
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
end
@default unquote(opts[:default])
if @default do
if !is_float(@default) , do: raise ":default must be a float"
if !is_nil(@min) && @default < @min, do: raise ":default must be a valid value"
if !is_nil(@max) && @max < @default, do: raise ":default must be a valid value"
defun default :: t, do: @default
end
end
end
end
defmodule Croma.SubtypeOfString do
defmacro __using__(opts) do
quote do
@pattern unquote(opts[:pattern])
if !Regex.regex?(@pattern), do: raise ":pattern must be a regex"
@type t :: String.t
defun validate(s: term) :: R.t(t) do
s when is_binary(s) ->
if Regex.match?(@pattern, s) do
{:ok, s}
else
{:error, "validation error for #{__MODULE__}: #{inspect s}"}
end
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
@default unquote(opts[:default])
if @default do
if !Regex.match?(@pattern, @default), do: raise ":default must be a valid string"
defun default :: t, do: @default
end
end
end
end
defmodule Croma.SubtypeOfAtom do
defp values_as_typespec([v ]), do: v
defp values_as_typespec([h|t]), do: {:|, [], [h, values_as_typespec(t)]}
defmacro __using__(opts) do
value_atoms = opts[:values] || raise ":values must be present"
if Enum.empty?(value_atoms), do: raise ":values must be present"
value_strings = Enum.map(value_atoms, &Atom.to_string/1)
values_as_typespec = values_as_typespec(value_atoms)
quote do
@type t :: unquote(values_as_typespec)
defun validate(term: any) :: R.t(t) do
a when is_atom(a) ->
if a in unquote(value_atoms) do
{:ok, a}
else
{:error, "validation error for #{__MODULE__}: #{inspect a}"}
end
s when is_binary(s) ->
if s in unquote(value_strings) do
{:ok, String.to_existing_atom(s)}
else
{:error, "validation error for #{__MODULE__}: #{inspect s}"}
end
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
@default unquote(opts[:default])
if @default do
unless @default in unquote(value_atoms), do: raise ":default must be a valid atom"
defun default :: t, do: @default
end
end
end
end
defmodule Croma.SubtypeOfList do
defmacro __using__(opts) do
mod = opts[:elem_module] || raise ":elem_module must be given"
quote do
@type t :: [unquote(mod).t]
defun validate(term: any) :: R.t(t) do
l when is_list(l) ->
require R
R.m do
elems <- Enum.map(l, &unquote(mod).validate/1) |> R.sequence
if valid_length?(length(elems)) do
{:ok, elems}
else
{:error, "validation error for #{__MODULE__}: #{inspect l}"}
end
end
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
@min unquote(opts[:min_length])
@max unquote(opts[:max_length])
cond do
is_nil(@min) && is_nil(@max) ->
defp valid_length?(_), do: true
is_nil(@min) ->
defp valid_length?(len), do: len <= @max
is_nil(@max) ->
defp valid_length?(len), do: @min <= len
true ->
defp valid_length?(len), do: @min <= len && len <= @max
end
@default unquote(opts[:default])
if @default do
if Enum.any?(@default, fn e -> unquote(mod).validate(e) |> R.error? end), do: raise ":default must be a valid list"
len = length(@default)
if !is_nil(@min) && len < @min, do: raise ":default must be a valid list"
if !is_nil(@max) && @max < len, do: raise ":default must be a valid list"
defun default :: t, do: @default
end
end
end
end