@@ -139,6 +139,85 @@ defprotocol Inspect do
139139 # Handle structs in Any
140140 @ fallback_to_any true
141141
142+ @ impl true
143+ defmacro __deriving__ ( module , options ) do
144+ info = Macro . struct_info! ( module , __CALLER__ )
145+ fields = Enum . sort ( Enum . map ( info , & & 1 . field ) -- [ :__exception__ , :__struct__ ] )
146+
147+ only = Keyword . get ( options , :only , fields )
148+ except = Keyword . get ( options , :except , [ ] )
149+ optional = Keyword . get ( options , :optional , [ ] )
150+
151+ :ok = validate_option ( :only , only , fields , module )
152+ :ok = validate_option ( :except , except , fields , module )
153+ :ok = validate_option ( :optional , optional , fields , module )
154+
155+ inspect_module =
156+ if fields == Enum . sort ( only ) and except == [ ] do
157+ Inspect.Map
158+ else
159+ Inspect.Any
160+ end
161+
162+ filtered_fields =
163+ fields
164+ |> Enum . reject ( & ( & 1 in except ) )
165+ |> Enum . filter ( & ( & 1 in only ) )
166+
167+ filtered_guard =
168+ quote do
169+ var! ( field ) in unquote ( filtered_fields )
170+ end
171+
172+ field_guard =
173+ if optional == [ ] do
174+ filtered_guard
175+ else
176+ optional_map =
177+ for field <- optional , into: % { } do
178+ default = Enum . find ( info , % { } , & ( & 1 . field == field ) ) |> Map . get ( :default , nil )
179+ { field , default }
180+ end
181+
182+ quote do
183+ unquote ( filtered_guard ) and
184+ not case unquote ( Macro . escape ( optional_map ) ) do
185+ % { ^ var! ( field ) => var! ( default ) } ->
186+ var! ( default ) == Map . get ( var! ( struct ) , var! ( field ) )
187+
188+ % { } ->
189+ false
190+ end
191+ end
192+ end
193+
194+ quote do
195+ defimpl Inspect , for: unquote ( module ) do
196+ def inspect ( var! ( struct ) , var! ( opts ) ) do
197+ var! ( infos ) =
198+ for % { field: var! ( field ) } = var! ( info ) <- unquote ( module ) . __info__ ( :struct ) ,
199+ unquote ( field_guard ) ,
200+ do: var! ( info )
201+
202+ var! ( name ) = Macro . inspect_atom ( :literal , unquote ( module ) )
203+ unquote ( inspect_module ) . inspect ( var! ( struct ) , var! ( name ) , var! ( infos ) , var! ( opts ) )
204+ end
205+ end
206+ end
207+ end
208+
209+ defp validate_option ( option , option_list , fields , module ) do
210+ case option_list -- fields do
211+ [ ] ->
212+ :ok
213+
214+ unknown_fields ->
215+ raise ArgumentError ,
216+ "unknown fields #{ Kernel . inspect ( unknown_fields ) } in #{ Kernel . inspect ( option ) } " <>
217+ "when deriving the Inspect protocol for #{ Kernel . inspect ( module ) } "
218+ end
219+ end
220+
142221 @ doc """
143222 Converts `term` into an algebra document.
144223
@@ -548,79 +627,6 @@ defimpl Inspect, for: Reference do
548627end
549628
550629defimpl Inspect , for: Any do
551- defmacro __deriving__ ( module , struct , options ) do
552- fields = Enum . sort ( Map . keys ( struct ) -- [ :__exception__ , :__struct__ ] )
553-
554- only = Keyword . get ( options , :only , fields )
555- except = Keyword . get ( options , :except , [ ] )
556- optional = Keyword . get ( options , :optional , [ ] )
557-
558- :ok = validate_option ( :only , only , fields , module )
559- :ok = validate_option ( :except , except , fields , module )
560- :ok = validate_option ( :optional , optional , fields , module )
561-
562- inspect_module =
563- if fields == Enum . sort ( only ) and except == [ ] do
564- Inspect.Map
565- else
566- Inspect.Any
567- end
568-
569- filtered_fields =
570- fields
571- |> Enum . reject ( & ( & 1 in except ) )
572- |> Enum . filter ( & ( & 1 in only ) )
573-
574- filtered_guard =
575- quote do
576- var! ( field ) in unquote ( filtered_fields )
577- end
578-
579- field_guard =
580- if optional == [ ] do
581- filtered_guard
582- else
583- optional_map = for field <- optional , into: % { } , do: { field , Map . fetch! ( struct , field ) }
584-
585- quote do
586- unquote ( filtered_guard ) and
587- not case unquote ( Macro . escape ( optional_map ) ) do
588- % { ^ var! ( field ) => var! ( default ) } ->
589- var! ( default ) == Map . get ( var! ( struct ) , var! ( field ) )
590-
591- % { } ->
592- false
593- end
594- end
595- end
596-
597- quote do
598- defimpl Inspect , for: unquote ( module ) do
599- def inspect ( var! ( struct ) , var! ( opts ) ) do
600- var! ( infos ) =
601- for % { field: var! ( field ) } = var! ( info ) <- unquote ( module ) . __info__ ( :struct ) ,
602- unquote ( field_guard ) ,
603- do: var! ( info )
604-
605- var! ( name ) = Macro . inspect_atom ( :literal , unquote ( module ) )
606- unquote ( inspect_module ) . inspect ( var! ( struct ) , var! ( name ) , var! ( infos ) , var! ( opts ) )
607- end
608- end
609- end
610- end
611-
612- defp validate_option ( option , option_list , fields , module ) do
613- case option_list -- fields do
614- [ ] ->
615- :ok
616-
617- unknown_fields ->
618- raise ArgumentError ,
619- "unknown fields #{ Kernel . inspect ( unknown_fields ) } in #{ Kernel . inspect ( option ) } " <>
620- "when deriving the Inspect protocol for #{ Kernel . inspect ( module ) } "
621- end
622- end
623-
624630 def inspect ( % module { } = struct , opts ) do
625631 try do
626632 { module . __struct__ ( ) , module . __info__ ( :struct ) }
0 commit comments