• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Sequence field IR and lowerings
2 
3 use crate::{Asn1Type, FieldAttrs, TagMode, TagNumber, TypeAttrs};
4 use proc_macro2::TokenStream;
5 use quote::quote;
6 use syn::{Field, Ident, Path, Type};
7 
8 /// "IR" for a field of a derived `Sequence`.
9 pub(super) struct SequenceField {
10     /// Variant name.
11     pub(super) ident: Ident,
12 
13     /// Field-level attributes.
14     pub(super) attrs: FieldAttrs,
15 
16     /// Field type
17     pub(super) field_type: Type,
18 }
19 
20 impl SequenceField {
21     /// Create a new [`SequenceField`] from the input [`Field`].
new(field: &Field, type_attrs: &TypeAttrs) -> syn::Result<Self>22     pub(super) fn new(field: &Field, type_attrs: &TypeAttrs) -> syn::Result<Self> {
23         let ident = field.ident.as_ref().cloned().ok_or_else(|| {
24             syn::Error::new_spanned(
25                 field,
26                 "no name on struct field i.e. tuple structs unsupported",
27             )
28         })?;
29 
30         let attrs = FieldAttrs::parse(&field.attrs, type_attrs)?;
31 
32         if attrs.asn1_type.is_some() && attrs.default.is_some() {
33             return Err(syn::Error::new_spanned(
34                 ident,
35                 "ASN.1 `type` and `default` options cannot be combined",
36             ));
37         }
38 
39         if attrs.default.is_some() && attrs.optional {
40             return Err(syn::Error::new_spanned(
41                 ident,
42                 "`optional` and `default` field qualifiers are mutually exclusive",
43             ));
44         }
45 
46         Ok(Self {
47             ident,
48             attrs,
49             field_type: field.ty.clone(),
50         })
51     }
52 
53     /// Derive code for decoding a field of a sequence.
to_decode_tokens(&self) -> TokenStream54     pub(super) fn to_decode_tokens(&self) -> TokenStream {
55         let mut lowerer = LowerFieldDecoder::new(&self.attrs);
56 
57         if self.attrs.asn1_type.is_some() {
58             lowerer.apply_asn1_type(self.attrs.optional);
59         }
60 
61         if let Some(default) = &self.attrs.default {
62             // TODO(tarcieri): default in conjunction with ASN.1 types?
63             debug_assert!(
64                 self.attrs.asn1_type.is_none(),
65                 "`type` and `default` are mutually exclusive"
66             );
67 
68             // TODO(tarcieri): support for context-specific fields with defaults?
69             if self.attrs.context_specific.is_none() {
70                 lowerer.apply_default(default, &self.field_type);
71             }
72         }
73 
74         lowerer.into_tokens(&self.ident)
75     }
76 
77     /// Derive code for encoding a field of a sequence.
to_encode_tokens(&self) -> TokenStream78     pub(super) fn to_encode_tokens(&self) -> TokenStream {
79         let mut lowerer = LowerFieldEncoder::new(&self.ident);
80         let attrs = &self.attrs;
81 
82         if let Some(ty) = &attrs.asn1_type {
83             // TODO(tarcieri): default in conjunction with ASN.1 types?
84             debug_assert!(
85                 attrs.default.is_none(),
86                 "`type` and `default` are mutually exclusive"
87             );
88             lowerer.apply_asn1_type(ty, attrs.optional);
89         }
90 
91         if let Some(tag_number) = &attrs.context_specific {
92             lowerer.apply_context_specific(tag_number, &attrs.tag_mode, attrs.optional);
93         }
94 
95         if let Some(default) = &attrs.default {
96             debug_assert!(
97                 !attrs.optional,
98                 "`default`, and `optional` are mutually exclusive"
99             );
100             lowerer.apply_default(&self.ident, default);
101         }
102 
103         lowerer.into_tokens()
104     }
105 }
106 
107 /// AST lowerer for field decoders.
108 struct LowerFieldDecoder {
109     /// Decoder-in-progress.
110     decoder: TokenStream,
111 }
112 
113 impl LowerFieldDecoder {
114     /// Create a new field decoder lowerer.
new(attrs: &FieldAttrs) -> Self115     fn new(attrs: &FieldAttrs) -> Self {
116         Self {
117             decoder: attrs.decoder(),
118         }
119     }
120 
121     ///  the field decoder to tokens.
into_tokens(self, ident: &Ident) -> TokenStream122     fn into_tokens(self, ident: &Ident) -> TokenStream {
123         let decoder = self.decoder;
124 
125         quote! {
126             let #ident = #decoder;
127         }
128     }
129 
130     /// Apply the ASN.1 type (if defined).
apply_asn1_type(&mut self, optional: bool)131     fn apply_asn1_type(&mut self, optional: bool) {
132         let decoder = &self.decoder;
133 
134         self.decoder = if optional {
135             quote! {
136                 #decoder.map(TryInto::try_into).transpose()?
137             }
138         } else {
139             quote! {
140                 #decoder.try_into()?
141             }
142         }
143     }
144 
145     /// Handle default value for a type.
apply_default(&mut self, default: &Path, field_type: &Type)146     fn apply_default(&mut self, default: &Path, field_type: &Type) {
147         self.decoder = quote! {
148             Option::<#field_type>::decode(reader)?.unwrap_or_else(#default);
149         };
150     }
151 }
152 
153 /// AST lowerer for field encoders.
154 struct LowerFieldEncoder {
155     /// Encoder-in-progress.
156     encoder: TokenStream,
157 }
158 
159 impl LowerFieldEncoder {
160     /// Create a new field encoder lowerer.
new(ident: &Ident) -> Self161     fn new(ident: &Ident) -> Self {
162         Self {
163             encoder: quote!(self.#ident),
164         }
165     }
166 
167     ///  the field encoder to tokens.
into_tokens(self) -> TokenStream168     fn into_tokens(self) -> TokenStream {
169         self.encoder
170     }
171 
172     /// Apply the ASN.1 type (if defined).
apply_asn1_type(&mut self, asn1_type: &Asn1Type, optional: bool)173     fn apply_asn1_type(&mut self, asn1_type: &Asn1Type, optional: bool) {
174         let binding = &self.encoder;
175 
176         self.encoder = if optional {
177             let map_arg = quote!(field);
178             let encoder = asn1_type.encoder(&map_arg);
179 
180             quote! {
181                 #binding.as_ref().map(|#map_arg| {
182                     der::Result::Ok(#encoder)
183                 }).transpose()?
184             }
185         } else {
186             let encoder = asn1_type.encoder(binding);
187             quote!(#encoder)
188         };
189     }
190 
191     /// Handle default value for a type.
apply_default(&mut self, ident: &Ident, default: &Path)192     fn apply_default(&mut self, ident: &Ident, default: &Path) {
193         let encoder = &self.encoder;
194 
195         self.encoder = quote! {
196             if &self.#ident == &#default() {
197                 None
198             } else {
199                 Some(#encoder)
200             }
201         };
202     }
203 
204     /// Make this field context-specific.
apply_context_specific( &mut self, tag_number: &TagNumber, tag_mode: &TagMode, optional: bool, )205     fn apply_context_specific(
206         &mut self,
207         tag_number: &TagNumber,
208         tag_mode: &TagMode,
209         optional: bool,
210     ) {
211         let encoder = &self.encoder;
212         let number_tokens = tag_number.to_tokens();
213         let mode_tokens = tag_mode.to_tokens();
214 
215         if optional {
216             self.encoder = quote! {
217                 #encoder.as_ref().map(|field| {
218                     ::der::asn1::ContextSpecificRef {
219                         tag_number: #number_tokens,
220                         tag_mode: #mode_tokens,
221                         value: field,
222                     }
223                 })
224             };
225         } else {
226             self.encoder = quote! {
227                 ::der::asn1::ContextSpecificRef {
228                     tag_number: #number_tokens,
229                     tag_mode: #mode_tokens,
230                     value: &#encoder,
231                 }
232             };
233         }
234     }
235 }
236 
237 #[cfg(test)]
238 mod tests {
239     use super::SequenceField;
240     use crate::{FieldAttrs, TagMode, TagNumber};
241     use proc_macro2::Span;
242     use quote::quote;
243     use syn::{punctuated::Punctuated, Ident, Path, PathSegment, Type, TypePath};
244 
245     /// Create a [`Type::Path`].
type_path(ident: Ident) -> Type246     pub fn type_path(ident: Ident) -> Type {
247         let mut segments = Punctuated::new();
248         segments.push_value(PathSegment {
249             ident,
250             arguments: Default::default(),
251         });
252 
253         Type::Path(TypePath {
254             qself: None,
255             path: Path {
256                 leading_colon: None,
257                 segments,
258             },
259         })
260     }
261 
262     #[test]
simple()263     fn simple() {
264         let span = Span::call_site();
265         let ident = Ident::new("example_field", span);
266 
267         let attrs = FieldAttrs {
268             asn1_type: None,
269             context_specific: None,
270             default: None,
271             extensible: false,
272             optional: false,
273             tag_mode: TagMode::Explicit,
274             constructed: false,
275         };
276 
277         let field_type = Ident::new("String", span);
278 
279         let field = SequenceField {
280             ident,
281             attrs,
282             field_type: type_path(field_type),
283         };
284 
285         assert_eq!(
286             field.to_decode_tokens().to_string(),
287             quote! {
288                 let example_field = reader.decode()?;
289             }
290             .to_string()
291         );
292 
293         assert_eq!(
294             field.to_encode_tokens().to_string(),
295             quote! {
296                 self.example_field
297             }
298             .to_string()
299         );
300     }
301 
302     #[test]
implicit()303     fn implicit() {
304         let span = Span::call_site();
305         let ident = Ident::new("implicit_field", span);
306 
307         let attrs = FieldAttrs {
308             asn1_type: None,
309             context_specific: Some(TagNumber(0)),
310             default: None,
311             extensible: false,
312             optional: false,
313             tag_mode: TagMode::Implicit,
314             constructed: false,
315         };
316 
317         let field_type = Ident::new("String", span);
318 
319         let field = SequenceField {
320             ident,
321             attrs,
322             field_type: type_path(field_type),
323         };
324 
325         assert_eq!(
326             field.to_decode_tokens().to_string(),
327             quote! {
328                 let implicit_field = ::der::asn1::ContextSpecific::<>::decode_implicit(
329                         reader,
330                         ::der::TagNumber::N0
331                     )?
332                     .ok_or_else(|| {
333                         der::Tag::ContextSpecific {
334                             number: ::der::TagNumber::N0,
335                             constructed: false
336                         }
337                         .value_error()
338                     })?
339                     .value;
340             }
341             .to_string()
342         );
343 
344         assert_eq!(
345             field.to_encode_tokens().to_string(),
346             quote! {
347                 ::der::asn1::ContextSpecificRef {
348                     tag_number: ::der::TagNumber::N0,
349                     tag_mode: ::der::TagMode::Implicit,
350                     value: &self.implicit_field,
351                 }
352             }
353             .to_string()
354         );
355     }
356 }
357