@@ -10,14 +10,17 @@ use std::fmt;
1010use std:: hash:: Hash ;
1111use std:: iter;
1212
13- use syn:: { LitInt , LitStr } ;
13+ use syn:: { parse_quote , LitInt , LitStr } ;
1414use syn:: spanned:: Spanned ;
1515
1616pub use typeparam:: TypeParams ;
1717pub use codec:: CustomCodec ;
1818pub use encoding:: Encoding ;
1919pub use idx:: Idx ;
2020
21+ use crate :: attrs:: codec:: { Decode , Encode , PathOrClosure } ;
22+ use crate :: { is_decode_bound, is_encode_bound, is_length_bound} ;
23+
2124/// Recognised attributes.
2225#[ derive( Debug , Clone ) ]
2326pub struct Attributes {
@@ -41,6 +44,7 @@ pub enum Kind {
4144 CborLen ,
4245 Tag ,
4346 Skip ,
47+ SkipIf ,
4448 Flat ,
4549 Default
4650}
@@ -61,6 +65,7 @@ enum Value {
6165 CborLen ( syn:: ExprPath , proc_macro2:: Span ) ,
6266 Tag ( u64 , proc_macro2:: Span ) ,
6367 Skip ( proc_macro2:: Span ) ,
68+ SkipIf ( Option < syn:: ExprPath > , proc_macro2:: Span ) ,
6469 Flat ( proc_macro2:: Span ) ,
6570 Default ( proc_macro2:: Span )
6671}
@@ -120,14 +125,100 @@ impl Attributes {
120125 return Err ( syn:: Error :: new ( * s, "`tag` and `transparent` are mutually exclusive" ) )
121126 }
122127 }
123- if let Some ( Value :: Skip ( s) ) = this. get ( Kind :: Skip ) {
124- if this. attrs . len ( ) > 1 {
125- return Err ( syn:: Error :: new ( * s, "`skip` does not allow other attributes" ) )
126- }
128+ if this. contains_key ( Kind :: Skip )
129+ && let Some ( ( _, a) ) = this. attrs
130+ . iter ( )
131+ . find ( |( k, _) | !matches ! ( k, Kind :: Skip | Kind :: TypeParam ) )
132+ {
133+ return Err ( syn:: Error :: new ( a. span ( ) , "`skip` can not be used with this attribute" ) )
134+ }
135+ if this. contains_key ( Kind :: Flat )
136+ && let Some ( Value :: Encoding ( Encoding :: Map , s) ) = this. get ( Kind :: Encoding )
137+ {
138+ return Err ( syn:: Error :: new ( * s, "flat enum does not support map encoding" ) )
127139 }
128- if let Some ( Value :: Flat ( _) ) = this. get ( Kind :: Flat ) {
129- if let Some ( Value :: Encoding ( Encoding :: Map , s) ) = this. get ( Kind :: Encoding ) {
130- return Err ( syn:: Error :: new ( * s, "flat enum does not support map encoding" ) )
140+ // `skip_if` triggers the creation of a custom codec where `encode` and `decode`
141+ // correspond to the default routines, `is_nil` is defined via `skip_if`'s
142+ // predicate and `nil` points to an internal helper that matches the signature
143+ // of `nil` and uses `Default::default` to create a default value.
144+ //
145+ // If a custom codec is already defined, `is_nil` and `nil` are set to `skip_if`'s
146+ // predicate and the internal default helper. If `is_nil` or `nil` already exist
147+ // in a pre-existing custom codec, an error is returned.
148+ if let Some ( Value :: SkipIf ( is_nil@Some ( _) , skip_if_span) ) = this. get_mut ( Kind :: SkipIf ) {
149+ let is_nil = is_nil. take ( ) . expect ( "some is_nil" ) ;
150+ let encode = parse_quote ! ( minicbor:: Encode :: encode) ;
151+ let decode = parse_quote ! ( minicbor:: Decode :: decode) ;
152+ let default = PathOrClosure :: Closure ( parse_quote ! ( || Some ( Default :: default ( ) ) ) ) ;
153+ let skip_if_span = * skip_if_span;
154+ match this. remove ( Kind :: Codec ) {
155+ None => {
156+ let e = Encode { encode, is_nil : Some ( is_nil) , require_bound : true } ;
157+ let d = Decode { decode, nil : Some ( default) , require_bound : true } ;
158+ let c = CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) ) ;
159+ this. attrs . insert ( Kind :: Codec , Value :: Codec ( c, skip_if_span) ) ;
160+ }
161+ Some ( Value :: Codec ( CustomCodec :: Encode ( mut e) , span) ) => {
162+ if e. is_nil . is_some ( ) {
163+ let msg = "skip_if and `is_nil` are mutually exclusive" ;
164+ return Err ( syn:: Error :: new ( skip_if_span, msg) )
165+ }
166+ let c = {
167+ let d = Decode { decode, nil : Some ( default) , require_bound : true } ;
168+ e. is_nil = Some ( is_nil) ;
169+ CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) )
170+ } ;
171+ this. attrs . insert ( Kind :: Codec , Value :: Codec ( c, span) ) ;
172+ }
173+ Some ( Value :: Codec ( CustomCodec :: Decode ( mut d) , span) ) => {
174+ if d. nil . is_some ( ) {
175+ let msg = "skip_if and `nil` are mutually exclusive" ;
176+ return Err ( syn:: Error :: new ( skip_if_span, msg) )
177+ }
178+ let c = {
179+ let e = Encode { encode, is_nil : Some ( is_nil) , require_bound : true } ;
180+ d. nil = Some ( default) ;
181+ CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) )
182+ } ;
183+ this. attrs . insert ( Kind :: Codec , Value :: Codec ( c, span) ) ;
184+ }
185+ Some ( Value :: Codec ( CustomCodec :: Both ( mut e, mut d) , span) ) => {
186+ if e. is_nil . is_some ( ) {
187+ let msg = "skip_if and `is_nil` are mutually exclusive" ;
188+ return Err ( syn:: Error :: new ( skip_if_span, msg) )
189+ }
190+ if d. nil . is_some ( ) {
191+ let msg = "skip_if and `nil` are mutually exclusive" ;
192+ return Err ( syn:: Error :: new ( skip_if_span, msg) )
193+ }
194+ let c = {
195+ e. is_nil = Some ( is_nil) ;
196+ d. nil = Some ( default) ;
197+ CustomCodec :: Both ( e, d)
198+ } ;
199+ this. attrs . insert ( Kind :: Codec , Value :: Codec ( c, span) ) ;
200+ }
201+ Some ( Value :: Codec ( m@CustomCodec :: Module ( _, has_nil) , span) ) => {
202+ if has_nil {
203+ let msg = "skip_if and `has_nil` are mutually exclusive" ;
204+ return Err ( syn:: Error :: new ( skip_if_span, msg) )
205+ }
206+ let c = {
207+ let e = Encode {
208+ encode : m. to_encode_path ( ) . expect ( "module => encode path" ) ,
209+ is_nil : Some ( is_nil) ,
210+ require_bound : false ,
211+ } ;
212+ let d = Decode {
213+ decode : m. to_decode_path ( ) . expect ( "module => decode path" ) ,
214+ nil : Some ( default) ,
215+ require_bound : false
216+ } ;
217+ CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) )
218+ } ;
219+ this. attrs . insert ( Kind :: Codec , Value :: Codec ( c, span) ) ;
220+ }
221+ _ => { }
131222 }
132223 }
133224 Ok ( this)
@@ -168,18 +259,29 @@ impl Attributes {
168259 attrs. try_insert ( Kind :: HasNil , Value :: HasNil ( meta. path . span ( ) ) ) ?
169260 } else if meta. path . is_ident ( "encode_with" ) {
170261 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
171- let c = CustomCodec :: Encode ( codec:: Encode { encode : s. parse ( ) ?, is_nil : None } ) ;
262+ let c = CustomCodec :: Encode ( codec:: Encode {
263+ encode : s. parse ( ) ?,
264+ is_nil : None ,
265+ require_bound : false
266+ } ) ;
172267 attrs. try_insert ( Kind :: Codec , Value :: Codec ( c, meta. path . span ( ) ) ) ?
173268 } else if meta. path . is_ident ( "is_nil" ) {
174269 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
175270 attrs. try_insert ( Kind :: IsNil , Value :: IsNil ( s. parse ( ) ?, meta. path . span ( ) ) ) ?
176271 } else if meta. path . is_ident ( "decode_with" ) {
177272 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
178- let c = CustomCodec :: Decode ( codec:: Decode { decode : s. parse ( ) ?, nil : None } ) ;
273+ let c = CustomCodec :: Decode ( codec:: Decode {
274+ decode : s. parse ( ) ?,
275+ nil : None ,
276+ require_bound : false
277+ } ) ;
179278 attrs. try_insert ( Kind :: Codec , Value :: Codec ( c, meta. path . span ( ) ) ) ?
180279 } else if meta. path . is_ident ( "nil" ) {
181280 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
182281 attrs. try_insert ( Kind :: Nil , Value :: Nil ( s. parse ( ) ?, meta. path . span ( ) ) ) ?
282+ } else if meta. path . is_ident ( "skip_if" ) {
283+ let s: LitStr = meta. value ( ) ?. parse ( ) ?;
284+ attrs. try_insert ( Kind :: SkipIf , Value :: SkipIf ( Some ( s. parse ( ) ?) , meta. path . span ( ) ) ) ?;
183285 } else if meta. path . is_ident ( "with" ) {
184286 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
185287 let c = CustomCodec :: Module ( s. parse ( ) ?, false ) ;
@@ -211,8 +313,32 @@ impl Attributes {
211313 } else if meta. path . is_ident ( "bound" ) {
212314 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
213315 let t: syn:: TypeParam = s. parse ( ) ?;
214- let m = iter:: once ( ( t. ident . clone ( ) , t) ) . collect :: < HashMap < _ , _ > > ( ) ;
215- let b = TypeParams :: All { encode : m. clone ( ) , length : m. clone ( ) , decode : m } ;
316+ let b = TypeParams :: All {
317+ encode : {
318+ let mut e = t. clone ( ) ;
319+ e. bounds = e. bounds
320+ . into_iter ( )
321+ . filter ( |b| !( is_decode_bound ( b) || is_length_bound ( b) ) )
322+ . collect ( ) ;
323+ HashMap :: from_iter ( [ ( e. ident . clone ( ) , e) ] )
324+ } ,
325+ length : {
326+ let mut e = t. clone ( ) ;
327+ e. bounds = e. bounds
328+ . into_iter ( )
329+ . filter ( |b| !( is_encode_bound ( b) || is_decode_bound ( b) ) )
330+ . collect ( ) ;
331+ HashMap :: from_iter ( [ ( e. ident . clone ( ) , e) ] )
332+ } ,
333+ decode : {
334+ let mut d = t;
335+ d. bounds = d. bounds
336+ . into_iter ( )
337+ . filter ( |b| !( is_encode_bound ( b) || is_length_bound ( b) ) )
338+ . collect ( ) ;
339+ HashMap :: from_iter ( [ ( d. ident . clone ( ) , d) ] )
340+ }
341+ } ;
216342 attrs. try_insert ( Kind :: TypeParam , Value :: TypeParam ( b, meta. path . span ( ) ) ) ?
217343 } else if meta. path . is_ident ( "context_bound" ) {
218344 let s: LitStr = meta. value ( ) ?. parse ( ) ?;
@@ -305,6 +431,10 @@ impl Attributes {
305431 self . contains_key ( Kind :: Skip )
306432 }
307433
434+ pub fn skip_if_codec ( & self ) -> bool {
435+ self . contains_key ( Kind :: SkipIf )
436+ }
437+
308438 pub fn flat ( & self ) -> bool {
309439 self . contains_key ( Kind :: Flat )
310440 }
@@ -347,6 +477,7 @@ impl Attributes {
347477 | Kind :: HasNil
348478 | Kind :: CborLen
349479 | Kind :: Skip
480+ | Kind :: SkipIf
350481 | Kind :: Flat
351482 | Kind :: Default
352483 => {
@@ -365,6 +496,7 @@ impl Attributes {
365496 | Kind :: CborLen
366497 | Kind :: Tag
367498 | Kind :: Skip
499+ | Kind :: SkipIf
368500 | Kind :: Default
369501 => { }
370502 | Kind :: Encoding
@@ -394,6 +526,7 @@ impl Attributes {
394526 | Kind :: HasNil
395527 | Kind :: CborLen
396528 | Kind :: Skip
529+ | Kind :: SkipIf
397530 | Kind :: Default
398531 => {
399532 let msg = format ! ( "attribute is not supported on {}-level" , self . level) ;
@@ -416,6 +549,7 @@ impl Attributes {
416549 | Kind :: ContextBound
417550 | Kind :: CborLen
418551 | Kind :: Skip
552+ | Kind :: SkipIf
419553 | Kind :: Flat
420554 | Kind :: Default
421555 => {
@@ -429,12 +563,20 @@ impl Attributes {
429563 let s = val. span ( ) ;
430564 match ( val, & cc) {
431565 ( Value :: Codec ( CustomCodec :: Encode ( e) , _) , CustomCodec :: Decode ( d) ) => {
432- let d = codec:: Decode { decode : d. decode . clone ( ) , nil : d. nil . clone ( ) } ;
566+ let d = codec:: Decode {
567+ decode : d. decode . clone ( ) ,
568+ nil : d. nil . clone ( ) ,
569+ require_bound : d. require_bound
570+ } ;
433571 * cc = CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) ) ;
434572 return Ok ( ( ) )
435573 }
436574 ( Value :: Codec ( CustomCodec :: Decode ( d) , _) , CustomCodec :: Encode ( e) ) => {
437- let e = codec:: Encode { encode : e. encode . clone ( ) , is_nil : e. is_nil . clone ( ) } ;
575+ let e = codec:: Encode {
576+ encode : e. encode . clone ( ) ,
577+ is_nil : e. is_nil . clone ( ) ,
578+ require_bound : e. require_bound
579+ } ;
438580 * cc = CustomCodec :: Both ( Box :: new ( e) , Box :: new ( d) ) ;
439581 return Ok ( ( ) )
440582 }
@@ -484,14 +626,14 @@ impl Attributes {
484626 if d. nil . is_some ( ) {
485627 return Err ( syn:: Error :: new ( * s, "duplicate attribute" ) )
486628 }
487- d. nil = Some ( nil. clone ( ) ) ;
629+ d. nil = Some ( PathOrClosure :: Path ( nil. clone ( ) ) ) ;
488630 return Ok ( ( ) )
489631 }
490632 Some ( Value :: Codec ( CustomCodec :: Both ( _, d) , _) ) => {
491633 if d. nil . is_some ( ) {
492634 return Err ( syn:: Error :: new ( * s, "duplicate attribute" ) )
493635 }
494- d. nil = Some ( nil. clone ( ) ) ;
636+ d. nil = Some ( PathOrClosure :: Path ( nil. clone ( ) ) ) ;
495637 return Ok ( ( ) )
496638 }
497639 _ => { }
@@ -519,7 +661,7 @@ impl Attributes {
519661 if d. nil . is_some ( ) {
520662 return Err ( syn:: Error :: new ( * s, "duplicate attribute" ) )
521663 }
522- d. nil = Some ( nil)
664+ d. nil = Some ( PathOrClosure :: Path ( nil) )
523665 }
524666 }
525667 Value :: Codec ( CustomCodec :: Both ( e, d) , s) => {
@@ -533,7 +675,7 @@ impl Attributes {
533675 if d. nil . is_some ( ) {
534676 return Err ( syn:: Error :: new ( * s, "duplicate attribute" ) )
535677 }
536- d. nil = Some ( nil)
678+ d. nil = Some ( PathOrClosure :: Path ( nil) )
537679 }
538680 }
539681 Value :: Codec ( CustomCodec :: Module ( _, b) , s) => {
@@ -548,17 +690,13 @@ impl Attributes {
548690 }
549691 }
550692 Value :: CborLen ( _, s) => {
551- if let Some ( Value :: Codec ( c, _) ) = self . get ( Kind :: Codec ) {
552- if c. is_module ( ) {
553- return Err ( syn:: Error :: new ( * s, "`cbor_len` and `with` are mutually exclusive" ) )
554- }
693+ if let Some ( Value :: Codec ( c, _) ) = self . get ( Kind :: Codec ) && c. is_module ( ) {
694+ return Err ( syn:: Error :: new ( * s, "`cbor_len` and `with` are mutually exclusive" ) )
555695 }
556696 }
557697 Value :: Borrow ( _, s) => {
558- if let Some ( idx) = self . index ( ) {
559- if idx. is_b ( ) {
560- return Err ( syn:: Error :: new ( * s, "`borrow` and `b` are mutually exclusive" ) )
561- }
698+ if let Some ( idx) = self . index ( ) && idx. is_b ( ) {
699+ return Err ( syn:: Error :: new ( * s, "`borrow` and `b` are mutually exclusive" ) )
562700 }
563701 }
564702 Value :: Index ( idx, s) if idx. is_b ( ) => {
@@ -590,6 +728,7 @@ impl Value {
590728 Value :: CborLen ( _, s) => * s,
591729 Value :: Tag ( _, s) => * s,
592730 Value :: Skip ( s) => * s,
731+ Value :: SkipIf ( _, s) => * s,
593732 Value :: Flat ( s) => * s,
594733 Value :: Default ( s) => * s
595734 }
0 commit comments