shahryarjb
Need help to create nested struct macro (A.B.C)
Hello, I am developing a macro to create nested struct in a module or (module → module …), I took the original code from this repo, because it had been abandoned and I want many options for validation etc, I move it inside my project, you can see it in this link.
The problem:
To create a nested struct, I create a new module in one module. For example (A.B.C) is made. Since struct B cannot be called in A at compile time, I just want to create a field named module in its parent struct.
But it seems that the parent has already been compiled and does not allow me to do this.
Please see this part of code:
https://github.com/mishka-group/mishka_developer_tools/blob/master/lib/macros/guarded_struct.ex#L303-L318
in this part I create another module, but how can define a filed which struct type with options
defmodule A do
defstruct name: "", :b
defmodule B do
defstruct name: ""
end
end
I tried to use @before_compile {__CALLER__.module, :add_parent_field_of_sub_field} to add module attribute like using this
== Compilation error in file lib/macros/guarded_struct.ex ==
** (ArgumentError) cannot set attribute @before_compile inside function/macro
Even I use this macro inside sub_field macro, to define a field, but it has no effect on my code
Example code I want finally:
defmodule TestNestedStruct do
use GuardedStruct
guardedstruct do
field(:title, String.t())
field(:subject, String.t())
sub_field(:oop, struct(), enforce: true) do
field(:title, String.t())
field(:fam, String.t())
sub_field(:soos, struct(), enforce: true) do
field(:fam, String.t())
end
field(:site, String.t())
end
field(:site, String.t())
end
end
If I create a field has same name with module name, I can check it inside my builder function.
I have no idea how can do it, thank you in advance
First Post!
shahryarjb
I think it works for me, but I need more test, if my idea is good for this problem?
defmacro sub_field(name, type, opts \\ [], do: block) do
ast = register_struct(block, opts)
type = Macro.escape(type)
converted_name =
name
|> Atom.to_string()
|> Macro.camelize()
|> String.to_atom()
|> then(&Module.concat(__CALLER__.module, &1))
quote do
GuardedStruct.__field__(unquote(name), unquote(type), unquote(opts), __ENV__)
defmodule unquote(converted_name) do
unquote(ast)
end
end
end
Output
%MishkaDeveloperToolsTest.GuardedStructTest.TestNestedStruct{
site: nil,
oop: nil,
subject: nil,
title: nil
}
[:site, :oop, :subject, :title]
[
__struct__: 0,
__struct__: 1,
builder: 1,
enforce_keys: 0,
enforce_keys: 1,
keys: 0,
keys: 1
]
--------------------------------
%MishkaDeveloperToolsTest.GuardedStructTest.TestNestedStruct.Oop{
site: nil,
soos: nil,
fam: nil,
title: nil
}
[:site, :soos, :fam, :title]
[
__struct__: 0,
__struct__: 1,
builder: 1,
enforce_keys: 0,
enforce_keys: 1,
keys: 0,
keys: 1
]
--------------------------------
%MishkaDeveloperToolsTest.GuardedStructTest.TestNestedStruct.Oop.Soos{fam: nil}
[:fam]
[
__struct__: 0,
__struct__: 1,
builder: 1,
enforce_keys: 0,
enforce_keys: 1,
keys: 0,
keys: 1
]
If you see I just create a field in parent struct and I will check it inside builder function, there is no way I think to load and call the B struct inside A struct field, I think, am I right?







