22
33use std:: default:: Default ;
44
5- use syn:: { spanned:: Spanned , Meta , NestedMeta , Result } ;
5+ use syn:: { spanned:: Spanned , Meta , NestedMeta , Path , Result } ;
66
7- use crate :: { DeriveWhere , Error , Trait , TraitImpl } ;
7+ use crate :: { DeriveWhere , Error , Trait } ;
88
99/// Stores what [`Trait`]s to skip this field or variant for.
1010#[ cfg_attr( test, derive( Debug ) ) ]
@@ -14,7 +14,7 @@ pub enum Skip {
1414 /// Field skipped for all [`Trait`]s that support it.
1515 All ,
1616 /// Field skipped for the [`Trait`]s listed.
17- Traits ( Vec < Trait > ) ,
17+ Traits ( Vec < SkipGroup > ) ,
1818}
1919
2020impl Default for Skip {
@@ -102,37 +102,34 @@ impl Skip {
102102
103103 for nested_meta in & list. nested {
104104 if let NestedMeta :: Meta ( Meta :: Path ( path) ) = nested_meta {
105- let trait_ = Trait :: from_path ( path) ?;
106-
107- // Don't allow unsupported traits to be skipped.
108- if trait_. supports_skip ( ) {
109- // Don't allow to skip the same trait twice.
110- if traits. contains ( & trait_) {
111- return Err ( Error :: option_skip_duplicate (
112- path. span ( ) ,
113- trait_. as_str ( ) ,
114- ) ) ;
115- } else {
116- // Don't allow to skip a trait already set to be skipped in the
117- // parent.
118- match skip_inner {
119- Some ( skip_inner) if skip_inner. skip ( & trait_) => {
120- return Err ( Error :: option_skip_inner ( path. span ( ) ) )
121- }
122- _ => {
123- // Don't allow to skip trait that isn't being implemented.
124- if derive_wheres. iter ( ) . any ( |derive_where| {
125- derive_where. trait_ ( & trait_) . is_some ( )
126- } ) {
127- traits. push ( trait_)
128- } else {
129- return Err ( Error :: option_skip_trait ( path. span ( ) ) ) ;
130- }
105+ let skip_group = SkipGroup :: from_path ( path) ?;
106+
107+ // Don't allow to skip the same trait twice.
108+ if traits. contains ( & skip_group) {
109+ return Err ( Error :: option_skip_duplicate (
110+ path. span ( ) ,
111+ skip_group. as_str ( ) ,
112+ ) ) ;
113+ } else {
114+ // Don't allow to skip a trait already set to be skipped in the
115+ // parent.
116+ match skip_inner {
117+ Some ( skip_inner) if skip_inner. group_skipped ( skip_group) => {
118+ return Err ( Error :: option_skip_inner ( path. span ( ) ) )
119+ }
120+ _ => {
121+ // Don't allow to skip trait that isn't being implemented.
122+ if derive_wheres. iter ( ) . any ( |derive_where| {
123+ skip_group
124+ . traits ( )
125+ . any ( |trait_| derive_where. contains ( trait_) )
126+ } ) {
127+ traits. push ( skip_group)
128+ } else {
129+ return Err ( Error :: option_skip_trait ( path. span ( ) ) ) ;
131130 }
132131 }
133132 }
134- } else {
135- return Err ( Error :: option_skip_support ( path. span ( ) , trait_. as_str ( ) ) ) ;
136133 }
137134 } else {
138135 return Err ( Error :: option_syntax ( nested_meta. span ( ) ) ) ;
@@ -147,15 +144,118 @@ impl Skip {
147144
148145 /// Returns `true` if this item, variant or field is skipped with the given
149146 /// [`Trait`].
150- pub fn skip ( & self , trait_ : & Trait ) -> bool {
147+ pub fn trait_skipped ( & self , trait_ : Trait ) -> bool {
148+ match self {
149+ Skip :: None => false ,
150+ Skip :: All => SkipGroup :: trait_supported ( trait_) ,
151+ Skip :: Traits ( skip_groups) => skip_groups
152+ . iter ( )
153+ . any ( |skip_group| skip_group. traits ( ) . any ( |this_trait| this_trait == trait_) ) ,
154+ }
155+ }
156+
157+ /// Returns `true` if this item, variant or field is skipped with the given
158+ /// [`SkipGroup`].
159+ pub fn group_skipped ( & self , group : SkipGroup ) -> bool {
151160 match self {
152161 Skip :: None => false ,
153- Skip :: All => trait_. supports_skip ( ) ,
154- Skip :: Traits ( traits) => {
155- let skip = traits. contains ( trait_) ;
156- debug_assert ! ( !skip || trait_. supports_skip( ) ) ;
157- skip
162+ Skip :: All => true ,
163+ Skip :: Traits ( groups) => groups. iter ( ) . any ( |this_group| * this_group == group) ,
164+ }
165+ }
166+ }
167+
168+ /// Available groups of [`Trait`]s to skip.
169+ #[ derive( Clone , Copy , Eq , PartialEq ) ]
170+ #[ cfg_attr( test, derive( Debug ) ) ]
171+ pub enum SkipGroup {
172+ /// [`Debug`].
173+ Debug ,
174+ /// [`Eq`], [`Hash`], [`Ord`], [`PartialEq`] and [`PartialOrd`].
175+ EqHashOrd ,
176+ /// [`Hash`].
177+ Hash ,
178+ /// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) and
179+ /// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html).
180+ #[ cfg( feature = "zeroize" ) ]
181+ Zeroize ,
182+ }
183+
184+ impl SkipGroup {
185+ /// Create [`SkipGroup`] from [`Path`].
186+ fn from_path ( path : & Path ) -> Result < Self > {
187+ if let Some ( ident) = path. get_ident ( ) {
188+ use SkipGroup :: * ;
189+
190+ match ident. to_string ( ) . as_str ( ) {
191+ "Debug" => Ok ( Debug ) ,
192+ "EqHashOrd" => Ok ( EqHashOrd ) ,
193+ "Hash" => Ok ( Hash ) ,
194+ #[ cfg( feature = "zeroize" ) ]
195+ "Zeroize" => Ok ( Zeroize ) ,
196+ _ => Err ( Error :: skip_group ( path. span ( ) ) ) ,
158197 }
198+ } else {
199+ Err ( Error :: skip_group ( path. span ( ) ) )
200+ }
201+ }
202+
203+ /// [`str`] representation of this [`Trait`].
204+ /// Used to compare against [`Ident`](struct@syn::Ident)s and create error
205+ /// messages.
206+ const fn as_str ( self ) -> & ' static str {
207+ match self {
208+ Self :: Debug => "Debug" ,
209+ Self :: EqHashOrd => "EqHashOrd" ,
210+ Self :: Hash => "Hash" ,
211+ #[ cfg( feature = "zeroize" ) ]
212+ Self :: Zeroize => "Zeroize" ,
213+ }
214+ }
215+
216+ /// [`Trait`]s supported by this group.
217+ fn traits ( self ) -> impl Iterator < Item = Trait > {
218+ match self {
219+ Self :: Debug => [ Some ( Trait :: Debug ) , None , None , None , None ]
220+ . into_iter ( )
221+ . flatten ( ) ,
222+ Self :: EqHashOrd => [
223+ Some ( Trait :: Eq ) ,
224+ Some ( Trait :: Hash ) ,
225+ Some ( Trait :: Ord ) ,
226+ Some ( Trait :: PartialEq ) ,
227+ Some ( Trait :: PartialOrd ) ,
228+ ]
229+ . into_iter ( )
230+ . flatten ( ) ,
231+ Self :: Hash => [ Some ( Trait :: Hash ) , None , None , None , None ]
232+ . into_iter ( )
233+ . flatten ( ) ,
234+ #[ cfg( feature = "zeroize" ) ]
235+ Self :: Zeroize => [
236+ Some ( Trait :: Zeroize ) ,
237+ Some ( Trait :: ZeroizeOnDrop ) ,
238+ None ,
239+ None ,
240+ None ,
241+ ]
242+ . into_iter ( )
243+ . flatten ( ) ,
244+ }
245+ }
246+
247+ /// Returns `true` if [`Trait`] is supported by any group.
248+ pub fn trait_supported ( trait_ : Trait ) -> bool {
249+ match trait_ {
250+ Trait :: Clone | Trait :: Copy | Trait :: Default => false ,
251+ Trait :: Debug
252+ | Trait :: Eq
253+ | Trait :: Hash
254+ | Trait :: Ord
255+ | Trait :: PartialEq
256+ | Trait :: PartialOrd => true ,
257+ #[ cfg( feature = "zeroize" ) ]
258+ Trait :: Zeroize | Trait :: ZeroizeOnDrop => true ,
159259 }
160260 }
161261}
0 commit comments