Skip to content

Commit

Permalink
der_derive: remove proc-macro-error dependency (#1180)
Browse files Browse the repository at this point in the history
  • Loading branch information
jplatte authored Aug 5, 2023
1 parent 5171277 commit c512452
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 168 deletions.
25 changes: 0 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion der/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ proc-macro = true

[dependencies]
proc-macro2 = "1"
proc-macro-error = "1"
quote = "1"
syn = { version = "2", features = ["extra-traits"] }
98 changes: 54 additions & 44 deletions der/derive/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! Attribute-related types used by the proc macro

use crate::{Asn1Type, Tag, TagMode, TagNumber};
use proc_macro2::TokenStream;
use proc_macro_error::{abort, abort_call_site};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::{fmt::Debug, str::FromStr};
use syn::punctuated::Punctuated;
Expand All @@ -23,31 +22,34 @@ pub(crate) struct TypeAttrs {

impl TypeAttrs {
/// Parse attributes from a struct field or enum variant.
pub fn parse(attrs: &[Attribute]) -> Self {
pub fn parse(attrs: &[Attribute]) -> syn::Result<Self> {
let mut tag_mode = None;

let mut parsed_attrs = Vec::new();
AttrNameValue::from_attributes(attrs, &mut parsed_attrs);
AttrNameValue::from_attributes(attrs, &mut parsed_attrs)?;

for attr in parsed_attrs {
// `tag_mode = "..."` attribute
if let Some(mode) = attr.parse_value("tag_mode") {
if tag_mode.is_some() {
abort!(attr.name, "duplicate ASN.1 `tag_mode` attribute");
}

tag_mode = Some(mode);
} else {
abort!(
attr.name,
let mode = attr.parse_value("tag_mode")?.ok_or_else(|| {
syn::Error::new_spanned(
&attr.name,
"invalid `asn1` attribute (valid options are `tag_mode`)",
);
)
})?;

if tag_mode.is_some() {
return Err(syn::Error::new_spanned(
&attr.name,
"duplicate ASN.1 `tag_mode` attribute",
));
}

tag_mode = Some(mode);
}

Self {
Ok(Self {
tag_mode: tag_mode.unwrap_or_default(),
}
})
}
}

Expand Down Expand Up @@ -90,7 +92,7 @@ impl FieldAttrs {
}

/// Parse attributes from a struct field or enum variant.
pub fn parse(attrs: &[Attribute], type_attrs: &TypeAttrs) -> Self {
pub fn parse(attrs: &[Attribute], type_attrs: &TypeAttrs) -> syn::Result<Self> {
let mut asn1_type = None;
let mut context_specific = None;
let mut default = None;
Expand All @@ -100,57 +102,60 @@ impl FieldAttrs {
let mut constructed = None;

let mut parsed_attrs = Vec::new();
AttrNameValue::from_attributes(attrs, &mut parsed_attrs);
AttrNameValue::from_attributes(attrs, &mut parsed_attrs)?;

for attr in parsed_attrs {
// `context_specific = "..."` attribute
if let Some(tag_number) = attr.parse_value("context_specific") {
if let Some(tag_number) = attr.parse_value("context_specific")? {
if context_specific.is_some() {
abort!(attr.name, "duplicate ASN.1 `context_specific` attribute");
}

context_specific = Some(tag_number);
// `default` attribute
} else if attr.parse_value::<String>("default").is_some() {
} else if attr.parse_value::<String>("default")?.is_some() {
if default.is_some() {
abort!(attr.name, "duplicate ASN.1 `default` attribute");
}

default = Some(attr.value.parse().unwrap_or_else(|e| {
abort!(attr.value, "error parsing ASN.1 `default` attribute: {}", e)
}));
default = Some(attr.value.parse().map_err(|e| {
syn::Error::new_spanned(
attr.value,
format_args!("error parsing ASN.1 `default` attribute: {e}"),
)
})?);
// `extensible` attribute
} else if let Some(ext) = attr.parse_value("extensible") {
} else if let Some(ext) = attr.parse_value("extensible")? {
if extensible.is_some() {
abort!(attr.name, "duplicate ASN.1 `extensible` attribute");
}

extensible = Some(ext);
// `optional` attribute
} else if let Some(opt) = attr.parse_value("optional") {
} else if let Some(opt) = attr.parse_value("optional")? {
if optional.is_some() {
abort!(attr.name, "duplicate ASN.1 `optional` attribute");
}

optional = Some(opt);
// `tag_mode` attribute
} else if let Some(mode) = attr.parse_value("tag_mode") {
} else if let Some(mode) = attr.parse_value("tag_mode")? {
if tag_mode.is_some() {
abort!(attr.name, "duplicate ASN.1 `tag_mode` attribute");
}

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

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

constructed = Some(ty);
Expand All @@ -163,28 +168,31 @@ impl FieldAttrs {
}
}

Self {
Ok(Self {
asn1_type,
context_specific,
default,
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> {
pub fn tag(&self) -> syn::Result<Option<Tag>> {
match self.context_specific {
Some(tag_number) => Some(Tag::ContextSpecific {
Some(tag_number) => Ok(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`"),
TagMode::Explicit => Ok(self.asn1_type.map(Tag::Universal)),
TagMode::Implicit => Err(syn::Error::new(
Span::call_site(),
"implicit tagging requires a `tag_number`",
)),
},
}
}
Expand Down Expand Up @@ -319,34 +327,36 @@ impl AttrNameValue {
}

/// Parse a slice of attributes.
pub fn from_attributes(attrs: &[Attribute], out: &mut Vec<Self>) {
pub fn from_attributes(attrs: &[Attribute], out: &mut Vec<Self>) -> syn::Result<()> {
for attr in attrs {
if !attr.path().is_ident(ATTR_NAME) {
continue;
}

match Self::parse_attribute(attr) {
Ok(parsed) => out.extend(parsed),
Err(e) => abort!(attr, "{}", e),
};
Err(e) => abort!(attr, e),
}
}

Ok(())
}

/// Parse an attribute value if the name matches the specified one.
pub fn parse_value<T>(&self, name: &str) -> Option<T>
pub fn parse_value<T>(&self, name: &str) -> syn::Result<Option<T>>
where
T: FromStr + Debug,
T::Err: Debug,
{
if self.name.is_ident(name) {
Ok(if self.name.is_ident(name) {
Some(
self.value
.value()
.parse()
.unwrap_or_else(|_| abort!(self.name, "error parsing attribute")),
.map_err(|_| syn::Error::new_spanned(&self.name, "error parsing attribute"))?,
)
} else {
None
}
})
}
}
15 changes: 7 additions & 8 deletions der/derive/src/choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ mod variant;
use self::variant::ChoiceVariant;
use crate::{default_lifetime, TypeAttrs};
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use quote::quote;
use syn::{DeriveInput, Ident, Lifetime};

Expand All @@ -25,7 +24,7 @@ pub(crate) struct DeriveChoice {

impl DeriveChoice {
/// Parse [`DeriveInput`].
pub fn new(input: DeriveInput) -> Self {
pub fn new(input: DeriveInput) -> syn::Result<Self> {
let data = match input.data {
syn::Data::Enum(data) => data,
_ => abort!(
Expand All @@ -41,18 +40,18 @@ impl DeriveChoice {
.next()
.map(|lt| lt.lifetime.clone());

let type_attrs = TypeAttrs::parse(&input.attrs);
let type_attrs = TypeAttrs::parse(&input.attrs)?;
let variants = data
.variants
.iter()
.map(|variant| ChoiceVariant::new(variant, &type_attrs))
.collect();
.collect::<syn::Result<_>>()?;

Self {
Ok(Self {
ident: input.ident,
lifetime,
variants,
}
})
}

/// Lower the derived output into a [`TokenStream`].
Expand Down Expand Up @@ -161,7 +160,7 @@ mod tests {
}
};

let ir = DeriveChoice::new(input);
let ir = DeriveChoice::new(input).unwrap();
assert_eq!(ir.ident, "Time");
assert_eq!(ir.lifetime, None);
assert_eq!(ir.variants.len(), 2);
Expand Down Expand Up @@ -201,7 +200,7 @@ mod tests {
}
};

let ir = DeriveChoice::new(input);
let ir = DeriveChoice::new(input).unwrap();
assert_eq!(ir.ident, "ImplicitChoice");
assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
assert_eq!(ir.variants.len(), 3);
Expand Down
29 changes: 15 additions & 14 deletions der/derive/src/choice/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use crate::{FieldAttrs, Tag, TypeAttrs};
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use quote::quote;
use syn::{Fields, Ident, Path, Type, Variant};

Expand Down Expand Up @@ -33,20 +32,22 @@ impl From<Path> for TagOrPath {
}
}

impl From<&Variant> for TagOrPath {
fn from(input: &Variant) -> Self {
impl TryFrom<&Variant> for TagOrPath {
type Error = syn::Error;

fn try_from(input: &Variant) -> syn::Result<Self> {
if let Fields::Unnamed(fields) = &input.fields {
if fields.unnamed.len() == 1 {
if let Type::Path(path) = &fields.unnamed[0].ty {
return path.path.clone().into();
return Ok(path.path.clone().into());
}
}
}

abort!(
Err(syn::Error::new_spanned(
&input.ident,
"no #[asn1(type=...)] specified for enum variant"
)
"no #[asn1(type=...)] specified for enum variant",
))
}
}

Expand All @@ -73,9 +74,9 @@ pub(super) struct ChoiceVariant {

impl ChoiceVariant {
/// Create a new [`ChoiceVariant`] from the input [`Variant`].
pub(super) fn new(input: &Variant, type_attrs: &TypeAttrs) -> Self {
pub(super) fn new(input: &Variant, type_attrs: &TypeAttrs) -> syn::Result<Self> {
let ident = input.ident.clone();
let attrs = FieldAttrs::parse(&input.attrs, type_attrs);
let attrs = FieldAttrs::parse(&input.attrs, type_attrs)?;

if attrs.extensible {
abort!(&ident, "`extensible` is not allowed on CHOICE");
Expand All @@ -88,12 +89,12 @@ impl ChoiceVariant {
_ => abort!(&ident, "enum variant must be a 1-element tuple struct"),
}

let tag = attrs
.tag()
.map(TagOrPath::from)
.unwrap_or_else(|| TagOrPath::from(input));
let tag = match attrs.tag()? {
Some(x) => x.into(),
None => input.try_into()?,
};

Self { ident, attrs, tag }
Ok(Self { ident, attrs, tag })
}

/// Derive a match arm of the impl body for `TryFrom<der::asn1::Any<'_>>`.
Expand Down
Loading

0 comments on commit c512452

Please sign in to comment.