I’m trying to write my own typed struct macro. It feels like it’s almost working, but I have a little snag with %__MODULE__{}
that I can’t seem to get over.
Here is the macro:
defmodule TypedStruct do
defmacro deftypedstruct(fields, enforced_fields \\ nil) do
fields_map =
case fields do
{:%{}, _, flist} -> Enum.into(flist, %{})
_ -> raise ArgumentError, "Fields must be a map!"
end
enforced_list = if is_nil(enforced_fields), do: Map.keys(fields_map), else: enforced_fields
field_specs = Map.to_list(fields_map)
field_names = Map.keys(fields_map)
IO.inspect(field_specs)
IO.inspect(enforced_list)
IO.inspect(field_names)
quote do
@type t :: %__MODULE__{unquote(field_specs)}
@enforce_keys unquote(enforced_list)
defstruct unquote(field_names)
end
end
end
And here is how I’m using it:
defmodule Foo do
require TypedStruct
TypedStruct.deftypedstruct(%{foo: String.t(), bar: nil | integer()}, [:foo])
end
The printout when running that is:
➜ ~ elixir -r typedstruct.ex test.ex
[
bar: {:|, [line: 3], [nil, {:integer, [line: 3], []}]},
foo: {{:., [line: 3], [{:__aliases__, [line: 3], [:String]}, :t]}, [line: 3],
[]}
]
[:foo]
[:bar, :foo]
** (CompileError) test.ex:3: expected key-value pairs in struct __MODULE__
(elixir 1.10.2) lib/kernel/typespec.ex:898: Kernel.Typespec.compile_error/2
(elixir 1.10.2) lib/kernel/typespec.ex:294: Kernel.Typespec.translate_type/2
(stdlib 3.11.2) lists.erl:1354: :lists.mapfoldl/3
(elixir 1.10.2) lib/kernel/typespec.ex:228: Kernel.Typespec.translate_typespecs_for_module/2
(elixir 1.10.2) src/elixir_erl_compiler.erl:12: anonymous fn/3 in :elixir_erl_compiler.spawn/2
You can see my field_specs
is a keyword list of field name and field specs (in quoted form). But unquoting it to the %__MODULE__{}
is not working for some reason. I can’t seem to find a correct combo of unquote/escape that works. Any help would be good, even better if you can make me understand the issue!