aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2023-12-19 15:48:02 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-19 15:48:02 +0000
commitdaa2e0d9f3758b265ff76c4961aeef509a1667fc (patch)
treed7a51a0948b277f6fb2bdf69a6d20f7523902fb5
parentd6e853ae9d87d4c84836abfda7aba754a48c6ecd (diff)
parente2b1a5ad0b4e3eff6cc889c59afaee40a274ea0c (diff)
downloadder_derive-master.tar.gz
Upgrade der_derive to 0.7.2 am: b6cbded0a1 am: d83c21456d am: e2b1a5ad0bHEADmastermain
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/der_derive/+/2878114 Change-Id: Iac03c5646325bad1ebd87a266e6804cdaadeadbe Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp5
-rw-r--r--CHANGELOG.md20
-rw-r--r--Cargo.toml9
-rw-r--r--Cargo.toml.orig7
-rw-r--r--METADATA21
-rw-r--r--README.md8
-rw-r--r--cargo2rulesmk.json1
-rw-r--r--cargo_embargo.json3
-rw-r--r--patches/rules.mk.diff12
-rw-r--r--rules.mk5
-rw-r--r--src/attributes.rs151
-rw-r--r--src/choice.rs22
-rw-r--r--src/choice/variant.rs29
-rw-r--r--src/enumerated.rs72
-rw-r--r--src/lib.rs39
-rw-r--r--src/sequence.rs119
-rw-r--r--src/sequence/field.rs36
-rw-r--r--src/tag.rs9
-rw-r--r--src/value_ord.rs40
20 files changed, 328 insertions, 282 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index b80855a..833570a 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "1dccbc1893d5bae33959e0ccb496b9ce5a7de122"
+ "sha1": "e9a38ae2a0eb0244e69548e611196d1cf03cc1a1"
},
"path_in_vcs": "der/derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index ba5621e..3ff6d78 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,14 +35,13 @@ rust_proc_macro {
name: "libder_derive",
crate_name: "der_derive",
cargo_env_compat: true,
- cargo_pkg_version: "0.6.1",
+ cargo_pkg_version: "0.7.2",
srcs: ["src/lib.rs"],
edition: "2021",
rustlibs: [
"libproc_macro2",
- "libproc_macro_error",
"libquote",
- "libsyn_deprecated",
+ "libsyn",
],
compile_multilib: "first",
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4a9502..194f7af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## 0.7.2 (2023-08-07)
+### Changed
+- fix doc typo and use a valid tag number ([#1184])
+- remove proc-macro-error dependency ([#1180])
+
+[#1180]: https://github.com/RustCrypto/formats/pull/1180
+[#1184]: https://github.com/RustCrypto/formats/pull/1184
+
+## 0.7.1 (2023-04-19)
+### Added
+ - Support for type generics in `Sequence` macro ([#1014])
+
+[#1014]: https://github.com/RustCrypto/formats/pull/1014
+
+## 0.7.0 (2023-02-26)
+### Changed
+- Eliminate dynamism from encoding ([#828])
+
+[#828]: https://github.com/RustCrypto/formats/pull/828
+
## 0.6.1 (2022-12-05)
### Added
- Support for deriving `ValueOrd` on `Choice` enums ([#723])
diff --git a/Cargo.toml b/Cargo.toml
index d40159e..3d86ca3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.56"
+rust-version = "1.65"
name = "der_derive"
-version = "0.6.1"
+version = "0.7.2"
authors = ["RustCrypto Developers"]
description = "Custom derive support for the `der` crate's `Choice` and `Sequence` traits"
documentation = "https://docs.rs/der"
@@ -38,9 +38,6 @@ repository = "https://github.com/RustCrypto/formats/tree/master/der/derive"
[lib]
proc-macro = true
-[dependencies.proc-macro-error]
-version = "1"
-
[dependencies.proc-macro2]
version = "1"
@@ -48,5 +45,5 @@ version = "1"
version = "1"
[dependencies.syn]
-version = "1.0.58"
+version = "2"
features = ["extra-traits"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 9360572..cced55d 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "der_derive"
-version = "0.6.1"
+version = "0.7.2"
description = "Custom derive support for the `der` crate's `Choice` and `Sequence` traits"
authors = ["RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
@@ -10,13 +10,12 @@ categories = ["cryptography", "data-structures", "encoding", "no-std", "parser-i
keywords = ["asn1", "der", "crypto", "itu", "pkcs"]
readme = "README.md"
edition = "2021"
-rust-version = "1.56"
+rust-version = "1.65"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
-proc-macro-error = "1"
quote = "1"
-syn = { version = "1.0.58", features = ["extra-traits"] }
+syn = { version = "2", features = ["extra-traits"] }
diff --git a/METADATA b/METADATA
index 2b628e7..0958102 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# This project was upgraded with external_updater.
# Usage: tools/external_updater/updater.sh update rust/crates/der_derive
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "der_derive"
description: "Custom derive support for the `der` crate\'s `Choice` and `Sequence` traits"
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/der_derive"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/der_derive/der_derive-0.6.1.crate"
- }
- version: "0.6.1"
license_type: NOTICE
last_upgrade_date {
- year: 2022
+ year: 2023
month: 12
- day: 9
+ day: 15
+ }
+ homepage: "https://crates.io/crates/der_derive"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/der_derive/der_derive-0.7.2.crate"
+ version: "0.7.2"
}
}
diff --git a/README.md b/README.md
index 904b124..606310a 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,15 @@
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
-Custom derive support for the `der` crate's `Choice` and `Sequence` traits.
+Custom derive support for the `der` crate's `Choice` and `Sequence` traits:
+
+<https://github.com/RustCrypto/formats/tree/master/der>
[Documentation][docs-link]
## Minimum Supported Rust Version
-This crate requires **Rust 1.56** at a minimum.
+This crate requires **Rust 1.65** at a minimum.
We may change the MSRV in the future, but it will be accompanied by a minor
version bump.
@@ -42,7 +44,7 @@ dual licensed as above, without any additional terms or conditions.
[build-image]: https://github.com/RustCrypto/formats/actions/workflows/der.yml/badge.svg
[build-link]: https://github.com/RustCrypto/formats/actions/workflows/der.yml
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
-[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
+[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats
diff --git a/cargo2rulesmk.json b/cargo2rulesmk.json
index 01f7467..2c63c08 100644
--- a/cargo2rulesmk.json
+++ b/cargo2rulesmk.json
@@ -1,3 +1,2 @@
{
- "patch": "patches/rules.mk.diff"
}
diff --git a/cargo_embargo.json b/cargo_embargo.json
index 5e1c824..5c68873 100644
--- a/cargo_embargo.json
+++ b/cargo_embargo.json
@@ -1,7 +1,4 @@
{
- "module_name_overrides": {
- "libsyn": "libsyn_deprecated"
- },
"package": {
"der_derive": {
"device_supported": false,
diff --git a/patches/rules.mk.diff b/patches/rules.mk.diff
deleted file mode 100644
index e3bbe54..0000000
--- a/patches/rules.mk.diff
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git b/rules.mk a/rules.mk
-index 60a751a..7369647 100644
---- b/rules.mk
-+++ a/rules.mk
-@@ -13,6 +13,6 @@ MODULE_LIBRARY_DEPS := \
- external/rust/crates/proc-macro-error \
- external/rust/crates/proc-macro2 \
- external/rust/crates/quote \
-- external/rust/crates/syn \
-+ external/rust/crates/syn/1.0.107 \
-
- include make/library.mk
diff --git a/rules.mk b/rules.mk
index f9daabd..ec3d49c 100644
--- a/rules.mk
+++ b/rules.mk
@@ -1,4 +1,4 @@
-# This file is generated by cargo2rulesmk.py --run --config cargo2rulesmk.json --features .
+# This file is generated by cargo2rulesmk.py --run --config cargo2rulesmk.json.
# Do not modify this file as changes will be overridden on upgrade.
LOCAL_DIR := $(GET_LOCAL_DIR)
@@ -10,9 +10,8 @@ MODULE_SRCS := \
MODULE_RUST_EDITION := 2021
MODULE_LIBRARY_DEPS := \
- external/rust/crates/proc-macro-error \
external/rust/crates/proc-macro2 \
external/rust/crates/quote \
- external/rust/crates/syn/1.0.107 \
+ external/rust/crates/syn \
include make/library.mk
diff --git a/src/attributes.rs b/src/attributes.rs
index c765e19..fa050cb 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -1,18 +1,15 @@
//! 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::{Attribute, Lit, LitStr, Meta, MetaList, MetaNameValue, NestedMeta, Path};
+use syn::punctuated::Punctuated;
+use syn::{parse::Parse, parse::ParseStream, Attribute, Ident, LitStr, Path, Token};
/// Attribute name.
pub(crate) const ATTR_NAME: &str = "asn1";
-/// Parsing error message.
-const PARSE_ERR_MSG: &str = "error parsing `asn1` attribute";
-
/// Attributes on a `struct` or `enum` type.
#[derive(Clone, Debug, Default)]
pub(crate) struct TypeAttrs {
@@ -25,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(),
- }
+ })
}
}
@@ -92,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;
@@ -102,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);
@@ -165,7 +168,7 @@ impl FieldAttrs {
}
}
- Self {
+ Ok(Self {
asn1_type,
context_specific,
default,
@@ -173,20 +176,23 @@ impl FieldAttrs {
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`",
+ )),
},
}
}
@@ -284,7 +290,7 @@ impl FieldAttrs {
}
/// Name/value pair attribute.
-struct AttrNameValue {
+pub(crate) struct AttrNameValue {
/// Attribute name.
pub name: Path,
@@ -292,50 +298,65 @@ struct AttrNameValue {
pub value: LitStr,
}
+impl Parse for AttrNameValue {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let name = match input.parse() {
+ Ok(name) => name,
+ // If it doesn't parse as a path, check if it's the keyword `type`
+ // The asn1 macro uses this even though Path cannot technically contain
+ // non-identifiers, so it needs to be forced in.
+ Err(e) => {
+ if let Ok(tok) = input.parse::<Token![type]>() {
+ Path::from(Ident::new("type", tok.span))
+ } else {
+ // If it still doesn't parse, report the original error rather than the
+ // one produced by the workaround.
+ return Err(e);
+ }
+ }
+ };
+ input.parse::<Token![=]>()?;
+ let value = input.parse()?;
+ Ok(Self { name, value })
+ }
+}
+
impl AttrNameValue {
+ pub fn parse_attribute(attr: &Attribute) -> syn::Result<impl IntoIterator<Item = Self>> {
+ attr.parse_args_with(Punctuated::<Self, Token![,]>::parse_terminated)
+ }
+
/// 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) {
+ if !attr.path().is_ident(ATTR_NAME) {
continue;
}
- let nested = match attr.parse_meta().expect(PARSE_ERR_MSG) {
- Meta::List(MetaList { nested, .. }) => nested,
- other => abort!(other, "malformed `asn1` attribute"),
- };
-
- for meta in &nested {
- match meta {
- NestedMeta::Meta(Meta::NameValue(MetaNameValue {
- path,
- lit: Lit::Str(lit_str),
- ..
- })) => out.push(Self {
- name: path.clone(),
- value: lit_str.clone(),
- }),
- _ => abort!(nested, "malformed `asn1` attribute"),
- }
+ match Self::parse_attribute(attr) {
+ Ok(parsed) => out.extend(parsed),
+ 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
- }
+ })
}
}
diff --git a/src/choice.rs b/src/choice.rs
index ac0dc37..22ab5ea 100644
--- a/src/choice.rs
+++ b/src/choice.rs
@@ -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};
@@ -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!(
@@ -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`].
@@ -61,7 +60,10 @@ impl DeriveChoice {
let lifetime = match self.lifetime {
Some(ref lifetime) => quote!(#lifetime),
- None => default_lifetime(),
+ None => {
+ let lifetime = default_lifetime();
+ quote!(#lifetime)
+ }
};
// Lifetime parameters
@@ -108,7 +110,7 @@ impl DeriveChoice {
}
impl<#lt_params> ::der::EncodeValue for #ident<#lt_params> {
- fn encode_value(&self, encoder: &mut dyn ::der::Writer) -> ::der::Result<()> {
+ fn encode_value(&self, encoder: &mut impl ::der::Writer) -> ::der::Result<()> {
match self {
#(#encode_body)*
}
@@ -158,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);
@@ -198,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);
diff --git a/src/choice/variant.rs b/src/choice/variant.rs
index d74be61..c11ecbf 100644
--- a/src/choice/variant.rs
+++ b/src/choice/variant.rs
@@ -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};
@@ -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",
+ ))
}
}
@@ -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");
@@ -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<'_>>`.
diff --git a/src/enumerated.rs b/src/enumerated.rs
index 64db64a..6bdd5ff 100644
--- a/src/enumerated.rs
+++ b/src/enumerated.rs
@@ -2,11 +2,11 @@
//! the purposes of decoding/encoding ASN.1 `ENUMERATED` types as mapped to
//! enum variants.
+use crate::attributes::AttrNameValue;
use crate::{default_lifetime, ATTR_NAME};
use proc_macro2::TokenStream;
-use proc_macro_error::abort;
use quote::quote;
-use syn::{DeriveInput, Expr, ExprLit, Ident, Lit, LitInt, Meta, MetaList, NestedMeta, Variant};
+use syn::{DeriveInput, Expr, ExprLit, Ident, Lit, LitInt, Variant};
/// Valid options for the `#[repr]` attribute on `Enumerated` types.
const REPR_TYPES: &[&str] = &["u8", "u16", "u32"];
@@ -28,7 +28,7 @@ pub(crate) struct DeriveEnumerated {
impl DeriveEnumerated {
/// 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!(
@@ -42,23 +42,21 @@ impl DeriveEnumerated {
let mut integer = false;
for attr in &input.attrs {
- if attr.path.is_ident(ATTR_NAME) {
- if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
- for meta in nested {
- if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
- if nv.path.is_ident("type") {
- if let Lit::Str(lit) = nv.lit {
- match lit.value().as_str() {
- "ENUMERATED" => integer = false,
- "INTEGER" => integer = true,
- s => abort!(lit, "`type = \"{}\"` is unsupported", s),
- }
- }
- }
+ if attr.path().is_ident(ATTR_NAME) {
+ let kvs = match AttrNameValue::parse_attribute(attr) {
+ Ok(kvs) => kvs,
+ Err(e) => abort!(attr, e),
+ };
+ for anv in kvs {
+ if anv.name.is_ident("type") {
+ match anv.value.value().as_str() {
+ "ENUMERATED" => integer = false,
+ "INTEGER" => integer = true,
+ s => abort!(anv.value, format_args!("`type = \"{s}\"` is unsupported")),
}
}
}
- } else if attr.path.is_ident("repr") {
+ } else if attr.path().is_ident("repr") {
if repr.is_some() {
abort!(
attr,
@@ -66,16 +64,15 @@ impl DeriveEnumerated {
);
}
- let r = attr
- .parse_args::<Ident>()
- .unwrap_or_else(|_| abort!(attr, "error parsing `#[repr]` attribute"));
+ let r = attr.parse_args::<Ident>().map_err(|_| {
+ syn::Error::new_spanned(attr, "error parsing `#[repr]` attribute")
+ })?;
// Validate
if !REPR_TYPES.contains(&r.to_string().as_str()) {
abort!(
attr,
- "invalid `#[repr]` type: allowed types are {:?}",
- REPR_TYPES
+ format_args!("invalid `#[repr]` type: allowed types are {REPR_TYPES:?}"),
);
}
@@ -84,20 +81,23 @@ impl DeriveEnumerated {
}
// Parse enum variants
- let variants = data.variants.iter().map(EnumeratedVariant::new).collect();
+ let variants = data
+ .variants
+ .iter()
+ .map(EnumeratedVariant::new)
+ .collect::<syn::Result<_>>()?;
- Self {
+ Ok(Self {
ident: input.ident.clone(),
- repr: repr.unwrap_or_else(|| {
- abort!(
+ repr: repr.ok_or_else(|| {
+ syn::Error::new_spanned(
&input.ident,
- "no `#[repr]` attribute on enum: must be one of {:?}",
- REPR_TYPES
+ format_args!("no `#[repr]` attribute on enum: must be one of {REPR_TYPES:?}"),
)
- }),
+ })?,
variants,
integer,
- }
+ })
}
/// Lower the derived output into a [`TokenStream`].
@@ -130,7 +130,7 @@ impl DeriveEnumerated {
::der::EncodeValue::value_len(&(*self as #repr))
}
- fn encode_value(&self, encoder: &mut dyn ::der::Writer) -> ::der::Result<()> {
+ fn encode_value(&self, encoder: &mut impl ::der::Writer) -> ::der::Result<()> {
::der::EncodeValue::encode_value(&(*self as #repr), encoder)
}
}
@@ -164,9 +164,9 @@ pub struct EnumeratedVariant {
impl EnumeratedVariant {
/// Create a new [`ChoiceVariant`] from the input [`Variant`].
- fn new(input: &Variant) -> Self {
+ fn new(input: &Variant) -> syn::Result<Self> {
for attr in &input.attrs {
- if attr.path.is_ident(ATTR_NAME) {
+ if attr.path().is_ident(ATTR_NAME) {
abort!(
attr,
"`asn1` attribute is not allowed on fields of `Enumerated` types"
@@ -181,10 +181,10 @@ impl EnumeratedVariant {
lit: Lit::Int(discriminant),
..
}),
- )) => Self {
+ )) => Ok(Self {
ident: input.ident.clone(),
discriminant: discriminant.clone(),
- },
+ }),
Some((_, other)) => abort!(other, "invalid discriminant for `Enumerated`"),
None => abort!(input, "`Enumerated` variant has no discriminant"),
}
@@ -224,7 +224,7 @@ mod tests {
}
};
- let ir = DeriveEnumerated::new(input);
+ let ir = DeriveEnumerated::new(input).unwrap();
assert_eq!(ir.ident, "CrlReason");
assert_eq!(ir.repr, "u32");
assert_eq!(ir.variants.len(), 10);
diff --git a/src/lib.rs b/src/lib.rs
index b7aef1d..87409f2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -55,7 +55,7 @@
//! This attribute can be added to associate a particular `CONTEXT-SPECIFIC`
//! tag number with a given enum variant or struct field.
//!
-//! The value must be quoted and contain a number, e.g. `#[asn1(context_specific = "42"]`.
+//! The value must be quoted and contain a number, e.g. `#[asn1(context_specific = "29")]`.
//!
//! ### `#[asn1(default = "...")]` attribute: `DEFAULT` support
//!
@@ -121,6 +121,12 @@
unused_qualifications
)]
+macro_rules! abort {
+ ( $tokens:expr, $message:expr $(,)? ) => {
+ return Err(syn::Error::new_spanned($tokens, $message))
+ };
+}
+
mod asn1_type;
mod attributes;
mod choice;
@@ -140,14 +146,11 @@ use crate::{
};
use proc_macro::TokenStream;
use proc_macro2::Span;
-use proc_macro_error::proc_macro_error;
-use quote::quote;
use syn::{parse_macro_input, DeriveInput, Lifetime};
/// Get the default lifetime.
-fn default_lifetime() -> proc_macro2::TokenStream {
- let lifetime = Lifetime::new("'__der_lifetime", Span::call_site());
- quote!(#lifetime)
+fn default_lifetime() -> Lifetime {
+ Lifetime::new("'__der_lifetime", Span::call_site())
}
/// Derive the [`Choice`][1] trait on an `enum`.
@@ -188,10 +191,12 @@ fn default_lifetime() -> proc_macro2::TokenStream {
/// [3]: https://docs.rs/der/latest/der/trait.Encode.html
/// [4]: https://docs.rs/der_derive/
#[proc_macro_derive(Choice, attributes(asn1))]
-#[proc_macro_error]
pub fn derive_choice(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
- DeriveChoice::new(input).to_tokens().into()
+ match DeriveChoice::new(input) {
+ Ok(t) => t.to_tokens().into(),
+ Err(e) => e.to_compile_error().into(),
+ }
}
/// Derive decoders and encoders for ASN.1 [`Enumerated`] types on a
@@ -224,10 +229,12 @@ pub fn derive_choice(input: TokenStream) -> TokenStream {
/// Note that the derive macro will write a `TryFrom<...>` impl for the
/// provided `#[repr]`, which is used by the decoder.
#[proc_macro_derive(Enumerated, attributes(asn1))]
-#[proc_macro_error]
pub fn derive_enumerated(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
- DeriveEnumerated::new(input).to_tokens().into()
+ match DeriveEnumerated::new(input) {
+ Ok(t) => t.to_tokens().into(),
+ Err(e) => e.to_compile_error().into(),
+ }
}
/// Derive the [`Sequence`][1] trait on a `struct`.
@@ -264,10 +271,12 @@ pub fn derive_enumerated(input: TokenStream) -> TokenStream {
/// [1]: https://docs.rs/der/latest/der/trait.Sequence.html
/// [2]: https://docs.rs/der_derive/
#[proc_macro_derive(Sequence, attributes(asn1))]
-#[proc_macro_error]
pub fn derive_sequence(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
- DeriveSequence::new(input).to_tokens().into()
+ match DeriveSequence::new(input) {
+ Ok(t) => t.to_tokens().into(),
+ Err(e) => e.to_compile_error().into(),
+ }
}
/// Derive the [`ValueOrd`][1] trait on a `struct`.
@@ -277,8 +286,10 @@ pub fn derive_sequence(input: TokenStream) -> TokenStream {
///
/// [1]: https://docs.rs/der/latest/der/trait.ValueOrd.html
#[proc_macro_derive(ValueOrd, attributes(asn1))]
-#[proc_macro_error]
pub fn derive_value_ord(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
- DeriveValueOrd::new(input).to_tokens().into()
+ match DeriveValueOrd::new(input) {
+ Ok(t) => t.to_tokens().into(),
+ Err(e) => e.to_compile_error().into(),
+ }
}
diff --git a/src/sequence.rs b/src/sequence.rs
index 3e47246..e4ad9dd 100644
--- a/src/sequence.rs
+++ b/src/sequence.rs
@@ -6,17 +6,16 @@ mod field;
use crate::{default_lifetime, TypeAttrs};
use field::SequenceField;
use proc_macro2::TokenStream;
-use proc_macro_error::abort;
use quote::quote;
-use syn::{DeriveInput, Ident, Lifetime};
+use syn::{DeriveInput, GenericParam, Generics, Ident, LifetimeParam};
/// Derive the `Sequence` trait for a struct
pub(crate) struct DeriveSequence {
/// Name of the sequence struct.
ident: Ident,
- /// Lifetime of the struct.
- lifetime: Option<Lifetime>,
+ /// Generics of the struct.
+ generics: Generics,
/// Fields of the struct.
fields: Vec<SequenceField>,
@@ -24,7 +23,7 @@ pub(crate) struct DeriveSequence {
impl DeriveSequence {
/// Parse [`DeriveInput`].
- pub fn new(input: DeriveInput) -> Self {
+ pub fn new(input: DeriveInput) -> syn::Result<Self> {
let data = match input.data {
syn::Data::Struct(data) => data,
_ => abort!(
@@ -33,57 +32,60 @@ impl DeriveSequence {
),
};
- // TODO(tarcieri): properly handle multiple lifetimes
- let lifetime = input
- .generics
- .lifetimes()
- .next()
- .map(|lt| lt.lifetime.clone());
-
- let type_attrs = TypeAttrs::parse(&input.attrs);
+ let type_attrs = TypeAttrs::parse(&input.attrs)?;
let fields = data
.fields
.iter()
.map(|field| SequenceField::new(field, &type_attrs))
- .collect();
+ .collect::<syn::Result<_>>()?;
- Self {
+ Ok(Self {
ident: input.ident,
- lifetime,
+ generics: input.generics.clone(),
fields,
- }
+ })
}
/// Lower the derived output into a [`TokenStream`].
pub fn to_tokens(&self) -> TokenStream {
let ident = &self.ident;
+ let mut generics = self.generics.clone();
- let lifetime = match self.lifetime {
- Some(ref lifetime) => quote!(#lifetime),
- None => default_lifetime(),
- };
-
- // Lifetime parameters
- // TODO(tarcieri): support multiple lifetimes
- let lt_params = self
- .lifetime
- .as_ref()
- .map(|_| lifetime.clone())
- .unwrap_or_default();
+ // Use the first lifetime parameter as lifetime for Decode/Encode lifetime
+ // if none found, add one.
+ let lifetime = generics
+ .lifetimes()
+ .next()
+ .map(|lt| lt.lifetime.clone())
+ .unwrap_or_else(|| {
+ let lt = default_lifetime();
+ generics
+ .params
+ .insert(0, GenericParam::Lifetime(LifetimeParam::new(lt.clone())));
+ lt
+ });
+
+ // We may or may not have inserted a lifetime.
+ let (_, ty_generics, where_clause) = self.generics.split_for_impl();
+ let (impl_generics, _, _) = generics.split_for_impl();
let mut decode_body = Vec::new();
let mut decode_result = Vec::new();
- let mut encode_body = Vec::new();
+ let mut encoded_lengths = Vec::new();
+ let mut encode_fields = Vec::new();
for field in &self.fields {
decode_body.push(field.to_decode_tokens());
decode_result.push(&field.ident);
- encode_body.push(field.to_encode_tokens());
+
+ let field = field.to_encode_tokens();
+ encoded_lengths.push(quote!(#field.encoded_len()?));
+ encode_fields.push(quote!(#field.encode(writer)?;));
}
quote! {
- impl<#lifetime> ::der::DecodeValue<#lifetime> for #ident<#lt_params> {
+ impl #impl_generics ::der::DecodeValue<#lifetime> for #ident #ty_generics #where_clause {
fn decode_value<R: ::der::Reader<#lifetime>>(
reader: &mut R,
header: ::der::Header,
@@ -100,16 +102,25 @@ impl DeriveSequence {
}
}
- impl<#lifetime> ::der::Sequence<#lifetime> for #ident<#lt_params> {
- fn fields<F, T>(&self, f: F) -> ::der::Result<T>
- where
- F: FnOnce(&[&dyn der::Encode]) -> ::der::Result<T>,
- {
- f(&[
- #(#encode_body),*
- ])
+ impl #impl_generics ::der::EncodeValue for #ident #ty_generics #where_clause {
+ fn value_len(&self) -> ::der::Result<::der::Length> {
+ use ::der::Encode as _;
+
+ [
+ #(#encoded_lengths),*
+ ]
+ .into_iter()
+ .try_fold(::der::Length::ZERO, |acc, len| acc + len)
+ }
+
+ fn encode_value(&self, writer: &mut impl ::der::Writer) -> ::der::Result<()> {
+ use ::der::Encode as _;
+ #(#encode_fields)*
+ Ok(())
}
}
+
+ impl #impl_generics ::der::Sequence<#lifetime> for #ident #ty_generics #where_clause {}
}
}
}
@@ -131,9 +142,12 @@ mod tests {
}
};
- let ir = DeriveSequence::new(input);
+ let ir = DeriveSequence::new(input).unwrap();
assert_eq!(ir.ident, "AlgorithmIdentifier");
- assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
+ assert_eq!(
+ ir.generics.lifetimes().next().unwrap().lifetime.to_string(),
+ "'a"
+ );
assert_eq!(ir.fields.len(), 2);
let algorithm_field = &ir.fields[0];
@@ -162,9 +176,12 @@ mod tests {
}
};
- let ir = DeriveSequence::new(input);
+ let ir = DeriveSequence::new(input).unwrap();
assert_eq!(ir.ident, "SubjectPublicKeyInfo");
- assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
+ assert_eq!(
+ ir.generics.lifetimes().next().unwrap().lifetime.to_string(),
+ "'a"
+ );
assert_eq!(ir.fields.len(), 2);
let algorithm_field = &ir.fields[0];
@@ -227,9 +244,12 @@ mod tests {
}
};
- let ir = DeriveSequence::new(input);
+ let ir = DeriveSequence::new(input).unwrap();
assert_eq!(ir.ident, "OneAsymmetricKey");
- assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
+ assert_eq!(
+ ir.generics.lifetimes().next().unwrap().lifetime.to_string(),
+ "'a"
+ );
assert_eq!(ir.fields.len(), 5);
let version_field = &ir.fields[0];
@@ -299,9 +319,12 @@ mod tests {
}
};
- let ir = DeriveSequence::new(input);
+ let ir = DeriveSequence::new(input).unwrap();
assert_eq!(ir.ident, "ImplicitSequence");
- assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
+ assert_eq!(
+ ir.generics.lifetimes().next().unwrap().lifetime.to_string(),
+ "'a"
+ );
assert_eq!(ir.fields.len(), 3);
let bit_string = &ir.fields[0];
diff --git a/src/sequence/field.rs b/src/sequence/field.rs
index 4f478f0..3fb1832 100644
--- a/src/sequence/field.rs
+++ b/src/sequence/field.rs
@@ -2,7 +2,6 @@
use crate::{Asn1Type, FieldAttrs, TagMode, TagNumber, TypeAttrs};
use proc_macro2::TokenStream;
-use proc_macro_error::abort;
use quote::quote;
use syn::{Field, Ident, Path, Type};
@@ -20,35 +19,35 @@ pub(super) struct SequenceField {
impl SequenceField {
/// Create a new [`SequenceField`] from the input [`Field`].
- pub(super) fn new(field: &Field, type_attrs: &TypeAttrs) -> Self {
- let ident = field.ident.as_ref().cloned().unwrap_or_else(|| {
- abort!(
+ pub(super) fn new(field: &Field, type_attrs: &TypeAttrs) -> syn::Result<Self> {
+ let ident = field.ident.as_ref().cloned().ok_or_else(|| {
+ syn::Error::new_spanned(
field,
- "no name on struct field i.e. tuple structs unsupported"
+ "no name on struct field i.e. tuple structs unsupported",
)
- });
+ })?;
- let attrs = FieldAttrs::parse(&field.attrs, type_attrs);
+ let attrs = FieldAttrs::parse(&field.attrs, type_attrs)?;
if attrs.asn1_type.is_some() && attrs.default.is_some() {
- abort!(
+ return Err(syn::Error::new_spanned(
ident,
- "ASN.1 `type` and `default` options cannot be combined"
- );
+ "ASN.1 `type` and `default` options cannot be combined",
+ ));
}
if attrs.default.is_some() && attrs.optional {
- abort!(
+ return Err(syn::Error::new_spanned(
ident,
- "`optional` and `default` field qualifiers are mutually exclusive"
- );
+ "`optional` and `default` field qualifiers are mutually exclusive",
+ ));
}
- Self {
+ Ok(Self {
ident,
attrs,
field_type: field.ty.clone(),
- }
+ })
}
/// Derive code for decoding a field of a sequence.
@@ -167,8 +166,7 @@ impl LowerFieldEncoder {
/// the field encoder to tokens.
fn into_tokens(self) -> TokenStream {
- let encoder = self.encoder;
- quote! { &#encoder }
+ self.encoder
}
/// Apply the ASN.1 type (if defined).
@@ -295,7 +293,7 @@ mod tests {
assert_eq!(
field.to_encode_tokens().to_string(),
quote! {
- &self.example_field
+ self.example_field
}
.to_string()
);
@@ -346,7 +344,7 @@ mod tests {
assert_eq!(
field.to_encode_tokens().to_string(),
quote! {
- &::der::asn1::ContextSpecificRef {
+ ::der::asn1::ContextSpecificRef {
tag_number: ::der::TagNumber::N0,
tag_mode: ::der::TagMode::Implicit,
value: &self.implicit_field,
diff --git a/src/tag.rs b/src/tag.rs
index f2e39ec..aab2899 100644
--- a/src/tag.rs
+++ b/src/tag.rs
@@ -53,11 +53,12 @@ impl Tag {
}
/// Tagging modes: `EXPLICIT` versus `IMPLICIT`.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) enum TagMode {
/// `EXPLICIT` tagging.
///
/// Tag is added in addition to the inner tag of the type.
+ #[default]
Explicit,
/// `IMPLICIT` tagging.
@@ -89,12 +90,6 @@ impl FromStr for TagMode {
}
}
-impl Default for TagMode {
- fn default() -> TagMode {
- TagMode::Explicit
- }
-}
-
impl Display for TagMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
diff --git a/src/value_ord.rs b/src/value_ord.rs
index 97e37ca..0ba8ddf 100644
--- a/src/value_ord.rs
+++ b/src/value_ord.rs
@@ -7,7 +7,6 @@
use crate::{FieldAttrs, TypeAttrs};
use proc_macro2::TokenStream;
-use proc_macro_error::abort;
use quote::quote;
use syn::{DeriveInput, Field, Ident, Lifetime, Variant};
@@ -28,9 +27,9 @@ pub(crate) struct DeriveValueOrd {
impl DeriveValueOrd {
/// Parse [`DeriveInput`].
- pub fn new(input: DeriveInput) -> Self {
+ pub fn new(input: DeriveInput) -> syn::Result<Self> {
let ident = input.ident;
- let type_attrs = TypeAttrs::parse(&input.attrs);
+ let type_attrs = TypeAttrs::parse(&input.attrs)?;
// TODO(tarcieri): properly handle multiple lifetimes
let lifetime = input
@@ -44,14 +43,14 @@ impl DeriveValueOrd {
data.variants
.into_iter()
.map(|variant| ValueField::new_enum(variant, &type_attrs))
- .collect(),
+ .collect::<syn::Result<_>>()?,
InputType::Enum,
),
syn::Data::Struct(data) => (
data.fields
.into_iter()
.map(|field| ValueField::new_struct(field, &type_attrs))
- .collect(),
+ .collect::<syn::Result<_>>()?,
InputType::Struct,
),
_ => abort!(
@@ -61,12 +60,12 @@ impl DeriveValueOrd {
),
};
- Self {
+ Ok(Self {
ident,
lifetime,
fields,
input_type,
- }
+ })
}
/// Lower the derived output into a [`TokenStream`].
@@ -142,31 +141,30 @@ struct ValueField {
impl ValueField {
/// Create from an `enum` variant.
- fn new_enum(variant: Variant, type_attrs: &TypeAttrs) -> Self {
+ fn new_enum(variant: Variant, type_attrs: &TypeAttrs) -> syn::Result<Self> {
let ident = variant.ident;
- let attrs = FieldAttrs::parse(&variant.attrs, type_attrs);
- Self {
+ let attrs = FieldAttrs::parse(&variant.attrs, type_attrs)?;
+ Ok(Self {
ident,
attrs,
is_enum: true,
- }
+ })
}
/// Create from a `struct` field.
- fn new_struct(field: Field, type_attrs: &TypeAttrs) -> Self {
- let ident = field
- .ident
- .as_ref()
- .cloned()
- .unwrap_or_else(|| abort!(&field, "tuple structs are not supported"));
-
- let attrs = FieldAttrs::parse(&field.attrs, type_attrs);
- Self {
+ fn new_struct(field: Field, type_attrs: &TypeAttrs) -> syn::Result<Self> {
+ let ident =
+ field.ident.as_ref().cloned().ok_or_else(|| {
+ syn::Error::new_spanned(&field, "tuple structs are not supported")
+ })?;
+
+ let attrs = FieldAttrs::parse(&field.attrs, type_attrs)?;
+ Ok(Self {
ident,
attrs,
is_enum: false,
- }
+ })
}
/// Lower to [`TokenStream`].