der_derive/
attributes.rs

1//! Attribute-related types used by the proc macro
2
3use crate::{Asn1Type, Tag, TagMode, TagNumber};
4use proc_macro2::{Span, TokenStream};
5use quote::quote;
6use std::{fmt::Debug, str::FromStr};
7use syn::punctuated::Punctuated;
8use syn::{parse::Parse, parse::ParseStream, Attribute, Ident, LitStr, Path, Token};
9
10/// Attribute name.
11pub(crate) const ATTR_NAME: &str = "asn1";
12
13/// Attributes on a `struct` or `enum` type.
14#[derive(Clone, Debug, Default)]
15pub(crate) struct TypeAttrs {
16    /// Tagging mode for this type: `EXPLICIT` or `IMPLICIT`, supplied as
17    /// `#[asn1(tag_mode = "...")]`.
18    ///
19    /// The default value is `EXPLICIT`.
20    pub tag_mode: TagMode,
21}
22
23impl TypeAttrs {
24    /// Parse attributes from a struct field or enum variant.
25    pub fn parse(attrs: &[Attribute]) -> syn::Result<Self> {
26        let mut tag_mode = None;
27
28        let mut parsed_attrs = Vec::new();
29        AttrNameValue::from_attributes(attrs, &mut parsed_attrs)?;
30
31        for attr in parsed_attrs {
32            // `tag_mode = "..."` attribute
33            let mode = attr.parse_value("tag_mode")?.ok_or_else(|| {
34                syn::Error::new_spanned(
35                    &attr.name,
36                    "invalid `asn1` attribute (valid options are `tag_mode`)",
37                )
38            })?;
39
40            if tag_mode.is_some() {
41                return Err(syn::Error::new_spanned(
42                    &attr.name,
43                    "duplicate ASN.1 `tag_mode` attribute",
44                ));
45            }
46
47            tag_mode = Some(mode);
48        }
49
50        Ok(Self {
51            tag_mode: tag_mode.unwrap_or_default(),
52        })
53    }
54}
55
56/// Field-level attributes.
57#[derive(Clone, Debug, Default)]
58pub(crate) struct FieldAttrs {
59    /// Value of the `#[asn1(type = "...")]` attribute if provided.
60    pub asn1_type: Option<Asn1Type>,
61
62    /// Value of the `#[asn1(context_specific = "...")] attribute if provided.
63    pub context_specific: Option<TagNumber>,
64
65    /// Indicates name of function that supplies the default value, which will be used in cases
66    /// where encoding is omitted per DER and to omit the encoding per DER
67    pub default: Option<Path>,
68
69    /// Is this field "extensible", i.e. preceded by the `...` extensibility marker?
70    pub extensible: bool,
71
72    /// Is this field `OPTIONAL`?
73    pub optional: bool,
74
75    /// Tagging mode for this type: `EXPLICIT` or `IMPLICIT`, supplied as
76    /// `#[asn1(tag_mode = "...")]`.
77    ///
78    /// Inherits from the type-level tagging mode if specified, or otherwise
79    /// defaults to `EXPLICIT`.
80    pub tag_mode: TagMode,
81
82    /// Is the inner type constructed?
83    pub constructed: bool,
84}
85
86impl FieldAttrs {
87    /// Return true when either an optional or default ASN.1 attribute is associated
88    /// with a field. Default signifies optionality due to omission of default values in
89    /// DER encodings.
90    fn is_optional(&self) -> bool {
91        self.optional || self.default.is_some()
92    }
93
94    /// Parse attributes from a struct field or enum variant.
95    pub fn parse(attrs: &[Attribute], type_attrs: &TypeAttrs) -> syn::Result<Self> {
96        let mut asn1_type = None;
97        let mut context_specific = None;
98        let mut default = None;
99        let mut extensible = None;
100        let mut optional = None;
101        let mut tag_mode = None;
102        let mut constructed = None;
103
104        let mut parsed_attrs = Vec::new();
105        AttrNameValue::from_attributes(attrs, &mut parsed_attrs)?;
106
107        for attr in parsed_attrs {
108            // `context_specific = "..."` attribute
109            if let Some(tag_number) = attr.parse_value("context_specific")? {
110                if context_specific.is_some() {
111                    abort!(attr.name, "duplicate ASN.1 `context_specific` attribute");
112                }
113
114                context_specific = Some(tag_number);
115            // `default` attribute
116            } else if attr.parse_value::<String>("default")?.is_some() {
117                if default.is_some() {
118                    abort!(attr.name, "duplicate ASN.1 `default` attribute");
119                }
120
121                default = Some(attr.value.parse().map_err(|e| {
122                    syn::Error::new_spanned(
123                        attr.value,
124                        format_args!("error parsing ASN.1 `default` attribute: {e}"),
125                    )
126                })?);
127            // `extensible` attribute
128            } else if let Some(ext) = attr.parse_value("extensible")? {
129                if extensible.is_some() {
130                    abort!(attr.name, "duplicate ASN.1 `extensible` attribute");
131                }
132
133                extensible = Some(ext);
134            // `optional` attribute
135            } else if let Some(opt) = attr.parse_value("optional")? {
136                if optional.is_some() {
137                    abort!(attr.name, "duplicate ASN.1 `optional` attribute");
138                }
139
140                optional = Some(opt);
141            // `tag_mode` attribute
142            } else if let Some(mode) = attr.parse_value("tag_mode")? {
143                if tag_mode.is_some() {
144                    abort!(attr.name, "duplicate ASN.1 `tag_mode` attribute");
145                }
146
147                tag_mode = Some(mode);
148            // `type = "..."` attribute
149            } else if let Some(ty) = attr.parse_value("type")? {
150                if asn1_type.is_some() {
151                    abort!(attr.name, "duplicate ASN.1 `type` attribute");
152                }
153
154                asn1_type = Some(ty);
155            // `constructed = "..."` attribute
156            } else if let Some(ty) = attr.parse_value("constructed")? {
157                if constructed.is_some() {
158                    abort!(attr.name, "duplicate ASN.1 `constructed` attribute");
159                }
160
161                constructed = Some(ty);
162            } else {
163                abort!(
164                    attr.name,
165                    "unknown field-level `asn1` attribute \
166                    (valid options are `context_specific`, `type`)",
167                );
168            }
169        }
170
171        Ok(Self {
172            asn1_type,
173            context_specific,
174            default,
175            extensible: extensible.unwrap_or_default(),
176            optional: optional.unwrap_or_default(),
177            tag_mode: tag_mode.unwrap_or(type_attrs.tag_mode),
178            constructed: constructed.unwrap_or_default(),
179        })
180    }
181
182    /// Get the expected [`Tag`] for this field.
183    pub fn tag(&self) -> syn::Result<Option<Tag>> {
184        match self.context_specific {
185            Some(tag_number) => Ok(Some(Tag::ContextSpecific {
186                constructed: self.constructed,
187                number: tag_number,
188            })),
189
190            None => match self.tag_mode {
191                TagMode::Explicit => Ok(self.asn1_type.map(Tag::Universal)),
192                TagMode::Implicit => Err(syn::Error::new(
193                    Span::call_site(),
194                    "implicit tagging requires a `tag_number`",
195                )),
196            },
197        }
198    }
199
200    /// Get a `der::Decoder` object which respects these field attributes.
201    pub fn decoder(&self) -> TokenStream {
202        if let Some(tag_number) = self.context_specific {
203            let type_params = self.asn1_type.map(|ty| ty.type_path()).unwrap_or_default();
204            let tag_number = tag_number.to_tokens();
205
206            let context_specific = match self.tag_mode {
207                TagMode::Explicit => {
208                    if self.extensible || self.is_optional() {
209                        quote! {
210                            ::der::asn1::ContextSpecific::<#type_params>::decode_explicit(
211                                reader,
212                                #tag_number
213                            )?
214                        }
215                    } else {
216                        quote! {
217                            match ::der::asn1::ContextSpecific::<#type_params>::decode(reader)? {
218                                field if field.tag_number == #tag_number => Some(field),
219                                _ => None
220                            }
221                        }
222                    }
223                }
224                TagMode::Implicit => {
225                    quote! {
226                        ::der::asn1::ContextSpecific::<#type_params>::decode_implicit(
227                            reader,
228                            #tag_number
229                        )?
230                    }
231                }
232            };
233
234            if self.is_optional() {
235                if let Some(default) = &self.default {
236                    quote!(#context_specific.map(|cs| cs.value).unwrap_or_else(#default))
237                } else {
238                    quote!(#context_specific.map(|cs| cs.value))
239                }
240            } else {
241                // TODO(tarcieri): better error handling?
242                let constructed = self.constructed;
243                quote! {
244                    #context_specific.ok_or_else(|| {
245                        der::Tag::ContextSpecific {
246                            number: #tag_number,
247                            constructed: #constructed
248                        }.value_error()
249                    })?.value
250                }
251            }
252        } else if let Some(default) = &self.default {
253            let type_params = self.asn1_type.map(|ty| ty.type_path()).unwrap_or_default();
254            self.asn1_type.map(|ty| ty.decoder()).unwrap_or_else(|| {
255                quote! {
256                    Option::<#type_params>::decode(reader)?.unwrap_or_else(#default),
257                }
258            })
259        } else {
260            self.asn1_type
261                .map(|ty| ty.decoder())
262                .unwrap_or_else(|| quote!(reader.decode()?))
263        }
264    }
265
266    /// Get tokens to encode the binding using `::der::EncodeValue`.
267    pub fn value_encode(&self, binding: &TokenStream) -> TokenStream {
268        match self.context_specific {
269            Some(tag_number) => {
270                let tag_number = tag_number.to_tokens();
271                let tag_mode = self.tag_mode.to_tokens();
272                quote! {
273                    ::der::asn1::ContextSpecificRef {
274                        tag_number: #tag_number,
275                        tag_mode: #tag_mode,
276                        value: #binding,
277                    }.encode_value(encoder)
278                }
279            }
280
281            None => self
282                .asn1_type
283                .map(|ty| {
284                    let encoder_obj = ty.encoder(binding);
285                    quote!(#encoder_obj.encode_value(encoder))
286                })
287                .unwrap_or_else(|| quote!(#binding.encode_value(encoder))),
288        }
289    }
290}
291
292/// Name/value pair attribute.
293pub(crate) struct AttrNameValue {
294    /// Attribute name.
295    pub name: Path,
296
297    /// Attribute value.
298    pub value: LitStr,
299}
300
301impl Parse for AttrNameValue {
302    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
303        let name = match input.parse() {
304            Ok(name) => name,
305            // If it doesn't parse as a path, check if it's the keyword `type`
306            // The asn1 macro uses this even though Path cannot technically contain
307            // non-identifiers, so it needs to be forced in.
308            Err(e) => {
309                if let Ok(tok) = input.parse::<Token![type]>() {
310                    Path::from(Ident::new("type", tok.span))
311                } else {
312                    // If it still doesn't parse, report the original error rather than the
313                    // one produced by the workaround.
314                    return Err(e);
315                }
316            }
317        };
318        input.parse::<Token![=]>()?;
319        let value = input.parse()?;
320        Ok(Self { name, value })
321    }
322}
323
324impl AttrNameValue {
325    pub fn parse_attribute(attr: &Attribute) -> syn::Result<impl IntoIterator<Item = Self>> {
326        attr.parse_args_with(Punctuated::<Self, Token![,]>::parse_terminated)
327    }
328
329    /// Parse a slice of attributes.
330    pub fn from_attributes(attrs: &[Attribute], out: &mut Vec<Self>) -> syn::Result<()> {
331        for attr in attrs {
332            if !attr.path().is_ident(ATTR_NAME) {
333                continue;
334            }
335
336            match Self::parse_attribute(attr) {
337                Ok(parsed) => out.extend(parsed),
338                Err(e) => abort!(attr, e),
339            }
340        }
341
342        Ok(())
343    }
344
345    /// Parse an attribute value if the name matches the specified one.
346    pub fn parse_value<T>(&self, name: &str) -> syn::Result<Option<T>>
347    where
348        T: FromStr + Debug,
349        T::Err: Debug,
350    {
351        Ok(if self.name.is_ident(name) {
352            Some(
353                self.value
354                    .value()
355                    .parse()
356                    .map_err(|_| syn::Error::new_spanned(&self.name, "error parsing attribute"))?,
357            )
358        } else {
359            None
360        })
361    }
362}