• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Choice variant IR and lowerings
2 
3 use crate::{FieldAttrs, Tag, TypeAttrs};
4 use proc_macro2::TokenStream;
5 use proc_macro_error::abort;
6 use quote::quote;
7 use syn::{Fields, Ident, Path, Type, Variant};
8 
9 #[derive(Clone, Debug, PartialEq, Eq)]
10 pub(super) enum TagOrPath {
11     Tag(Tag),
12     Path(Path),
13 }
14 
15 impl PartialEq<Tag> for TagOrPath {
eq(&self, rhs: &Tag) -> bool16     fn eq(&self, rhs: &Tag) -> bool {
17         match self {
18             Self::Tag(lhs) => lhs == rhs,
19             _ => false,
20         }
21     }
22 }
23 
24 impl From<Tag> for TagOrPath {
from(tag: Tag) -> Self25     fn from(tag: Tag) -> Self {
26         Self::Tag(tag)
27     }
28 }
29 
30 impl From<Path> for TagOrPath {
from(path: Path) -> Self31     fn from(path: Path) -> Self {
32         Self::Path(path)
33     }
34 }
35 
36 impl From<&Variant> for TagOrPath {
from(input: &Variant) -> Self37     fn from(input: &Variant) -> Self {
38         if let Fields::Unnamed(fields) = &input.fields {
39             if fields.unnamed.len() == 1 {
40                 if let Type::Path(path) = &fields.unnamed[0].ty {
41                     return path.path.clone().into();
42                 }
43             }
44         }
45 
46         abort!(
47             &input.ident,
48             "no #[asn1(type=...)] specified for enum variant"
49         )
50     }
51 }
52 
53 impl TagOrPath {
to_tokens(&self) -> TokenStream54     pub fn to_tokens(&self) -> TokenStream {
55         match self {
56             Self::Tag(tag) => tag.to_tokens(),
57             Self::Path(path) => quote! { <#path as ::der::FixedTag>::TAG },
58         }
59     }
60 }
61 
62 /// "IR" for a variant of a derived `Choice`.
63 pub(super) struct ChoiceVariant {
64     /// Variant name.
65     pub(super) ident: Ident,
66 
67     /// "Field" (in this case variant)-level attributes.
68     pub(super) attrs: FieldAttrs,
69 
70     /// Tag for the ASN.1 type.
71     pub(super) tag: TagOrPath,
72 }
73 
74 impl ChoiceVariant {
75     /// Create a new [`ChoiceVariant`] from the input [`Variant`].
new(input: &Variant, type_attrs: &TypeAttrs) -> Self76     pub(super) fn new(input: &Variant, type_attrs: &TypeAttrs) -> Self {
77         let ident = input.ident.clone();
78         let attrs = FieldAttrs::parse(&input.attrs, type_attrs);
79 
80         if attrs.extensible {
81             abort!(&ident, "`extensible` is not allowed on CHOICE");
82         }
83 
84         // Validate that variant is a 1-element tuple struct
85         match &input.fields {
86             // TODO(tarcieri): handle 0 bindings for ASN.1 NULL
87             Fields::Unnamed(fields) if fields.unnamed.len() == 1 => (),
88             _ => abort!(&ident, "enum variant must be a 1-element tuple struct"),
89         }
90 
91         let tag = attrs
92             .tag()
93             .map(TagOrPath::from)
94             .unwrap_or_else(|| TagOrPath::from(input));
95 
96         Self { ident, attrs, tag }
97     }
98 
99     /// Derive a match arm of the impl body for `TryFrom<der::asn1::Any<'_>>`.
to_decode_tokens(&self) -> TokenStream100     pub(super) fn to_decode_tokens(&self) -> TokenStream {
101         let tag = self.tag.to_tokens();
102         let ident = &self.ident;
103         let decoder = self.attrs.decoder();
104 
105         match self.attrs.asn1_type {
106             Some(..) => quote! { #tag => Ok(Self::#ident(#decoder.try_into()?)), },
107             None => quote! { #tag => Ok(Self::#ident(#decoder)), },
108         }
109     }
110 
111     /// Derive a match arm for the impl body for `der::EncodeValue::encode_value`.
to_encode_value_tokens(&self) -> TokenStream112     pub(super) fn to_encode_value_tokens(&self) -> TokenStream {
113         let ident = &self.ident;
114         let binding = quote!(variant);
115         let encoder = self.attrs.value_encode(&binding);
116         quote! {
117             Self::#ident(#binding) => #encoder,
118         }
119     }
120 
121     /// Derive a match arm for the impl body for `der::EncodeValue::value_len`.
to_value_len_tokens(&self) -> TokenStream122     pub(super) fn to_value_len_tokens(&self) -> TokenStream {
123         let ident = &self.ident;
124 
125         match self.attrs.context_specific {
126             Some(tag_number) => {
127                 let tag_number = tag_number.to_tokens();
128                 let tag_mode = self.attrs.tag_mode.to_tokens();
129 
130                 quote! {
131                     Self::#ident(variant) => ::der::asn1::ContextSpecificRef {
132                         tag_number: #tag_number,
133                         tag_mode: #tag_mode,
134                         value: variant,
135                     }.value_len(),
136                 }
137             }
138 
139             _ => quote! { Self::#ident(variant) => variant.value_len(), },
140         }
141     }
142 
143     /// Derive a match arm for the impl body for `der::Tagged::tag`.
to_tagged_tokens(&self) -> TokenStream144     pub(super) fn to_tagged_tokens(&self) -> TokenStream {
145         let ident = &self.ident;
146         let tag = self.tag.to_tokens();
147         quote! {
148             Self::#ident(_) => #tag,
149         }
150     }
151 }
152 
153 #[cfg(test)]
154 mod tests {
155     use super::ChoiceVariant;
156     use crate::{choice::variant::TagOrPath, Asn1Type, FieldAttrs, Tag, TagMode, TagNumber};
157     use proc_macro2::Span;
158     use quote::quote;
159     use syn::Ident;
160 
161     #[test]
simple()162     fn simple() {
163         let ident = Ident::new("ExampleVariant", Span::call_site());
164         let attrs = FieldAttrs::default();
165         let tag = Tag::Universal(Asn1Type::Utf8String).into();
166         let variant = ChoiceVariant { ident, attrs, tag };
167 
168         assert_eq!(
169             variant.to_decode_tokens().to_string(),
170             quote! {
171                 ::der::Tag::Utf8String => Ok(Self::ExampleVariant(
172                     reader.decode()?
173                 )),
174             }
175             .to_string()
176         );
177 
178         assert_eq!(
179             variant.to_encode_value_tokens().to_string(),
180             quote! {
181                 Self::ExampleVariant(variant) => variant.encode_value(encoder),
182             }
183             .to_string()
184         );
185 
186         assert_eq!(
187             variant.to_value_len_tokens().to_string(),
188             quote! {
189                 Self::ExampleVariant(variant) => variant.value_len(),
190             }
191             .to_string()
192         );
193 
194         assert_eq!(
195             variant.to_tagged_tokens().to_string(),
196             quote! {
197                 Self::ExampleVariant(_) => ::der::Tag::Utf8String,
198             }
199             .to_string()
200         )
201     }
202 
203     #[test]
utf8string()204     fn utf8string() {
205         let ident = Ident::new("ExampleVariant", Span::call_site());
206         let attrs = FieldAttrs {
207             asn1_type: Some(Asn1Type::Utf8String),
208             ..Default::default()
209         };
210         let tag = Tag::Universal(Asn1Type::Utf8String).into();
211         let variant = ChoiceVariant { ident, attrs, tag };
212 
213         assert_eq!(
214             variant.to_decode_tokens().to_string(),
215             quote! {
216                 ::der::Tag::Utf8String => Ok(Self::ExampleVariant(
217                     ::der::asn1::Utf8StringRef::decode(reader)?
218                     .try_into()?
219                 )),
220             }
221             .to_string()
222         );
223 
224         assert_eq!(
225             variant.to_encode_value_tokens().to_string(),
226             quote! {
227                 Self::ExampleVariant(variant) => ::der::asn1::Utf8StringRef::new(variant)?.encode_value(encoder),
228             }
229             .to_string()
230         );
231 
232         assert_eq!(
233             variant.to_value_len_tokens().to_string(),
234             quote! {
235                 Self::ExampleVariant(variant) => variant.value_len(),
236             }
237             .to_string()
238         );
239 
240         assert_eq!(
241             variant.to_tagged_tokens().to_string(),
242             quote! {
243                 Self::ExampleVariant(_) => ::der::Tag::Utf8String,
244             }
245             .to_string()
246         )
247     }
248 
249     #[test]
explicit()250     fn explicit() {
251         for tag_number in [0, 1, 2, 3] {
252             for constructed in [false, true] {
253                 let ident = Ident::new("ExplicitVariant", Span::call_site());
254                 let attrs = FieldAttrs {
255                     constructed,
256                     context_specific: Some(TagNumber(tag_number)),
257                     ..Default::default()
258                 };
259                 assert_eq!(attrs.tag_mode, TagMode::Explicit);
260 
261                 let tag = TagOrPath::Tag(Tag::ContextSpecific {
262                     constructed,
263                     number: TagNumber(tag_number),
264                 });
265 
266                 let variant = ChoiceVariant { ident, attrs, tag };
267                 let tag_number = TagNumber(tag_number).to_tokens();
268 
269                 assert_eq!(
270                     variant.to_decode_tokens().to_string(),
271                     quote! {
272                         ::der::Tag::ContextSpecific {
273                             constructed: #constructed,
274                             number: #tag_number,
275                         } => Ok(Self::ExplicitVariant(
276                             match ::der::asn1::ContextSpecific::<>::decode(reader)? {
277                                 field if field.tag_number == #tag_number => Some(field),
278                                 _ => None
279                             }
280                             .ok_or_else(|| {
281                                 der::Tag::ContextSpecific {
282                                     number: #tag_number,
283                                     constructed: #constructed
284                                 }
285                                 .value_error()
286                             })?
287                             .value
288                         )),
289                     }
290                     .to_string()
291                 );
292 
293                 assert_eq!(
294                     variant.to_encode_value_tokens().to_string(),
295                     quote! {
296                         Self::ExplicitVariant(variant) => ::der::asn1::ContextSpecificRef {
297                             tag_number: #tag_number,
298                             tag_mode: ::der::TagMode::Explicit,
299                             value: variant,
300                         }
301                         .encode_value(encoder),
302                     }
303                     .to_string()
304                 );
305 
306                 assert_eq!(
307                     variant.to_value_len_tokens().to_string(),
308                     quote! {
309                         Self::ExplicitVariant(variant) => ::der::asn1::ContextSpecificRef {
310                             tag_number: #tag_number,
311                             tag_mode: ::der::TagMode::Explicit,
312                             value: variant,
313                         }
314                         .value_len(),
315                     }
316                     .to_string()
317                 );
318 
319                 assert_eq!(
320                     variant.to_tagged_tokens().to_string(),
321                     quote! {
322                         Self::ExplicitVariant(_) => ::der::Tag::ContextSpecific {
323                             constructed: #constructed,
324                             number: #tag_number,
325                         },
326                     }
327                     .to_string()
328                 )
329             }
330         }
331     }
332 
333     #[test]
implicit()334     fn implicit() {
335         for tag_number in [0, 1, 2, 3] {
336             for constructed in [false, true] {
337                 let ident = Ident::new("ImplicitVariant", Span::call_site());
338 
339                 let attrs = FieldAttrs {
340                     constructed,
341                     context_specific: Some(TagNumber(tag_number)),
342                     tag_mode: TagMode::Implicit,
343                     ..Default::default()
344                 };
345 
346                 let tag = TagOrPath::Tag(Tag::ContextSpecific {
347                     constructed,
348                     number: TagNumber(tag_number),
349                 });
350 
351                 let variant = ChoiceVariant { ident, attrs, tag };
352                 let tag_number = TagNumber(tag_number).to_tokens();
353 
354                 assert_eq!(
355                     variant.to_decode_tokens().to_string(),
356                     quote! {
357                         ::der::Tag::ContextSpecific {
358                             constructed: #constructed,
359                             number: #tag_number,
360                         } => Ok(Self::ImplicitVariant(
361                             ::der::asn1::ContextSpecific::<>::decode_implicit(
362                                 reader,
363                                 #tag_number
364                             )?
365                             .ok_or_else(|| {
366                                 der::Tag::ContextSpecific {
367                                   number: #tag_number,
368                                   constructed: #constructed
369                                 }
370                                 .value_error()
371                             })?
372                             .value
373                         )),
374                     }
375                     .to_string()
376                 );
377 
378                 assert_eq!(
379                     variant.to_encode_value_tokens().to_string(),
380                     quote! {
381                         Self::ImplicitVariant(variant) => ::der::asn1::ContextSpecificRef {
382                             tag_number: #tag_number,
383                             tag_mode: ::der::TagMode::Implicit,
384                             value: variant,
385                         }
386                         .encode_value(encoder),
387                     }
388                     .to_string()
389                 );
390 
391                 assert_eq!(
392                     variant.to_value_len_tokens().to_string(),
393                     quote! {
394                         Self::ImplicitVariant(variant) => ::der::asn1::ContextSpecificRef {
395                             tag_number: #tag_number,
396                             tag_mode: ::der::TagMode::Implicit,
397                             value: variant,
398                         }
399                         .value_len(),
400                     }
401                     .to_string()
402                 );
403 
404                 assert_eq!(
405                     variant.to_tagged_tokens().to_string(),
406                     quote! {
407                         Self::ImplicitVariant(_) => ::der::Tag::ContextSpecific {
408                             constructed: #constructed,
409                             number: #tag_number,
410                         },
411                     }
412                     .to_string()
413                 )
414             }
415         }
416     }
417 }
418