summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2023-11-21 14:08:23 +0000
committerAndrew Walbran <qwandor@google.com>2023-12-07 15:24:05 +0000
commit4d229f7e8da066c8ef1024a11924338a39f25435 (patch)
tree4bb8b51b68ef03df1693cbb16ce28b0ea7797121 /src
parentcec6dd3b8982894c351622bf96ca2dca91c5476a (diff)
downloadasn1-rs-derive-4d229f7e8da066c8ef1024a11924338a39f25435.tar.gz
Import asn1-rs-derive crate.upstream
Request Document: go/android-rust-importing-crates For CL Reviewers: go/android3p#cl-review For Build Team: go/ab-third-party-imports Bug: 312436720 Test: Treehugger Change-Id: I44b17fd2de8eeae4ee43fda51177bad85c99e4bc
Diffstat (limited to 'src')
-rw-r--r--src/alias.rs61
-rw-r--r--src/container.rs488
-rw-r--r--src/lib.rs64
-rw-r--r--src/sequence.rs61
-rw-r--r--src/set.rs61
5 files changed, 735 insertions, 0 deletions
diff --git a/src/alias.rs b/src/alias.rs
new file mode 100644
index 0000000..82ca68e
--- /dev/null
+++ b/src/alias.rs
@@ -0,0 +1,61 @@
+use crate::container::*;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{Data, Ident};
+
+pub fn derive_ber_alias(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}
+
+pub fn derive_der_alias(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let impl_checkconstraints = container.gen_checkconstraints();
+ let impl_fromder = container.gen_fromder();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ #impl_checkconstraints
+ #impl_fromder
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}
diff --git a/src/container.rs b/src/container.rs
new file mode 100644
index 0000000..667992c
--- /dev/null
+++ b/src/container.rs
@@ -0,0 +1,488 @@
+use proc_macro2::{Literal, Span, TokenStream};
+use quote::{quote, ToTokens};
+use syn::{
+ parse::ParseStream, parse_quote, spanned::Spanned, Attribute, DataStruct, DeriveInput, Field,
+ Fields, Ident, Lifetime, LitInt, Meta, Type, WherePredicate,
+};
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ContainerType {
+ Alias,
+ Sequence,
+ Set,
+}
+
+impl ToTokens for ContainerType {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let s = match self {
+ ContainerType::Alias => quote! {},
+ ContainerType::Sequence => quote! { asn1_rs::Tag::Sequence },
+ ContainerType::Set => quote! { asn1_rs::Tag::Set },
+ };
+ s.to_tokens(tokens)
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Asn1Type {
+ Ber,
+ Der,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Asn1TagKind {
+ Explicit,
+ Implicit,
+}
+
+impl ToTokens for Asn1TagKind {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let s = match self {
+ Asn1TagKind::Explicit => quote! { asn1_rs::Explicit },
+ Asn1TagKind::Implicit => quote! { asn1_rs::Implicit },
+ };
+ s.to_tokens(tokens)
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Asn1TagClass {
+ Universal,
+ Application,
+ ContextSpecific,
+ Private,
+}
+
+impl ToTokens for Asn1TagClass {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let s = match self {
+ Asn1TagClass::Application => quote! { asn1_rs::Class::APPLICATION },
+ Asn1TagClass::ContextSpecific => quote! { asn1_rs::Class::CONTEXT_SPECIFIC },
+ Asn1TagClass::Private => quote! { asn1_rs::Class::PRIVATE },
+ Asn1TagClass::Universal => quote! { asn1_rs::Class::UNIVERSAL },
+ };
+ s.to_tokens(tokens)
+ }
+}
+
+pub struct Container {
+ pub container_type: ContainerType,
+ pub fields: Vec<FieldInfo>,
+ pub where_predicates: Vec<WherePredicate>,
+ pub error: Option<Attribute>,
+
+ is_any: bool,
+}
+
+impl Container {
+ pub fn from_datastruct(
+ ds: &DataStruct,
+ ast: &DeriveInput,
+ container_type: ContainerType,
+ ) -> Self {
+ let mut is_any = false;
+ match (container_type, &ds.fields) {
+ (ContainerType::Alias, Fields::Unnamed(f)) => {
+ if f.unnamed.len() != 1 {
+ panic!("Alias: only tuple fields with one element are supported");
+ }
+ match &f.unnamed[0].ty {
+ Type::Path(type_path)
+ if type_path
+ .clone()
+ .into_token_stream()
+ .to_string()
+ .starts_with("Any") =>
+ {
+ is_any = true;
+ }
+ _ => (),
+ }
+ }
+ (ContainerType::Alias, _) => panic!("BER/DER alias must be used with tuple strucs"),
+ (_, Fields::Unnamed(_)) => panic!("BER/DER sequence cannot be used on tuple structs"),
+ _ => (),
+ }
+
+ let fields = ds.fields.iter().map(FieldInfo::from).collect();
+
+ // get lifetimes from generics
+ let lfts: Vec<_> = ast.generics.lifetimes().collect();
+ let mut where_predicates = Vec::new();
+ if !lfts.is_empty() {
+ // input slice must outlive all lifetimes from Self
+ let lft = Lifetime::new("'ber", Span::call_site());
+ let wh: WherePredicate = parse_quote! { #lft: #(#lfts)+* };
+ where_predicates.push(wh);
+ };
+
+ // get custom attributes on container
+ let error = ast
+ .attrs
+ .iter()
+ .find(|attr| attr.path.is_ident(&Ident::new("error", Span::call_site())))
+ .cloned();
+
+ Container {
+ container_type,
+ fields,
+ where_predicates,
+ error,
+ is_any,
+ }
+ }
+
+ pub fn gen_tryfrom(&self) -> TokenStream {
+ let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
+ let parse_content =
+ derive_ber_sequence_content(&self.fields, Asn1Type::Ber, self.error.is_some());
+ let lifetime = Lifetime::new("'ber", Span::call_site());
+ let wh = &self.where_predicates;
+ let error = if let Some(attr) = &self.error {
+ get_attribute_meta(attr).expect("Invalid error attribute format")
+ } else {
+ quote! { asn1_rs::Error }
+ };
+
+ let fn_content = if self.container_type == ContainerType::Alias {
+ // special case: is this an alias for Any
+ if self.is_any {
+ quote! { Ok(Self(any)) }
+ } else {
+ quote! {
+ let res = TryFrom::try_from(any)?;
+ Ok(Self(res))
+ }
+ }
+ } else {
+ quote! {
+ use asn1_rs::nom::*;
+ any.tag().assert_eq(Self::TAG)?;
+
+ // no need to parse sequence, we already have content
+ let i = any.data;
+ //
+ #parse_content
+ //
+ let _ = i; // XXX check if empty?
+ Ok(Self{#(#field_names),*})
+ }
+ };
+ // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing
+ // the `where` statement if there are none.
+ quote! {
+ use asn1_rs::{Any, FromBer};
+ use core::convert::TryFrom;
+
+ gen impl<#lifetime> TryFrom<Any<#lifetime>> for @Self where #(#wh)+* {
+ type Error = #error;
+
+ fn try_from(any: Any<#lifetime>) -> asn1_rs::Result<Self, #error> {
+ #fn_content
+ }
+ }
+ }
+ }
+
+ pub fn gen_tagged(&self) -> TokenStream {
+ let tag = if self.container_type == ContainerType::Alias {
+ // special case: is this an alias for Any
+ if self.is_any {
+ return quote! {};
+ }
+ // find type of sub-item
+ let ty = &self.fields[0].type_;
+ quote! { <#ty as asn1_rs::Tagged>::TAG }
+ } else {
+ let container_type = self.container_type;
+ quote! { #container_type }
+ };
+ quote! {
+ gen impl<'ber> asn1_rs::Tagged for @Self {
+ const TAG: asn1_rs::Tag = #tag;
+ }
+ }
+ }
+
+ pub fn gen_checkconstraints(&self) -> TokenStream {
+ let lifetime = Lifetime::new("'ber", Span::call_site());
+ let wh = &self.where_predicates;
+ // let parse_content = derive_ber_sequence_content(&field_names, Asn1Type::Der);
+
+ let fn_content = if self.container_type == ContainerType::Alias {
+ // special case: is this an alias for Any
+ if self.is_any {
+ return quote! {};
+ }
+ let ty = &self.fields[0].type_;
+ quote! {
+ any.tag().assert_eq(Self::TAG)?;
+ <#ty>::check_constraints(any)
+ }
+ } else {
+ let check_fields: Vec<_> = self
+ .fields
+ .iter()
+ .map(|field| {
+ let ty = &field.type_;
+ quote! {
+ let (rem, any) = Any::from_der(rem)?;
+ <#ty as CheckDerConstraints>::check_constraints(&any)?;
+ }
+ })
+ .collect();
+ quote! {
+ any.tag().assert_eq(Self::TAG)?;
+ let rem = &any.data;
+ #(#check_fields)*
+ Ok(())
+ }
+ };
+
+ // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing
+ // the `where` statement if there are none.
+ quote! {
+ use asn1_rs::{CheckDerConstraints, Tagged};
+ gen impl<#lifetime> CheckDerConstraints for @Self where #(#wh)+* {
+ fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
+ #fn_content
+ }
+ }
+ }
+ }
+
+ pub fn gen_fromder(&self) -> TokenStream {
+ let lifetime = Lifetime::new("'ber", Span::call_site());
+ let wh = &self.where_predicates;
+ let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
+ let parse_content =
+ derive_ber_sequence_content(&self.fields, Asn1Type::Der, self.error.is_some());
+ let error = if let Some(attr) = &self.error {
+ get_attribute_meta(attr).expect("Invalid error attribute format")
+ } else {
+ quote! { asn1_rs::Error }
+ };
+
+ let fn_content = if self.container_type == ContainerType::Alias {
+ // special case: is this an alias for Any
+ if self.is_any {
+ quote! {
+ let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?;
+ Ok((rem,Self(any)))
+ }
+ } else {
+ quote! {
+ let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?;
+ any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?;
+ let res = TryFrom::try_from(any)?;
+ Ok((rem,Self(res)))
+ }
+ }
+ } else {
+ quote! {
+ let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?;
+ any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?;
+ let i = any.data;
+ //
+ #parse_content
+ //
+ // let _ = i; // XXX check if empty?
+ Ok((rem,Self{#(#field_names),*}))
+ }
+ };
+ // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing
+ // the `where` statement if there are none.
+ quote! {
+ use asn1_rs::FromDer;
+
+ gen impl<#lifetime> asn1_rs::FromDer<#lifetime, #error> for @Self where #(#wh)+* {
+ fn from_der(bytes: &#lifetime [u8]) -> asn1_rs::ParseResult<#lifetime, Self, #error> {
+ #fn_content
+ }
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct FieldInfo {
+ pub name: Ident,
+ pub type_: Type,
+ pub default: Option<TokenStream>,
+ pub optional: bool,
+ pub tag: Option<(Asn1TagKind, Asn1TagClass, u16)>,
+ pub map_err: Option<TokenStream>,
+}
+
+impl From<&Field> for FieldInfo {
+ fn from(field: &Field) -> Self {
+ // parse attributes and keep supported ones
+ let mut optional = false;
+ let mut tag = None;
+ let mut map_err = None;
+ let mut default = None;
+ let name = field
+ .ident
+ .as_ref()
+ .map_or_else(|| Ident::new("_", Span::call_site()), |s| s.clone());
+ for attr in &field.attrs {
+ let ident = match attr.path.get_ident() {
+ Some(ident) => ident.to_string(),
+ None => continue,
+ };
+ match ident.as_str() {
+ "map_err" => {
+ let expr: syn::Expr = attr.parse_args().expect("could not parse map_err");
+ map_err = Some(quote! { #expr });
+ }
+ "default" => {
+ let expr: syn::Expr = attr.parse_args().expect("could not parse default");
+ default = Some(quote! { #expr });
+ optional = true;
+ }
+ "optional" => optional = true,
+ "tag_explicit" => {
+ if tag.is_some() {
+ panic!("tag cannot be set twice!");
+ }
+ let (class, value) = attr.parse_args_with(parse_tag_args).unwrap();
+ tag = Some((Asn1TagKind::Explicit, class, value));
+ }
+ "tag_implicit" => {
+ if tag.is_some() {
+ panic!("tag cannot be set twice!");
+ }
+ let (class, value) = attr.parse_args_with(parse_tag_args).unwrap();
+ tag = Some((Asn1TagKind::Implicit, class, value));
+ }
+ // ignore unknown attributes
+ _ => (),
+ }
+ }
+ FieldInfo {
+ name,
+ type_: field.ty.clone(),
+ default,
+ optional,
+ tag,
+ map_err,
+ }
+ }
+}
+
+fn parse_tag_args(stream: ParseStream) -> Result<(Asn1TagClass, u16), syn::Error> {
+ let tag_class: Option<Ident> = stream.parse()?;
+ let tag_class = if let Some(ident) = tag_class {
+ let s = ident.to_string().to_uppercase();
+ match s.as_str() {
+ "UNIVERSAL" => Asn1TagClass::Universal,
+ "CONTEXT-SPECIFIC" => Asn1TagClass::ContextSpecific,
+ "APPLICATION" => Asn1TagClass::Application,
+ "PRIVATE" => Asn1TagClass::Private,
+ _ => {
+ return Err(syn::Error::new(stream.span(), "Invalid tag class"));
+ }
+ }
+ } else {
+ Asn1TagClass::ContextSpecific
+ };
+ let lit: LitInt = stream.parse()?;
+ let value = lit.base10_parse::<u16>()?;
+ Ok((tag_class, value))
+}
+
+fn derive_ber_sequence_content(
+ fields: &[FieldInfo],
+ asn1_type: Asn1Type,
+ custom_errors: bool,
+) -> TokenStream {
+ let field_parsers: Vec<_> = fields
+ .iter()
+ .map(|f| get_field_parser(f, asn1_type, custom_errors))
+ .collect();
+
+ quote! {
+ #(#field_parsers)*
+ }
+}
+
+fn get_field_parser(f: &FieldInfo, asn1_type: Asn1Type, custom_errors: bool) -> TokenStream {
+ let from = match asn1_type {
+ Asn1Type::Ber => quote! {FromBer::from_ber},
+ Asn1Type::Der => quote! {FromDer::from_der},
+ };
+ let name = &f.name;
+ let default = f
+ .default
+ .as_ref()
+ // use a type hint, otherwise compiler will not know what type provides .unwrap_or
+ .map(|x| quote! {let #name: Option<_> = #name; let #name = #name.unwrap_or(#x);});
+ let map_err = if let Some(tt) = f.map_err.as_ref() {
+ if asn1_type == Asn1Type::Ber {
+ Some(quote! { .finish().map_err(#tt) })
+ } else {
+ // Some(quote! { .map_err(|err| nom::Err::convert(#tt)) })
+ Some(quote! { .map_err(|err| err.map(#tt)) })
+ }
+ } else {
+ // add mapping functions only if custom errors are used
+ if custom_errors {
+ if asn1_type == Asn1Type::Ber {
+ Some(quote! { .finish() })
+ } else {
+ Some(quote! { .map_err(nom::Err::convert) })
+ }
+ } else {
+ None
+ }
+ };
+ if let Some((tag_kind, class, n)) = f.tag {
+ let tag = Literal::u16_unsuffixed(n);
+ // test if tagged + optional
+ if f.optional {
+ return quote! {
+ let (i, #name) = {
+ if i.is_empty() {
+ (i, None)
+ } else {
+ let (_, header): (_, asn1_rs::Header) = #from(i)#map_err?;
+ if header.tag().0 == #tag {
+ let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?;
+ (i, Some(t.into_inner()))
+ } else {
+ (i, None)
+ }
+ }
+ };
+ #default
+ };
+ } else {
+ // tagged, but not OPTIONAL
+ return quote! {
+ let (i, #name) = {
+ let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?;
+ (i, t.into_inner())
+ };
+ #default
+ };
+ }
+ } else {
+ // neither tagged nor optional
+ quote! {
+ let (i, #name) = #from(i)#map_err?;
+ #default
+ }
+ }
+}
+
+fn get_attribute_meta(attr: &Attribute) -> Result<TokenStream, syn::Error> {
+ if let Ok(Meta::List(meta)) = attr.parse_meta() {
+ let content = &meta.nested;
+ Ok(quote! { #content })
+ } else {
+ Err(syn::Error::new(
+ attr.span(),
+ "Invalid error attribute format",
+ ))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..c75cf37
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,64 @@
+mod alias;
+mod container;
+mod sequence;
+mod set;
+use alias::*;
+use sequence::*;
+use set::*;
+
+synstructure::decl_derive!([BerAlias, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_ber_alias);
+synstructure::decl_derive!([DerAlias, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_der_alias);
+
+synstructure::decl_derive!([BerSequence, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_ber_sequence);
+synstructure::decl_derive!([DerSequence, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_der_sequence);
+
+synstructure::decl_derive!([BerSet, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_ber_set);
+synstructure::decl_derive!([DerSet, attributes(
+ debug_derive,
+ default,
+ optional,
+ tag_explicit,
+ tag_implicit,
+ error,
+ map_err
+)] => derive_der_set);
diff --git a/src/sequence.rs b/src/sequence.rs
new file mode 100644
index 0000000..8a0d249
--- /dev/null
+++ b/src/sequence.rs
@@ -0,0 +1,61 @@
+use crate::container::*;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{Data, Ident};
+
+pub fn derive_ber_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}
+
+pub fn derive_der_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let impl_checkconstraints = container.gen_checkconstraints();
+ let impl_fromder = container.gen_fromder();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ #impl_checkconstraints
+ #impl_fromder
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}
diff --git a/src/set.rs b/src/set.rs
new file mode 100644
index 0000000..90ea04a
--- /dev/null
+++ b/src/set.rs
@@ -0,0 +1,61 @@
+use crate::container::*;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{Data, Ident};
+
+pub fn derive_ber_set(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}
+
+pub fn derive_der_set(s: synstructure::Structure) -> proc_macro2::TokenStream {
+ let ast = s.ast();
+
+ let container = match &ast.data {
+ Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set),
+ _ => panic!("Unsupported type, cannot derive"),
+ };
+
+ let debug_derive = ast.attrs.iter().any(|attr| {
+ attr.path
+ .is_ident(&Ident::new("debug_derive", Span::call_site()))
+ });
+ let impl_tryfrom = container.gen_tryfrom();
+ let impl_tagged = container.gen_tagged();
+ let impl_checkconstraints = container.gen_checkconstraints();
+ let impl_fromder = container.gen_fromder();
+ let ts = s.gen_impl(quote! {
+ extern crate asn1_rs;
+
+ #impl_tryfrom
+ #impl_tagged
+ #impl_checkconstraints
+ #impl_fromder
+ });
+ if debug_derive {
+ eprintln!("{}", ts);
+ }
+ ts
+}