Skip to content

Commit

Permalink
Merge branch 'fix-custom-type-spec' into cdc-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
fishtreesugar committed Apr 17, 2024
2 parents 75bca45 + 0682efd commit 9034fe4
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 19 deletions.
36 changes: 18 additions & 18 deletions lib/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -138,30 +138,30 @@ defmodule Strukt.Typespec do
defp type_to_type_name(:date),
do: compose_call(Date, :t, [])

defp type_to_type_name({:__aliases__, _, parts} = ast) do
case Module.concat(parts) do
defp type_to_type_name(mod) when is_atom(mod) and not is_nil(mod) do
case mod do
Ecto.Enum ->
primitive(:atom)

Ecto.UUID ->
primitive(:string)
compose_call(Ecto.UUID, :t, [])

mod ->
with {:module, _} <- Code.ensure_compiled(mod) do
try do
if Kernel.Typespec.defines_type?(mod, {:t, 0}) do
compose_call(ast, :t, [])
else
# No t/0 type defined, so fallback to any/0
primitive(:any)
end
rescue
ArgumentError ->
# We shouldn't hit this branch, but if Elixir can't find module metadata
# during defines_type?, it raises ArgumentError, so we handle this like the
# other pessimistic cases
primitive(:any)
end
with {:module, _} <- Code.ensure_compiled(mod),
{:ok, mod_types} <- Code.Typespec.fetch_types(mod),
t0 when not is_nil(t0) <-
Enum.find(
mod_types,
&match?(
{kind, {:t, _, args}} when kind in [:type, :opaque] and length(args) == 0,
&1
)
) do
compose_call(
{:__aliases__, [alias: false], Enum.map(Module.split(mod), &String.to_atom/1)},
:t,
[]
)
else
_ ->
# Module is unable to be loaded, either due to compiler deadlock, or because
Expand Down
20 changes: 20 additions & 0 deletions test/strukt_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,26 @@ defmodule Strukt.Test do
Fixtures.EmbedsOneTypeSpec.expected_type_spec_ast_str()
end

test "custom ecto type" do
require Fixtures.CustomEctoTypeTypeSepc

assert inspect(
Strukt.Typespec.generate(%Strukt.Typespec{
caller: Strukt.Test.Fixtures.CustomEctoTypeTypeSepc,
fields: [:uri],
info: %{
uri: %{
type: :field,
value_type: Custom.EctoType,
required: true
}
},
embeds: []
})
) ==
Fixtures.CustomEctoTypeTypeSepc.expected_type_spec_ast_str()
end

defp changeset_errors(%Ecto.Changeset{} = cs) do
cs
|> Ecto.Changeset.traverse_errors(fn {msg, opts} ->
Expand Down
48 changes: 47 additions & 1 deletion test/support/defstruct_fixtures.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,31 @@ defmodule Aux do
end
end

defmodule Custom.EctoType do
@moduledoc "Custom Ecto Type for test"
@type t() :: URI.t()

use Ecto.Type
def type, do: :map

def cast(uri) when is_binary(uri), do: {:ok, URI.parse(uri)}
def cast(%URI{} = uri), do: {:ok, uri}

def cast(_), do: :error

def load(data) when is_map(data) do
data =
for {key, val} <- data do
{String.to_existing_atom(key), val}
end

{:ok, struct!(URI, data)}
end

def dump(%URI{} = uri), do: {:ok, Map.from_struct(uri)}
def dump(_), do: :error
end

defmodule Strukt.Test.Fixtures do
use Strukt

Expand Down Expand Up @@ -72,6 +97,25 @@ defmodule Strukt.Test.Fixtures do
end
end

defmodule CustomEctoTypeTypeSepc do
use Strukt

@primary_key false
defstruct do
field(:uri, Custom.EctoType)
field(:foobar, :string)
end

defmacro expected_type_spec_ast_str do
quote context: __MODULE__ do
@type t :: %__MODULE__{
uri: Custom.EctoType.t()
}
end
|> inspect()
end
end

defmodule CustomFields do
@moduledoc "This module represents the params keys are not snake case"

Expand Down Expand Up @@ -327,7 +371,9 @@ defmodule Strukt.Test.Fixtures do
defstruct ValidateSets do
@moduledoc "This module exercises validations based on set membership"

field(:one_of, :string, one_of: [values: ["a", "b", "c"], message: "must be one of [a, b, c]"])
field(:one_of, :string,
one_of: [values: ["a", "b", "c"], message: "must be one of [a, b, c]"]
)

field(:none_of, :string,
none_of: [values: ["a", "b", "c"], message: "cannot be one of [a, b, c]"]
Expand Down

0 comments on commit 9034fe4

Please sign in to comment.