use crate::{FieldAttrs, TypeAttrs};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Field, Generics, Ident, Variant};
pub(crate) struct DeriveValueOrd {
ident: Ident,
generics: Generics,
fields: Vec<ValueField>,
input_type: InputType,
}
impl DeriveValueOrd {
pub fn new(input: DeriveInput) -> syn::Result<Self> {
let ident = input.ident;
let type_attrs = TypeAttrs::parse(&input.attrs)?;
let (fields, input_type) = match input.data {
syn::Data::Enum(data) => (
data.variants
.into_iter()
.map(|variant| ValueField::new_enum(variant, &type_attrs))
.collect::<syn::Result<_>>()?,
InputType::Enum,
),
syn::Data::Struct(data) => (
data.fields
.into_iter()
.map(|field| ValueField::new_struct(field, &type_attrs))
.collect::<syn::Result<_>>()?,
InputType::Struct,
),
_ => abort!(
ident,
"can't derive `ValueOrd` on this type: \
only `enum` and `struct` types are allowed",
),
};
Ok(Self {
ident,
generics: input.generics.clone(),
fields,
input_type,
})
}
pub fn to_tokens(&self) -> TokenStream {
let ident = &self.ident;
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let mut body = Vec::new();
for field in &self.fields {
body.push(field.to_tokens());
}
let body = match self.input_type {
InputType::Enum => {
quote! {
#[allow(unused_imports)]
use ::der::ValueOrd;
match (self, other) {
#(#body)*
_ => unreachable!(),
}
}
}
InputType::Struct => {
quote! {
#[allow(unused_imports)]
use ::der::{DerOrd, ValueOrd};
#(#body)*
Ok(::core::cmp::Ordering::Equal)
}
}
};
quote! {
impl #impl_generics ::der::ValueOrd for #ident #ty_generics #where_clause {
fn value_cmp(&self, other: &Self) -> ::der::Result<::core::cmp::Ordering> {
#body
}
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum InputType {
Enum,
Struct,
}
struct ValueField {
ident: Ident,
attrs: FieldAttrs,
is_enum: bool,
}
impl ValueField {
fn new_enum(variant: Variant, type_attrs: &TypeAttrs) -> syn::Result<Self> {
let ident = variant.ident;
let attrs = FieldAttrs::parse(&variant.attrs, type_attrs)?;
Ok(Self {
ident,
attrs,
is_enum: true,
})
}
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,
})
}
fn to_tokens(&self) -> TokenStream {
let ident = &self.ident;
if self.is_enum {
let binding1 = quote!(Self::#ident(this));
let binding2 = quote!(Self::#ident(other));
quote! {
(#binding1, #binding2) => this.value_cmp(other),
}
} else {
let mut binding1 = quote!(self.#ident);
let mut binding2 = quote!(other.#ident);
if let Some(ty) = &self.attrs.asn1_type {
binding1 = ty.encoder(&binding1);
binding2 = ty.encoder(&binding2);
}
quote! {
match #binding1.der_cmp(&#binding2)? {
::core::cmp::Ordering::Equal => (),
other => return Ok(other),
}
}
}
}
}