Skip to content

Commit

Permalink
der-derive: adds generics support for ValueOrd and Choice
Browse files Browse the repository at this point in the history
  • Loading branch information
baloo committed May 12, 2024
1 parent cde2991 commit 46ad187
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 51 deletions.
63 changes: 31 additions & 32 deletions der/derive/src/choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ use self::variant::ChoiceVariant;
use crate::{default_lifetime, TypeAttrs};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Ident, Lifetime};
use syn::{DeriveInput, GenericParam, Generics, Ident, LifetimeParam};

/// Derive the `Choice` trait for an enum.
pub(crate) struct DeriveChoice {
/// Name of the enum type.
ident: Ident,

/// Lifetime of the type.
lifetime: Option<Lifetime>,
/// Generics of the enum.
generics: Generics,

/// Variants of this `Choice`.
variants: Vec<ChoiceVariant>,
Expand All @@ -33,13 +33,6 @@ impl DeriveChoice {
),
};

// 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 variants = data
.variants
Expand All @@ -49,30 +42,33 @@ impl DeriveChoice {

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

/// 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 => {
let lifetime = default_lifetime();
quote!(#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 can_decode_body = Vec::new();
let mut decode_body = Vec::new();
Expand All @@ -89,13 +85,13 @@ impl DeriveChoice {
}

quote! {
impl<#lifetime> ::der::Choice<#lifetime> for #ident<#lt_params> {
impl #impl_generics ::der::Choice<#lifetime> for #ident #ty_generics #where_clause {
fn can_decode(tag: ::der::Tag) -> bool {
matches!(tag, #(#can_decode_body)|*)
}
}

impl<#lifetime> ::der::Decode<#lifetime> for #ident<#lt_params> {
impl #impl_generics ::der::Decode<#lifetime> for #ident #ty_generics #where_clause {
type Error = ::der::Error;

fn decode<R: ::der::Reader<#lifetime>>(reader: &mut R) -> ::der::Result<Self> {
Expand All @@ -111,7 +107,7 @@ impl DeriveChoice {
}
}

impl<#lt_params> ::der::EncodeValue for #ident<#lt_params> {
impl #impl_generics ::der::EncodeValue for #ident #ty_generics #where_clause {
fn encode_value(&self, encoder: &mut impl ::der::Writer) -> ::der::Result<()> {
match self {
#(#encode_body)*
Expand All @@ -125,7 +121,7 @@ impl DeriveChoice {
}
}

impl<#lt_params> ::der::Tagged for #ident<#lt_params> {
impl #impl_generics ::der::Tagged for #ident #ty_generics #where_clause {
fn tag(&self) -> ::der::Tag {
match self {
#(#tagged_body)*
Expand Down Expand Up @@ -165,7 +161,7 @@ mod tests {

let ir = DeriveChoice::new(input).unwrap();
assert_eq!(ir.ident, "Time");
assert_eq!(ir.lifetime, None);
assert_eq!(ir.generics.lifetimes().next(), None);
assert_eq!(ir.variants.len(), 2);

let utc_time = &ir.variants[0];
Expand Down Expand Up @@ -205,7 +201,10 @@ mod tests {

let ir = DeriveChoice::new(input).unwrap();
assert_eq!(ir.ident, "ImplicitChoice");
assert_eq!(ir.lifetime.unwrap().to_string(), "'a");
assert_eq!(
ir.generics.lifetimes().next().unwrap().lifetime.to_string(),
"'a"
);
assert_eq!(ir.variants.len(), 3);

let bit_string = &ir.variants[0];
Expand Down
25 changes: 6 additions & 19 deletions der/derive/src/value_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
use crate::{FieldAttrs, TypeAttrs};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Field, Ident, Lifetime, Variant};
use syn::{DeriveInput, Field, Generics, Ident, Variant};

/// Derive the `Enumerated` trait for an enum.
pub(crate) struct DeriveValueOrd {
/// Name of the enum.
ident: Ident,

/// Lifetime of the struct.
lifetime: Option<Lifetime>,
/// Generics of the enum.
generics: Generics,

/// Fields of structs or enum variants.
fields: Vec<ValueField>,
Expand All @@ -31,13 +31,6 @@ impl DeriveValueOrd {
let ident = input.ident;
let type_attrs = TypeAttrs::parse(&input.attrs)?;

// TODO(tarcieri): properly handle multiple lifetimes
let lifetime = input
.generics
.lifetimes()
.next()
.map(|lt| lt.lifetime.clone());

let (fields, input_type) = match input.data {
syn::Data::Enum(data) => (
data.variants
Expand All @@ -62,7 +55,7 @@ impl DeriveValueOrd {

Ok(Self {
ident,
lifetime,
generics: input.generics.clone(),
fields,
input_type,
})
Expand All @@ -72,13 +65,7 @@ impl DeriveValueOrd {
pub fn to_tokens(&self) -> TokenStream {
let ident = &self.ident;

// Lifetime parameters
// TODO(tarcieri): support multiple lifetimes
let lt_params = self
.lifetime
.as_ref()
.map(|lt| vec![lt.clone()])
.unwrap_or_default();
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();

let mut body = Vec::new();

Expand Down Expand Up @@ -110,7 +97,7 @@ impl DeriveValueOrd {
};

quote! {
impl<#(#lt_params)*> ::der::ValueOrd for #ident<#(#lt_params)*> {
impl #impl_generics ::der::ValueOrd for #ident #ty_generics #where_clause {
fn value_cmp(&self, other: &Self) -> ::der::Result<::core::cmp::Ordering> {
#body
}
Expand Down

0 comments on commit 46ad187

Please sign in to comment.