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