Skip to content

Commit

Permalink
Simplify building primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
solnic committed Jan 19, 2024
1 parent 7f29c31 commit 0576c92
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 24 deletions.
44 changes: 26 additions & 18 deletions lib/drops/type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Drops.Type do
Type behaviour
"""

alias __MODULE__

defmacro __using__(do: block) do
quote do
import Drops.Type
Expand All @@ -18,22 +20,29 @@ defmodule Drops.Type do
import Drops.Type.DSL

deftype(
primitive: Drops.Type.infer_primitive(unquote(spec), []),
constraints: Drops.Type.infer_constraints(unquote(spec), [])
primitive: Type.infer_primitive(unquote(spec)),
constraints: Type.infer_constraints(unquote(spec))
)

def new(attributes) do
def new(attributes) when is_list(attributes) do
struct(__MODULE__, attributes)
end

def new(spec, opts) do
%__MODULE__{
primitive: infer_primitive(spec, opts),
constraints: infer_constraints(spec, opts)
}
def new(spec) do
new(
primitive: infer_primitive(spec),
constraints: infer_constraints(spec)
)
end

def new(spec, constraints) when is_list(constraints) do
new(
primitive: infer_primitive(spec),
constraints: infer_constraints({:type, {spec, constraints}})
)
end

defoverridable new: 1, new: 2
defoverridable new: 1
end
end

Expand All @@ -56,27 +65,26 @@ defmodule Drops.Type do

defmacro deftype(primitive, attributes) when is_atom(primitive) do
all_attrs =
[primitive: primitive, constraints: Drops.Type.infer_constraints(primitive, [])] ++
[primitive: primitive, constraints: Type.infer_constraints(primitive)] ++
attributes

quote do
deftype(unquote(all_attrs))
end
end

def infer_primitive([], []), do: :any
def infer_primitive(name, _opts) when is_atom(name), do: name
def infer_primitive({:type, {name, _}}, _opts), do: name
def infer_primitive([]), do: :any
def infer_primitive(name) when is_atom(name), do: name
def infer_primitive({:type, {name, _}}), do: name

def infer_constraints([], []), do: []
def infer_constraints(type, _opts) when is_atom(type), do: [predicate(:type?, [type])]
def infer_constraints([]), do: []
def infer_constraints(type) when is_atom(type), do: [predicate(:type?, [type])]

def infer_constraints({:type, {type, predicates}}, _opts)
when length(predicates) > 0 do
def infer_constraints({:type, {type, predicates}}) when length(predicates) > 0 do
{:and, [predicate(:type?, type) | Enum.map(predicates, &predicate/1)]}
end

def infer_constraints({:type, {type, []}}, _opts) do
def infer_constraints({:type, {type, []}}) do
[predicate(:type?, type)]
end

Expand Down
4 changes: 2 additions & 2 deletions lib/drops/type/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ defmodule Drops.Type.Compiler do
mod.new(opts)
end

def visit(spec, opts) when is_tuple(spec) do
Primitive.new(spec, opts)
def visit(spec, _opts) when is_tuple(spec) do
Primitive.new(spec)
end
end
11 changes: 7 additions & 4 deletions test/contract/validator_test.exs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
defmodule Drops.ValidatorTest do
use Drops.ContractCase
use Drops.Validator
alias Drops.Type.Compiler

describe "validate/3" do
test "validates a string" do
assert validate("foo", Compiler.visit({:type, {:string, []}}, []), path: []) ==
assert validate("foo", Types.Primitive.new(:string), path: []) ==
{:ok, {[], "foo"}}
end

test "validates an integer with constraints" do
assert validate(11, Compiler.visit({:type, {:integer, [:odd?]}}, []), path: []) ==
{:ok, {[], 11}}
type = Types.Primitive.new(:integer, [:odd?])

assert validate(11, type, path: []) == {:ok, {[], 11}}

assert validate("foo", type, path: []) == {:error, {[], :type?, [:integer, "foo"]}}
assert validate(12, type, path: []) == {:error, {[], :odd?, [12]}}
end
end
end

0 comments on commit 0576c92

Please sign in to comment.