Skip to content

Commit

Permalink
Add ToStatic custom derive attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
chifflier committed Nov 14, 2024
1 parent adcd623 commit 11caead
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 0 deletions.
6 changes: 6 additions & 0 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ mod alias;
mod container;
mod sequence;
mod set;
mod tostatic;
use alias::*;
use sequence::*;
use set::*;
use tostatic::derive_tostatic;

synstructure::decl_derive!([BerAlias, attributes(
debug_derive,
Expand Down Expand Up @@ -62,3 +64,7 @@ synstructure::decl_derive!([DerSet, attributes(
error,
map_err
)] => derive_der_set);

synstructure::decl_derive!([ToStatic, attributes(
debug_derive
)] => derive_tostatic);
99 changes: 99 additions & 0 deletions derive/src/tostatic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use proc_macro2::Span;
use quote::quote;
use syn::{Data, DeriveInput, FieldsNamed, FieldsUnnamed, Ident, LitInt};

pub fn derive_tostatic(s: synstructure::Structure) -> proc_macro2::TokenStream {
let ast = s.ast();

let debug_derive = ast.attrs.iter().any(|attr| {
attr.meta
.path()
.is_ident(&Ident::new("debug_derive", Span::call_site()))
});

let ds = match &ast.data {
Data::Struct(ds) => ds,
_ => panic!("Unsupported type, cannot derive"),
};

let ts = match &ds.fields {
syn::Fields::Unit => derive_unit_struct(ast),
syn::Fields::Named(fields) => derive_named_struct(fields, ast),
syn::Fields::Unnamed(fields) => derive_unnamed_struct(fields, ast),
};

let ts = s.gen_impl(ts);

if debug_derive {
eprintln!("{ts}");
}
ts
}

fn derive_unit_struct(ast: &DeriveInput) -> proc_macro2::TokenStream {
let struct_ident = &ast.ident;

quote! {
gen impl asn1_rs::ToStatic for @Self {
type Owned = #struct_ident;

fn to_static(&self) -> Self::Owned {
#struct_ident
}
}
}
}

fn derive_named_struct(fields: &FieldsNamed, ast: &DeriveInput) -> proc_macro2::TokenStream {
let fields: Vec<_> = fields.named.iter().collect();

let field_idents: Vec<_> = fields.iter().map(|f| f.ident.clone()).collect();

let field_instrs = field_idents.iter().map(|ident| {
quote! { let #ident = self.#ident.to_static(); }
});

let struct_ident = &ast.ident;

quote! {
gen impl asn1_rs::ToStatic for @Self {
type Owned = #struct_ident<'static>;

fn to_static(&self) -> Self::Owned {
#(#field_instrs)*
#struct_ident{
#(#field_idents,)*
}
}
}
}
}

fn derive_unnamed_struct(fields: &FieldsUnnamed, ast: &DeriveInput) -> proc_macro2::TokenStream {
// TODO: if unnamed, check that there is only one lifetime in ast.generics
let fields: Vec<_> = fields.unnamed.iter().collect();

let field_idents: Vec<_> = (0..fields.len())
.map(|idx| Ident::new(&format!("_{idx}"), Span::call_site()))
.collect();

let field_instrs = field_idents.iter().enumerate().map(|(idx, ident)| {
let idx = LitInt::new(&format!("{idx}"), Span::call_site());
quote! { let #ident = self.#idx.to_static(); }
});

let struct_ident = &ast.ident;

quote! {
gen impl asn1_rs::ToStatic for @Self {
type Owned = #struct_ident<'static>;

fn to_static(&self) -> Self::Owned {
#(#field_instrs)*
#struct_ident(
#(#field_idents,)*
)
}
}
}
}
31 changes: 31 additions & 0 deletions src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,34 @@ pub use asn1_rs_derive::BerAlias;
/// struct S(pub u32);
/// ```
pub use asn1_rs_derive::DerAlias;

/// # ToStatic custom derive
///
/// `ToStatic` is a custom derive attribute, to derive the [`ToStatic`](ToStatic) trait automatically from the structure definition.
///
/// ## Example
///
/// ```rust
/// use asn1_rs::ToStatic;
/// use std::borrow::Cow;
///
/// #[derive(ToStatic)]
/// struct S<'a>(pub Cow<'a, str>);
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::ToStatic;
/// use std::borrow::Cow;
///
/// #[derive(ToStatic)]
/// #[debug_derive]
/// struct S<'a>(pub Cow<'a, str>);
/// ```
pub use asn1_rs_derive::ToStatic;
16 changes: 16 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::debug::{trace, trace_generic};
use crate::error::*;
use crate::{parse_der_any, Any, Class, Explicit, Implicit, Tag, TaggedParser};
use alloc::borrow::Cow;
use alloc::string::ToString;
use core::convert::{TryFrom, TryInto};
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
Expand Down Expand Up @@ -355,3 +357,17 @@ pub trait ToStatic {
type Owned: 'static;
fn to_static(&self) -> Self::Owned;
}

impl ToStatic for Cow<'_, str> {
type Owned = Cow<'static, str>;
fn to_static(&self) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_string())
}
}

impl ToStatic for Cow<'_, [u8]> {
type Owned = Cow<'static, [u8]>;
fn to_static(&self) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_vec())
}
}

0 comments on commit 11caead

Please sign in to comment.