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