diff --git a/der/derive/src/attributes.rs b/der/derive/src/attributes.rs index 2a50d431a..b95e4f0a7 100644 --- a/der/derive/src/attributes.rs +++ b/der/derive/src/attributes.rs @@ -79,6 +79,9 @@ pub(crate) struct FieldAttrs { /// Is the inner type constructed? pub constructed: bool, + + /// Is the type skipped? + pub skipped: Option, } impl FieldAttrs { @@ -98,6 +101,7 @@ impl FieldAttrs { let mut optional = None; let mut tag_mode = None; let mut constructed = None; + let mut skipped = None; let mut parsed_attrs = Vec::new(); AttrNameValue::from_attributes(attrs, &mut parsed_attrs); @@ -154,6 +158,15 @@ impl FieldAttrs { } constructed = Some(ty); + // `skipped` attribute + } else if attr.parse_value::("skipped").is_some() { + if skipped.is_some() { + abort!(attr.name, "duplicate ASN.1 `skipped` attribute"); + } + + skipped = Some(attr.value.parse().unwrap_or_else(|e| { + abort!(attr.value, "error parsing ASN.1 `skipped` attribute: {}", e) + })); } else { abort!( attr.name, @@ -171,6 +184,7 @@ impl FieldAttrs { optional: optional.unwrap_or_default(), tag_mode: tag_mode.unwrap_or(type_attrs.tag_mode), constructed: constructed.unwrap_or_default(), + skipped, } } diff --git a/der/derive/src/sequence.rs b/der/derive/src/sequence.rs index 15e445266..c0d6585cd 100644 --- a/der/derive/src/sequence.rs +++ b/der/derive/src/sequence.rs @@ -8,7 +8,7 @@ use field::SequenceField; use proc_macro2::TokenStream; use proc_macro_error::abort; use quote::quote; -use syn::{DeriveInput, Ident, Lifetime}; +use syn::{DeriveInput, Ident, Lifetime, TypeParam}; /// Derive the `Sequence` trait for a struct pub(crate) struct DeriveSequence { @@ -18,6 +18,9 @@ pub(crate) struct DeriveSequence { /// Lifetime of the struct. lifetime: Option, + /// Type param of the struct. + type_param: Option, + /// Fields of the struct. fields: Vec, } @@ -40,6 +43,8 @@ impl DeriveSequence { .next() .map(|lt| lt.lifetime.clone()); + let type_param = input.generics.type_params().next().cloned(); + let type_attrs = TypeAttrs::parse(&input.attrs); let fields = data @@ -51,6 +56,7 @@ impl DeriveSequence { Self { ident: input.ident, lifetime, + type_param, fields, } } @@ -72,6 +78,13 @@ impl DeriveSequence { .map(|_| lifetime.clone()) .unwrap_or_default(); + let type_params = self.type_param.as_ref().map(|t| { + let mut t = t.clone(); + t.default = None; + t + }); + let type_params_names = self.type_param.as_ref().map(|t| t.ident.clone()); + let mut decode_body = Vec::new(); let mut decode_result = Vec::new(); let mut encoded_lengths = Vec::new(); @@ -81,13 +94,14 @@ impl DeriveSequence { decode_body.push(field.to_decode_tokens()); decode_result.push(&field.ident); - let field = field.to_encode_tokens(); - encoded_lengths.push(quote!(#field.encoded_len()?)); - encode_fields.push(quote!(#field.encode(writer)?;)); + if let Some(field) = field.to_encode_tokens() { + encoded_lengths.push(quote!(#field.encoded_len()?)); + encode_fields.push(quote!(#field.encode(writer)?;)); + } } quote! { - impl<#lifetime> ::der::DecodeValue<#lifetime> for #ident<#lt_params> { + impl<#lifetime, #type_params> ::der::DecodeValue<#lifetime> for #ident<#lt_params #type_params_names> { fn decode_value>( reader: &mut R, header: ::der::Header, @@ -104,7 +118,7 @@ impl DeriveSequence { } } - impl<#lifetime> ::der::EncodeValue for #ident<#lt_params> { + impl<#lifetime, #type_params> ::der::EncodeValue for #ident<#lt_params #type_params_names> { fn value_len(&self) -> ::der::Result<::der::Length> { use ::der::Encode as _; @@ -122,7 +136,7 @@ impl DeriveSequence { } } - impl<#lifetime> ::der::Sequence<#lifetime> for #ident<#lt_params> {} + impl<#lifetime, #type_params> ::der::Sequence<#lifetime> for #ident<#lt_params #type_params_names> {} } } } diff --git a/der/derive/src/sequence/field.rs b/der/derive/src/sequence/field.rs index 05945ddf2..52824a232 100644 --- a/der/derive/src/sequence/field.rs +++ b/der/derive/src/sequence/field.rs @@ -72,11 +72,22 @@ impl SequenceField { } } + if let Some(default) = &self.attrs.skipped { + let ident = &self.ident; + return quote! { + let #ident = #default(); + }; + } + lowerer.into_tokens(&self.ident) } /// Derive code for encoding a field of a sequence. - pub(super) fn to_encode_tokens(&self) -> TokenStream { + pub(super) fn to_encode_tokens(&self) -> Option { + if self.attrs.skipped.is_some() { + return None; + } + let mut lowerer = LowerFieldEncoder::new(&self.ident); let attrs = &self.attrs; @@ -101,7 +112,7 @@ impl SequenceField { lowerer.apply_default(&self.ident, default); } - lowerer.into_tokens() + Some(lowerer.into_tokens()) } } @@ -241,7 +252,7 @@ mod tests { use crate::{FieldAttrs, TagMode, TagNumber}; use proc_macro2::Span; use quote::quote; - use syn::{punctuated::Punctuated, Ident, Path, PathSegment, Type, TypePath}; + use syn::{punctuated::Punctuated, Ident, Path, PathArguments, PathSegment, Type, TypePath}; /// Create a [`Type::Path`]. pub fn type_path(ident: Ident) -> Type { @@ -273,6 +284,7 @@ mod tests { optional: false, tag_mode: TagMode::Explicit, constructed: false, + skipped: None, }; let field_type = Ident::new("String", span); @@ -292,7 +304,7 @@ mod tests { ); assert_eq!( - field.to_encode_tokens().to_string(), + field.to_encode_tokens().unwrap().to_string(), quote! { self.example_field } @@ -313,6 +325,7 @@ mod tests { optional: false, tag_mode: TagMode::Implicit, constructed: false, + skipped: None, }; let field_type = Ident::new("String", span); @@ -343,7 +356,7 @@ mod tests { ); assert_eq!( - field.to_encode_tokens().to_string(), + field.to_encode_tokens().unwrap().to_string(), quote! { ::der::asn1::ContextSpecificRef { tag_number: ::der::TagNumber::N0, @@ -354,4 +367,52 @@ mod tests { .to_string() ); } + + #[test] + fn skipped() { + let span = Span::call_site(); + let ident = Ident::new("skipped", span); + + let mut segments = Punctuated::new(); + segments.push(PathSegment { + ident: Ident::new("Default", span), + arguments: PathArguments::None, + }); + segments.push(PathSegment { + ident: Ident::new("default", span), + arguments: PathArguments::None, + }); + + let attrs = FieldAttrs { + asn1_type: None, + context_specific: Some(TagNumber(0)), + default: None, + extensible: false, + optional: false, + tag_mode: TagMode::Implicit, + constructed: false, + skipped: Some(Path { + leading_colon: None, + segments, + }), + }; + + let field_type = Ident::new("String", span); + + let field = SequenceField { + ident, + attrs, + field_type: type_path(field_type), + }; + + assert_eq!( + field.to_decode_tokens().to_string(), + quote! { + let skipped = Default::default(); + } + .to_string() + ); + + assert!(field.to_encode_tokens().is_none()); + } } diff --git a/der/derive/src/value_ord.rs b/der/derive/src/value_ord.rs index 97e37cae5..ef06c047f 100644 --- a/der/derive/src/value_ord.rs +++ b/der/derive/src/value_ord.rs @@ -171,6 +171,10 @@ impl ValueField { /// Lower to [`TokenStream`]. fn to_tokens(&self) -> TokenStream { + if self.attrs.skipped.is_some() { + return TokenStream::default(); + } + let ident = &self.ident; if self.is_enum {