Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions der/derive/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ pub(crate) struct FieldAttrs {
/// Inherits from the type-level tagging mode if specified, or otherwise
/// defaults to `EXPLICIT`.
pub tag_mode: TagMode,

/// Is the inner type constructed?
pub constructed: bool,
}

impl FieldAttrs {
Expand All @@ -97,6 +100,7 @@ impl FieldAttrs {
let mut extensible = None;
let mut optional = None;
let mut tag_mode = None;
let mut constructed = None;

let mut parsed_attrs = Vec::new();
AttrNameValue::from_attributes(attrs, &mut parsed_attrs);
Expand Down Expand Up @@ -146,6 +150,13 @@ impl FieldAttrs {
}

asn1_type = Some(ty);
// `constructed = "..."` attribute
} else if let Some(ty) = attr.parse_value("constructed") {
if constructed.is_some() {
abort!(attr.name, "duplicate ASN.1 `constructed` attribute: {}");
}

constructed = Some(ty);
} else {
abort!(
attr.name,
Expand All @@ -162,25 +173,22 @@ impl FieldAttrs {
extensible: extensible.unwrap_or_default(),
optional: optional.unwrap_or_default(),
tag_mode: tag_mode.unwrap_or(type_attrs.tag_mode),
constructed: constructed.unwrap_or_default(),
}
}

/// Get the expected [`Tag`] for this field.
pub fn tag(&self) -> Option<Tag> {
match self.tag_mode {
TagMode::Explicit => self.asn1_type.map(Tag::Universal),
TagMode::Implicit => self
.context_specific
.map(|tag_number| {
Some(Tag::ContextSpecific {
// TODO(tarcieri): handle constructed inner types
constructed: false,
number: tag_number,
})
})
.unwrap_or_else(|| {
abort_call_site!("implicit tagging requires an associated `tag_number`")
}),
match self.context_specific {
Some(tag_number) => Some(Tag::ContextSpecific {
constructed: self.constructed,
number: tag_number,
}),

None => match self.tag_mode {
TagMode::Explicit => self.asn1_type.map(Tag::Universal),
TagMode::Implicit => abort_call_site!("implicit tagging requires a `tag_number`"),
},
}
}

Expand Down Expand Up @@ -226,11 +234,12 @@ impl FieldAttrs {
}
} else {
// TODO(tarcieri): better error handling?
let constructed = self.constructed;
quote! {
#context_specific.ok_or_else(|| {
der::Tag::ContextSpecific {
number: #tag_number,
constructed: false
constructed: #constructed
}.value_error()
})?.value
}
Expand Down
5 changes: 5 additions & 0 deletions der/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
//! - `UTCTime`: performs an intermediate conversion to [`der::asn1::UtcTime`]
//! - `UTF8String`: performs an intermediate conversion to [`der::asn1::Utf8String`]
//!
//! ### `#[asn1(constructed = "...")]` attribute: support for constructed inner types
//!
//! This attribute can be used to specify that an "inner" type is constructed. It is most
//! commonly used when a `CHOICE` has a constructed inner type.
//!
//! Note: please open a GitHub Issue if you would like to request support
//! for additional ASN.1 types.
//!
Expand Down
2 changes: 2 additions & 0 deletions der/derive/src/sequence/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ mod tests {
extensible: false,
optional: false,
tag_mode: TagMode::Explicit,
constructed: false,
};

let field_type = Ident::new("String", span);
Expand Down Expand Up @@ -319,6 +320,7 @@ mod tests {
extensible: false,
optional: false,
tag_mode: TagMode::Implicit,
constructed: false,
};

let field_type = Ident::new("String", span);
Expand Down