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