(option to) Use Kernel.struct!/2 to be stricter about making structs
Kernel.struct!/2 is stricter (see docs), which will prevent the ignorance of @enforce_keys and prevent invalid struct keys being passed.
If you want some code to do the two checks independently, you could add something like this:
use ExConstructor, check_enforce_keys: true, check_no_invalid_args: true
...
# pass @enforce_keys as 2nd param
def check_enforce_keys(args, enforce_keys) do
arg_keys =
args
|> Map.new
|> Map.keys
|> MapSet.new
required_keys = enforce_keys |> MapSet.new
if not MapSet.subset?(required_keys, arg_keys) do
raise(
ArgumentError,
"Requires keys #{required_keys |> Enum.to_list |> inspect} "
<> "but only #{arg_keys |> Enum.to_list |> inspect} "
<> "were given"
)
end
end
def check_no_invalid_args(args, module) do
arg_keys =
args
|> Map.new
|> Map.keys
|> MapSet.new
valid_keys =
module
|> struct([])
|> Map.from_struct
|> Map.keys
|> MapSet.new
if not MapSet.subset?(arg_keys, valid_keys) do
raise(
KeyError,
"Allowed keys are #{valid_keys |> Enum.to_list |> inspect} "
<> "but #{arg_keys |> Enum.to_list |> inspect} "
<> "were given"
)
end
end
I would probably prefer the code above over struct!/2, because some external data is bound to have various other bits of data not to be used by the struct - data that hopefully can be harmlessly ignored.
struct!/2 would fail with KeyError if passed a map/kwlist of keys not belonging to the struct.
I still would like @enforce_keys to actually do something though. If devs dont want the check to enforce keys they can just not put @enforce_keys in.