@@ -81,31 +81,17 @@ defmodule BACnet.BeamTypes do
8181
8282 def check_type ( :any , _value ) , do: true
8383
84- def check_type ( :boolean , value ) when is_boolean ( value ) , do: true
85- def check_type ( :boolean , _value ) , do: false
84+ def check_type ( :boolean , value ) , do: is_boolean ( value )
8685
87- def check_type ( :string , value ) when is_binary ( value ) , do: true
88- def check_type ( :string , _value ) , do: false
86+ def check_type ( :string , value ) , do: is_binary ( value ) and String . valid? ( value )
87+ def check_type ( :octet_string , value ) , do: is_binary ( value )
8988
90- def check_type ( :octet_string , value ) when is_binary ( value ) , do: true
91- def check_type ( :octet_string , _value ) , do: false
89+ def check_type ( :signed_integer , value ) , do: is_integer ( value )
90+ def check_type ( :unsigned_integer , value ) , do: is_integer ( value ) and value >= 0
9291
93- def check_type ( :signed_integer , value ) when is_integer ( value ) , do: true
94- def check_type ( :signed_integer , _value ) , do: false
92+ def check_type ( :real , value ) , do: is_float ( value ) or value in [ :NaN , :inf , :infn ]
9593
96- def check_type ( :unsigned_integer , value )
97- when is_integer ( value ) and value >= 0 ,
98- do: true
99-
100- def check_type ( :unsigned_integer , _value ) , do: false
101-
102- def check_type ( :real , value ) when is_float ( value ) , do: true
103- def check_type ( :real , value ) when value in [ :NaN , :inf , :infn ] , do: true
104- def check_type ( :real , _value ) , do: false
105-
106- def check_type ( :double , value ) when is_float ( value ) , do: true
107- def check_type ( :double , value ) when value in [ :NaN , :inf , :infn ] , do: true
108- def check_type ( :double , _value ) , do: false
94+ def check_type ( :double , value ) , do: is_float ( value ) or value in [ :NaN , :inf , :infn ]
10995
11096 def check_type ( :bitstring , value ) when is_tuple ( value ) do
11197 value
@@ -142,32 +128,28 @@ defmodule BACnet.BeamTypes do
142128 when is_integer ( fixed_size ) and fixed_size >= 1 ,
143129 do: false
144130
145- def check_type ( { :constant , type } , value ) when is_atom ( value ) ,
146- do: Constants . has_by_name ( type , value )
131+ def check_type ( { :constant , type } , value ) when is_atom ( type ) ,
132+ do: is_atom ( value ) and Constants . has_by_name ( type , value )
147133
148134 def check_type ( { :in_list , values } , value ) when is_list ( values ) do
149135 value in values
150136 end
151137
152- def check_type ( { :in_range , low , high } , value ) do
138+ def check_type ( { :in_range , low , high } , value ) when is_integer ( low ) and is_integer ( high ) do
153139 is_integer ( value ) and value >= low and value <= high
154140 end
155141
156142 def check_type ( { :list , _type } , [ ] ) , do: true
157143
158144 def check_type ( { :list , type } , value ) when is_list ( value ) do
159- if Enumerable . impl_for ( value ) do
160- Enum . all? ( value , & check_type ( type , & 1 ) )
161- else
162- false
163- end
145+ Enum . all? ( value , & check_type ( type , & 1 ) )
164146 end
165147
166148 def check_type ( { :list , _type } , _value ) , do: false
167149
168150 def check_type ( { :literal , eq } , value ) , do: value === eq
169151
170- def check_type ( { :tuple , subtypes } , value ) when is_tuple ( value ) do
152+ def check_type ( { :tuple , subtypes } , value ) when is_list ( subtypes ) and is_tuple ( value ) do
171153 if length ( subtypes ) == tuple_size ( value ) do
172154 subtypes
173155 |> Enum . with_index ( )
@@ -179,7 +161,7 @@ defmodule BACnet.BeamTypes do
179161 end
180162 end
181163
182- def check_type ( { :tuple , _subtypes } , _value ) , do: false
164+ def check_type ( { :tuple , subtypes } , _value ) when is_list ( subtypes ) , do: false
183165
184166 def check_type ( { :struct , type } , value ) when is_struct ( value , type ) do
185167 if function_exported? ( type , :valid? , 1 ) do
@@ -196,27 +178,37 @@ defmodule BACnet.BeamTypes do
196178 end
197179
198180 def check_type ( { :with_validator , type , validator } , value ) when is_function ( validator , 1 ) do
199- if check_type ( type , value ) do
200- validator . ( value )
201- else
202- false
203- end
181+ check_type_validator ( type , validator , value )
204182 end
205183
206184 def check_type ( { :with_validator , type , validator_ast } , value ) do
207185 { validator , _bind } = Code . eval_quoted ( validator_ast , [ ] , __ENV__ )
208- check_type ( { :with_validator , type , validator } , value )
186+ check_type_validator ( type , validator , value )
187+ end
188+
189+ def check_type ( type , _value ) do
190+ raise ArgumentError , "Unknown type: #{ inspect ( type ) } "
209191 end
210192
211- def check_type ( type , _value ) , do: raise ( "Unknown type: #{ inspect ( type ) } " )
193+ # This clause is needed to prevent an infinite loop
194+ defp check_type_validator ( type , validator , value ) do
195+ if is_function ( validator , 1 ) do
196+ check_type ( type , value ) and validator . ( value )
197+ else
198+ raise ArgumentError , "Only one arity functions are allowed for :with_validator"
199+ end
200+ end
212201
213202 @ doc """
214203 Generates `valid?/1` clause body based on the given module's `:t` typespec,
215204 it must reference a struct.
205+
206+ The variable used for the validation is called `t`.
207+ Keys that start with an underline are ignored.
216208 """
217209 @ spec generate_valid_clause ( module ( ) , Macro.Env . t ( ) ) :: Macro . t ( )
218210 def generate_valid_clause ( module , env ) when is_atom ( module ) do
219- validation = resolve_struct_type ( module , :t , env )
211+ validation = resolve_struct_type ( module , :t , env , ignore_underlined_keys: true )
220212
221213 if map_size ( validation ) == 0 do
222214 quote do
@@ -261,7 +253,7 @@ defmodule BACnet.BeamTypes do
261253 Tuple types such as `{binary(), integer()}` will be resolved to `{:tuple, [:octet_string, :integer]}`.
262254
263255 ```elixir
264- iex(1) > resolve_struct_type(BACnet.Protocol.EventParameters.ChangeOfBitstring, :t, __ENV__)
256+ iex> resolve_struct_type(BACnet.Protocol.EventParameters.ChangeOfBitstring, :t, __ENV__)
265257 %{
266258 alarm_values: {:list, :bitstring},
267259 bitmask: :bitstring,
@@ -292,10 +284,11 @@ defmodule BACnet.BeamTypes do
292284 { :type , _line , :map , fields } ->
293285 fields
294286 |> Map . new ( fn
295- { :type , _line , :map_field_exact , [ { :atom , 0 , :__struct__ } , { :atom , 0 , ^ module } ] } ->
287+ { :type , _line , :map_field_exact ,
288+ [ { :atom , _num , :__struct__ } , { :atom , _other , ^ module } ] } ->
296289 { :__drop__ , nil }
297290
298- { :type , _line , :map_field_exact , [ { :atom , 0 , key } , spec ] } ->
291+ { :type , _line , :map_field_exact , [ { :atom , _num , key } , spec ] } ->
299292 if ignore_underlined_keys and String . starts_with? ( Atom . to_string ( key ) , "_" ) do
300293 { :__drop__ , nil }
301294 else
@@ -308,8 +301,10 @@ defmodule BACnet.BeamTypes do
308301 |> Map . delete ( :__drop__ )
309302
310303 _term ->
304+ str_mod = String . replace ( "#{ module } " , "Elixir." , "" )
305+
311306 raise CompileError ,
312- description: "Type #{ module } .#{ type } does not export the type as struct" ,
307+ description: "Type #{ str_mod } .#{ type } does not export the type as struct" ,
313308 file: env . file ,
314309 line: env . line
315310 end
@@ -331,6 +326,11 @@ defmodule BACnet.BeamTypes do
331326 nil
332327 end
333328
329+ # Map true and false to boolean()
330+ defp field_typespec_to_bactype ( type , _env , _opts ) when type in [ true , false ] do
331+ :boolean
332+ end
333+
334334 defp field_typespec_to_bactype ( type , _env , _opts ) when is_atom ( type ) do
335335 type
336336 end
@@ -399,7 +399,8 @@ defmodule BACnet.BeamTypes do
399399 end
400400
401401 # Range expression x..y//z
402- defp field_typespec_to_bactype ( { :.. , _list , [ value1 , value2 , step ] } , _env , _opts ) do
402+ defp field_typespec_to_bactype ( { range , _list , [ value1 , value2 , step ] } , _env , _opts )
403+ when range in [ :.. , :..// ] do
403404 { :in_list , Enum . to_list ( value1 .. value2 // step ) }
404405 end
405406
@@ -490,7 +491,7 @@ defmodule BACnet.BeamTypes do
490491 # BACnet.Array.t(...) - Error as more than a single parameter (subtype)
491492 _term ->
492493 raise CompileError ,
493- description: "BACnetArray must have a single parameter " ,
494+ description: "BACnetArray must have one or two parameters " ,
494495 file: env . file ,
495496 line: env . line
496497 end
@@ -583,7 +584,7 @@ defmodule BACnet.BeamTypes do
583584
584585 # Remote type (such as String.t)
585586 defp map_beam_typespec_data (
586- { :remote_type , _line , [ { :atom , 0 , module } , { :atom , 0 , type } , args ] } ,
587+ { :remote_type , _line , [ { :atom , _num , module } , { :atom , _other , type } , args ] } ,
587588 env ,
588589 _spec_module ,
589590 opts
@@ -605,10 +606,10 @@ defmodule BACnet.BeamTypes do
605606 { :ann_type , _line , [ { :var , _line2 , _name } , { :type , _line3 , _type , _args } = spec ] } ->
606607 map_beam_typespec_data ( spec , env , spec_module , opts )
607608
608- { :atom , 0 , atom_name } ->
609+ { :atom , _num , atom_name } ->
609610 { :literal , atom_name }
610611
611- { :remote_type , _line , [ { :atom , 0 , module } , { :atom , 0 , type } , args ] } ->
612+ { :remote_type , _line , [ { :atom , _num , module } , { :atom , _other , type } , args ] } ->
612613 field_typespec_to_bactype (
613614 { { :. , [ ] , [ module , type ] } , [ ] , args } ,
614615 env ,
@@ -637,12 +638,16 @@ defmodule BACnet.BeamTypes do
637638 end
638639
639640 # Map typespec (structs count as this)
640- defp map_beam_typespec_data ( { :type , _line , :map , definition } , env , _spec_module , _opts ) do
641+ defp map_beam_typespec_data ( { :type , _line , :map , definition } , env , _spec_module , _opts )
642+ when not is_atom ( definition ) do
641643 # Check if it's a struct, if so, return the module name
642644 module =
643645 Enum . find_value ( definition , fn
644- { :type , _line , :map_field_exact , [ { :atom , 0 , :__struct__ } , { :atom , 0 , module } ] } -> module
645- _term -> false
646+ { :type , _line , :map_field_exact , [ { :atom , _num , :__struct__ } , { :atom , _other , module } ] } ->
647+ module
648+
649+ _term ->
650+ false
646651 end )
647652
648653 if module do
@@ -689,6 +694,12 @@ defmodule BACnet.BeamTypes do
689694 { subtype , subtypes }
690695 end
691696
697+ # Literal numbers or atoms
698+ defp map_beam_typespec_data ( { type , _num , val } , _env , _spec_module , _opts )
699+ when type in [ :atom , :integer ] do
700+ { :literal , val }
701+ end
702+
692703 # Custom local types
693704 defp map_beam_typespec_data (
694705 { :user_type , _line , type , args } ,
0 commit comments