der_derive/sequence/
field.rs

1//! Sequence field IR and lowerings
2
3use crate::{Asn1Type, FieldAttrs, TagMode, TagNumber, TypeAttrs};
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::{Field, Ident, Path, Type};
7
8/// "IR" for a field of a derived `Sequence`.
9pub(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
20impl SequenceField {
21    /// Create a new [`SequenceField`] from the input [`Field`].
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.
54    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.
78    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, &self.field_type);
101        }
102
103        lowerer.into_tokens()
104    }
105}
106
107/// AST lowerer for field decoders.
108struct LowerFieldDecoder {
109    /// Decoder-in-progress.
110    decoder: TokenStream,
111}
112
113impl LowerFieldDecoder {
114    /// Create a new field decoder lowerer.
115    fn new(attrs: &FieldAttrs) -> Self {
116        Self {
117            decoder: attrs.decoder(),
118        }
119    }
120
121    ///  the field decoder to tokens.
122    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).
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.
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.
154struct LowerFieldEncoder {
155    /// Encoder-in-progress.
156    encoder: TokenStream,
157}
158
159impl LowerFieldEncoder {
160    /// Create a new field encoder lowerer.
161    fn new(ident: &Ident) -> Self {
162        Self {
163            encoder: quote!(self.#ident),
164        }
165    }
166
167    ///  the field encoder to tokens.
168    fn into_tokens(self) -> TokenStream {
169        self.encoder
170    }
171
172    /// Apply the ASN.1 type (if defined).
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.
192    fn apply_default(&mut self, ident: &Ident, default: &Path, field_type: &Type) {
193        let encoder = &self.encoder;
194
195        self.encoder = quote! {
196            {
197                let default_value: #field_type = #default();
198                if &self.#ident == &default_value {
199                    None
200                } else {
201                    Some(#encoder)
202                }
203            }
204        };
205    }
206
207    /// Make this field context-specific.
208    fn apply_context_specific(
209        &mut self,
210        tag_number: &TagNumber,
211        tag_mode: &TagMode,
212        optional: bool,
213    ) {
214        let encoder = &self.encoder;
215        let number_tokens = tag_number.to_tokens();
216        let mode_tokens = tag_mode.to_tokens();
217
218        if optional {
219            self.encoder = quote! {
220                #encoder.as_ref().map(|field| {
221                    ::der::asn1::ContextSpecificRef {
222                        tag_number: #number_tokens,
223                        tag_mode: #mode_tokens,
224                        value: field,
225                    }
226                })
227            };
228        } else {
229            self.encoder = quote! {
230                ::der::asn1::ContextSpecificRef {
231                    tag_number: #number_tokens,
232                    tag_mode: #mode_tokens,
233                    value: &#encoder,
234                }
235            };
236        }
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::SequenceField;
243    use crate::{FieldAttrs, TagMode, TagNumber};
244    use proc_macro2::Span;
245    use quote::quote;
246    use syn::{punctuated::Punctuated, Ident, Path, PathSegment, Type, TypePath};
247
248    /// Create a [`Type::Path`].
249    pub fn type_path(ident: Ident) -> Type {
250        let mut segments = Punctuated::new();
251        segments.push_value(PathSegment {
252            ident,
253            arguments: Default::default(),
254        });
255
256        Type::Path(TypePath {
257            qself: None,
258            path: Path {
259                leading_colon: None,
260                segments,
261            },
262        })
263    }
264
265    #[test]
266    fn simple() {
267        let span = Span::call_site();
268        let ident = Ident::new("example_field", span);
269
270        let attrs = FieldAttrs {
271            asn1_type: None,
272            context_specific: None,
273            default: None,
274            extensible: false,
275            optional: false,
276            tag_mode: TagMode::Explicit,
277            constructed: false,
278        };
279
280        let field_type = Ident::new("String", span);
281
282        let field = SequenceField {
283            ident,
284            attrs,
285            field_type: type_path(field_type),
286        };
287
288        assert_eq!(
289            field.to_decode_tokens().to_string(),
290            quote! {
291                let example_field = reader.decode()?;
292            }
293            .to_string()
294        );
295
296        assert_eq!(
297            field.to_encode_tokens().to_string(),
298            quote! {
299                self.example_field
300            }
301            .to_string()
302        );
303    }
304
305    #[test]
306    fn implicit() {
307        let span = Span::call_site();
308        let ident = Ident::new("implicit_field", span);
309
310        let attrs = FieldAttrs {
311            asn1_type: None,
312            context_specific: Some(TagNumber(0)),
313            default: None,
314            extensible: false,
315            optional: false,
316            tag_mode: TagMode::Implicit,
317            constructed: false,
318        };
319
320        let field_type = Ident::new("String", span);
321
322        let field = SequenceField {
323            ident,
324            attrs,
325            field_type: type_path(field_type),
326        };
327
328        assert_eq!(
329            field.to_decode_tokens().to_string(),
330            quote! {
331                let implicit_field = ::der::asn1::ContextSpecific::<>::decode_implicit(
332                        reader,
333                        ::der::TagNumber::N0
334                    )?
335                    .ok_or_else(|| {
336                        der::Tag::ContextSpecific {
337                            number: ::der::TagNumber::N0,
338                            constructed: false
339                        }
340                        .value_error()
341                    })?
342                    .value;
343            }
344            .to_string()
345        );
346
347        assert_eq!(
348            field.to_encode_tokens().to_string(),
349            quote! {
350                ::der::asn1::ContextSpecificRef {
351                    tag_number: ::der::TagNumber::N0,
352                    tag_mode: ::der::TagMode::Implicit,
353                    value: &self.implicit_field,
354                }
355            }
356            .to_string()
357        );
358    }
359}