1 //! Choice variant IR and lowerings 2 3 use crate::{FieldAttrs, Tag, TypeAttrs}; 4 use proc_macro2::TokenStream; 5 use quote::quote; 6 use syn::{Fields, Ident, Path, Type, Variant}; 7 8 #[derive(Clone, Debug, PartialEq, Eq)] 9 pub(super) enum TagOrPath { 10 Tag(Tag), 11 Path(Path), 12 } 13 14 impl PartialEq<Tag> for TagOrPath { eq(&self, rhs: &Tag) -> bool15 fn eq(&self, rhs: &Tag) -> bool { 16 match self { 17 Self::Tag(lhs) => lhs == rhs, 18 _ => false, 19 } 20 } 21 } 22 23 impl From<Tag> for TagOrPath { from(tag: Tag) -> Self24 fn from(tag: Tag) -> Self { 25 Self::Tag(tag) 26 } 27 } 28 29 impl From<Path> for TagOrPath { from(path: Path) -> Self30 fn from(path: Path) -> Self { 31 Self::Path(path) 32 } 33 } 34 35 impl TryFrom<&Variant> for TagOrPath { 36 type Error = syn::Error; 37 try_from(input: &Variant) -> syn::Result<Self>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 54 impl TagOrPath { to_tokens(&self) -> TokenStream55 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 63 /// "IR" for a variant of a derived `Choice`. 64 pub(super) struct ChoiceVariant { 65 /// Variant name. 66 pub(super) ident: Ident, 67 68 /// "Field" (in this case variant)-level attributes. 69 pub(super) attrs: FieldAttrs, 70 71 /// Tag for the ASN.1 type. 72 pub(super) tag: TagOrPath, 73 } 74 75 impl ChoiceVariant { 76 /// Create a new [`ChoiceVariant`] from the input [`Variant`]. new(input: &Variant, type_attrs: &TypeAttrs) -> syn::Result<Self>77 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 // Validate that variant is a 1-element tuple struct 86 match &input.fields { 87 // TODO(tarcieri): handle 0 bindings for ASN.1 NULL 88 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 /// Derive a match arm of the impl body for `TryFrom<der::asn1::Any<'_>>`. to_decode_tokens(&self) -> TokenStream101 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 /// Derive a match arm for the impl body for `der::EncodeValue::encode_value`. to_encode_value_tokens(&self) -> TokenStream113 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 /// Derive a match arm for the impl body for `der::EncodeValue::value_len`. to_value_len_tokens(&self) -> TokenStream123 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 /// Derive a match arm for the impl body for `der::Tagged::tag`. to_tagged_tokens(&self) -> TokenStream145 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)] 155 mod 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] simple()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] utf8string()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] explicit()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] implicit()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 } 419