From 11caeadfef6c1ac72e3cf80c824315c320acddec Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Thu, 14 Nov 2024 12:26:13 +0100 Subject: [PATCH] Add `ToStatic` custom derive attribute --- derive/src/lib.rs | 6 +++ derive/src/tostatic.rs | 99 ++++++++++++++++++++++++++++++++++++++++++ src/derive.rs | 31 +++++++++++++ src/traits.rs | 16 +++++++ 4 files changed, 152 insertions(+) create mode 100644 derive/src/tostatic.rs diff --git a/derive/src/lib.rs b/derive/src/lib.rs index c75cf37..b2a486a 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -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, @@ -62,3 +64,7 @@ synstructure::decl_derive!([DerSet, attributes( error, map_err )] => derive_der_set); + +synstructure::decl_derive!([ToStatic, attributes( + debug_derive +)] => derive_tostatic); diff --git a/derive/src/tostatic.rs b/derive/src/tostatic.rs new file mode 100644 index 0000000..d99e60e --- /dev/null +++ b/derive/src/tostatic.rs @@ -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,)* + ) + } + } + } +} diff --git a/src/derive.rs b/src/derive.rs index faf862e..eae33a8 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -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; diff --git a/src/traits.rs b/src/traits.rs index 1f8a3da..cddbd53 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -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")] @@ -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) -> ::Owned { + Cow::Owned(self.to_string()) + } +} + +impl ToStatic for Cow<'_, [u8]> { + type Owned = Cow<'static, [u8]>; + fn to_static(&self) -> ::Owned { + Cow::Owned(self.to_vec()) + } +}