pub mod clone;
mod common_ord;
pub mod copy;
pub mod debug;
pub mod default;
#[cfg(feature = "serde")]
pub mod deserialize;
pub mod eq;
pub mod hash;
pub mod ord;
pub mod partial_eq;
pub mod partial_ord;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "serde")]
pub mod serialize;
#[cfg(feature = "zeroize")]
pub mod zeroize;
#[cfg(feature = "zeroize")]
pub mod zeroize_on_drop;
use std::{borrow::Cow, ops::Deref};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Attribute, DeriveInput, Ident, ImplGenerics, Meta, Path, Result, Token, TraitBound,
TraitBoundModifier, TypeGenerics, TypeParamBound, WhereClause,
};
use crate::{util::MetaListExt, Data, DeriveWhere, Error, Item, SplitGenerics};
#[derive(Clone, Copy, Eq, PartialEq)]
#[cfg_attr(test, derive(Debug))]
pub enum Trait {
Clone,
Copy,
Debug,
Default,
#[cfg(feature = "serde")]
Deserialize,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
#[cfg(feature = "serde")]
Serialize,
#[cfg(feature = "zeroize")]
Zeroize,
#[cfg(feature = "zeroize")]
ZeroizeOnDrop,
}
macro_rules! trait_dispatch {
($self:expr, $method:ident($($par:expr),*)) => {
match $self {
Trait::Clone => clone::Clone::$method($($par),*),
Trait::Copy => copy::Copy::$method($($par),*),
Trait::Debug => debug::Debug::$method($($par),*),
Trait::Default => default::Default::$method($($par),*),
#[cfg(feature = "serde")]
Trait::Deserialize => deserialize::Deserialize::$method($($par),*),
Trait::Eq => eq::Eq::$method($($par),*),
Trait::Hash => hash::Hash::$method($($par),*),
Trait::Ord => ord::Ord::$method($($par),*),
Trait::PartialEq => partial_eq::PartialEq::$method($($par),*),
Trait::PartialOrd => partial_ord::PartialOrd::$method($($par),*),
#[cfg(feature = "serde")]
Trait::Serialize => serialize::Serialize::$method($($par),*),
#[cfg(feature = "zeroize")]
Trait::Zeroize => zeroize::Zeroize::$method($($par),*),
#[cfg(feature = "zeroize")]
Trait::ZeroizeOnDrop => zeroize_on_drop::ZeroizeOnDrop::$method($($par),*),
}
};
}
impl Trait {
pub fn from_path(path: &Path) -> Result<Self> {
if let Some(ident) = path.get_ident() {
use Trait::*;
match ident.to_string().as_str() {
"Clone" => Ok(Clone),
"Copy" => Ok(Copy),
"Debug" => Ok(Debug),
"Default" => Ok(Default),
#[cfg(feature = "serde")]
"Deserialize" => Ok(Deserialize),
#[cfg(not(feature = "serde"))]
"Deserialize" => Err(Error::serde_feature(path.span())),
"Eq" => Ok(Eq),
"Hash" => Ok(Hash),
"Ord" => Ok(Ord),
"PartialEq" => Ok(PartialEq),
"PartialOrd" => Ok(PartialOrd),
#[cfg(feature = "serde")]
"Serialize" => Ok(Serialize),
#[cfg(not(feature = "serde"))]
"Serialize" => Err(Error::serde_feature(path.span())),
#[cfg(feature = "zeroize")]
"Zeroize" => Ok(Zeroize),
#[cfg(not(feature = "zeroize"))]
"Zeroize" => Err(Error::zeroize_feature(path.span())),
#[cfg(feature = "zeroize")]
"ZeroizeOnDrop" => Ok(ZeroizeOnDrop),
#[cfg(not(feature = "zeroize"))]
"ZeroizeOnDrop" => Err(Error::zeroize_feature(path.span())),
"crate" => Err(Error::crate_(path.span())),
_ => Err(Error::trait_(path.span())),
}
} else {
Err(Error::trait_(path.span()))
}
}
pub fn as_str(&self) -> &'static str {
trait_dispatch!(self, as_str())
}
pub fn default_derive_trait(&self) -> DeriveTrait {
trait_dispatch!(self, default_derive_trait())
}
pub fn parse_derive_trait(
&self,
attrs: &[Attribute],
span: Span,
list: Option<Punctuated<Meta, Token![,]>>,
) -> Result<DeriveTrait> {
trait_dispatch!(self, parse_derive_trait(attrs, span, list))
}
pub fn supports_union(&self) -> bool {
trait_dispatch!(self, supports_union())
}
pub fn additional_where_bounds(&self, data: &Item) -> Option<TypeParamBound> {
trait_dispatch!(self, additional_where_bounds(data))
}
}
#[derive(Eq, PartialEq)]
pub enum DeriveTrait {
Clone,
Copy,
Debug,
Default,
#[cfg(feature = "serde")]
Deserialize(deserialize::Deserialize),
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
#[cfg(feature = "serde")]
Serialize(serialize::Serialize),
#[cfg(feature = "zeroize")]
Zeroize(zeroize::Zeroize),
#[cfg(feature = "zeroize")]
ZeroizeOnDrop(zeroize_on_drop::ZeroizeOnDrop),
}
impl Deref for DeriveTrait {
type Target = dyn TraitImpl;
fn deref(&self) -> &Self::Target {
use DeriveTrait::*;
match self {
Clone => &clone::Clone,
Copy => ©::Copy,
Debug => &debug::Debug,
Default => &default::Default,
#[cfg(feature = "serde")]
Deserialize(trait_) => trait_,
Eq => &eq::Eq,
Hash => &hash::Hash,
Ord => &ord::Ord,
PartialEq => &partial_eq::PartialEq,
PartialOrd => &partial_ord::PartialOrd,
#[cfg(feature = "serde")]
Serialize(trait_) => trait_,
#[cfg(feature = "zeroize")]
Zeroize(trait_) => trait_,
#[cfg(feature = "zeroize")]
ZeroizeOnDrop(trait_) => trait_,
}
}
}
impl PartialEq<Trait> for &DeriveTrait {
fn eq(&self, other: &Trait) -> bool {
let trait_: &Trait = self;
trait_ == other
}
}
impl DeriveTrait {
pub fn where_bounds(&self, data: &Item) -> Punctuated<TypeParamBound, Token![+]> {
let mut list = Punctuated::new();
list.push(TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: self.path(),
}));
if let Some(bound) = self.additional_where_bounds(data) {
list.push(bound)
}
list
}
pub fn from_stream(
attrs: &[Attribute],
span: Span,
data: &syn::Data,
input: ParseStream,
) -> Result<(Span, Self)> {
match Meta::parse(input) {
Ok(meta) => {
let trait_ = Trait::from_path(meta.path())?;
if let syn::Data::Union(_) = data {
if !trait_.supports_union() {
return Err(Error::union(span));
}
}
match &meta {
Meta::Path(path) => Ok((
path.span(),
trait_.parse_derive_trait(attrs, meta.span(), None)?,
)),
Meta::List(list) => {
let nested = list.parse_non_empty_nested_metas()?;
Ok((
list.span(),
trait_.parse_derive_trait(attrs, meta.span(), Some(nested))?,
))
}
Meta::NameValue(name_value) => Err(Error::option_syntax(name_value.span())),
}
}
Err(error) => Err(Error::trait_syntax(error.span())),
}
}
}
pub trait TraitImpl: Deref<Target = Trait> {
fn as_str() -> &'static str
where
Self: Sized;
fn default_derive_trait() -> DeriveTrait
where
Self: Sized;
fn parse_derive_trait(
_attrs: &[Attribute],
span: Span,
list: Option<Punctuated<Meta, Token![,]>>,
) -> Result<DeriveTrait>
where
Self: Sized,
{
if list.is_some() {
Err(Error::options(span, Self::as_str()))
} else {
Ok(Self::default_derive_trait())
}
}
fn supports_union() -> bool
where
Self: Sized,
{
false
}
fn additional_where_bounds(_data: &Item) -> Option<TypeParamBound>
where
Self: Sized,
{
None
}
fn path(&self) -> Path;
fn additional_impl(&self) -> Option<(Path, TokenStream)> {
None
}
#[allow(clippy::too_many_arguments)]
fn impl_item(
&self,
_crate_: Option<&Path>,
_full_item: &DeriveInput,
imp: &ImplGenerics<'_>,
ident: &Ident,
ty: &TypeGenerics<'_>,
where_clause: &Option<Cow<'_, WhereClause>>,
body: TokenStream,
) -> TokenStream {
let path = self.path();
quote! {
#[automatically_derived]
impl #imp #path for #ident #ty
#where_clause
{
#body
}
}
}
fn build_signature(
&self,
_derive_where: &DeriveWhere,
_item: &Item,
_generics: &SplitGenerics<'_>,
_body: &TokenStream,
) -> TokenStream {
TokenStream::new()
}
fn build_body(&self, _derive_where: &DeriveWhere, _data: &Data) -> TokenStream {
TokenStream::new()
}
}