chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/educe/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

6
vendor/educe/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "bc05181883f49d4fb475375891fc53b6747f0eb6"
},
"path_in_vcs": ""
}

102
vendor/educe/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,102 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.60"
name = "educe"
version = "0.6.0"
authors = ["Magic Len <len@magiclen.org>"]
include = [
"src/**/*",
"Cargo.toml",
"README.md",
"LICENSE",
]
description = "This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits."
homepage = "https://magiclen.org/educe"
readme = "README.md"
keywords = [
"derive",
"macro",
"trait",
"field",
"procedural",
]
categories = [
"no-std",
"rust-patterns",
]
license = "MIT"
repository = "https://github.com/magiclen/educe"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[lib]
proc-macro = true
[dependencies.enum-ordinalize]
version = "4.2"
features = ["derive"]
default-features = false
[dependencies.proc-macro2]
version = "1"
[dependencies.quote]
version = "1"
[dependencies.syn]
version = "2"
[dev-dependencies.assert-eq-float]
version = "0.1"
[dev-dependencies.rustversion]
version = "1"
[dev-dependencies.syn]
version = "2"
features = ["full"]
[features]
Clone = []
Copy = []
Debug = []
Default = []
Deref = []
DerefMut = []
Eq = []
Hash = []
Into = []
Ord = []
PartialEq = []
PartialOrd = []
default = [
"Debug",
"Clone",
"Copy",
"PartialEq",
"Eq",
"PartialOrd",
"Ord",
"Hash",
"Default",
"Deref",
"DerefMut",
"Into",
]
full = ["syn/full"]

21
vendor/educe/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 magiclen.org (Ron Li)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1689
vendor/educe/README.md vendored Normal file

File diff suppressed because it is too large Load Diff

57
vendor/educe/src/common/bound.rs vendored Normal file
View File

@@ -0,0 +1,57 @@
use syn::{punctuated::Punctuated, token::Comma, GenericParam, Meta, Path, Type, WherePredicate};
use crate::common::where_predicates_bool::{
create_where_predicates_from_all_generic_parameters,
create_where_predicates_from_generic_parameters_check_types, meta_2_where_predicates,
WherePredicates, WherePredicatesOrBool,
};
pub(crate) enum Bound {
Disabled,
Auto,
Custom(WherePredicates),
All,
}
impl Bound {
#[inline]
pub(crate) fn from_meta(meta: &Meta) -> syn::Result<Self> {
debug_assert!(meta.path().is_ident("bound"));
Ok(match meta_2_where_predicates(meta)? {
WherePredicatesOrBool::WherePredicates(where_predicates) => {
Self::Custom(where_predicates)
},
WherePredicatesOrBool::Bool(b) => {
if b {
Self::Auto
} else {
Self::Disabled
}
},
WherePredicatesOrBool::All => Self::All,
})
}
}
impl Bound {
#[inline]
pub(crate) fn into_where_predicates_by_generic_parameters_check_types(
self,
params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
types: &[&Type],
supertraits: &[proc_macro2::TokenStream],
) -> Punctuated<WherePredicate, Comma> {
match self {
Self::Disabled => Punctuated::new(),
Self::Auto => create_where_predicates_from_generic_parameters_check_types(
bound_trait,
types,
supertraits,
),
Self::Custom(where_predicates) => where_predicates,
Self::All => create_where_predicates_from_all_generic_parameters(params, bound_trait),
}
}
}

109
vendor/educe/src/common/expr.rs vendored Normal file
View File

@@ -0,0 +1,109 @@
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, Expr, Lit, Meta, Type};
use super::path::path_to_string;
const INT_TYPES: [&str; 12] =
["u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"];
const FLOAT_TYPES: [&str; 2] = ["f32", "f64"];
#[inline]
pub(crate) fn meta_2_expr(meta: &Meta) -> syn::Result<Expr> {
match &meta {
Meta::NameValue(name_value) => Ok(name_value.value.clone()),
Meta::List(list) => list.parse_args::<Expr>(),
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!("expected `{path} = Expr` or `{path}(Expr)`", path = path_to_string(path)),
)),
}
}
#[inline]
pub(crate) fn auto_adjust_expr(expr: Expr, ty: Option<&Type>) -> Expr {
match &expr {
Expr::Lit(lit) => {
match &lit.lit {
Lit::Int(lit) => {
if let Some(Type::Path(ty)) = ty {
let ty_string = ty.into_token_stream().to_string();
if lit.suffix() == ty_string || INT_TYPES.contains(&ty_string.as_str()) {
// don't call into
return expr;
}
}
},
Lit::Float(lit) => {
if let Some(Type::Path(ty)) = ty {
let ty_string = ty.into_token_stream().to_string();
if lit.suffix() == ty_string || FLOAT_TYPES.contains(&ty_string.as_str()) {
// don't call into
return expr;
}
}
},
Lit::Str(_) => {
if let Some(Type::Reference(ty)) = ty {
let ty_string = ty.elem.clone().into_token_stream().to_string();
if ty_string == "str" {
// don't call into
return expr;
}
}
},
Lit::Bool(_) => {
if let Some(Type::Path(ty)) = ty {
let ty_string = ty.into_token_stream().to_string();
if ty_string == "bool" {
// don't call into
return expr;
}
}
},
Lit::Char(_) => {
if let Some(Type::Path(ty)) = ty {
let ty_string = ty.into_token_stream().to_string();
if ty_string == "char" {
// don't call into
return expr;
}
}
},
Lit::Byte(_) => {
if let Some(Type::Path(ty)) = ty {
let ty_string = ty.into_token_stream().to_string();
if ty_string == "u8" {
// don't call into
return expr;
}
}
},
Lit::ByteStr(_) => {
if let Some(Type::Reference(ty)) = ty {
if let Type::Array(ty) = ty.elem.as_ref() {
if let Type::Path(ty) = ty.elem.as_ref() {
let ty_string = ty.into_token_stream().to_string();
if ty_string == "u8" {
// don't call into
return expr;
}
}
}
}
},
_ => (),
}
syn::parse2(quote!(::core::convert::Into::into(#expr))).unwrap()
},
_ => expr,
}
}

161
vendor/educe/src/common/ident_bool.rs vendored Normal file
View File

@@ -0,0 +1,161 @@
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Expr, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue,
};
use super::path::path_to_string;
#[derive(Debug)]
pub(crate) enum IdentOrBool {
Ident(Ident),
Bool(bool),
}
impl Parse for IdentOrBool {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
if let Ok(lit) = input.parse::<Lit>() {
match lit {
Lit::Bool(lit) => return Ok(Self::Bool(lit.value)),
Lit::Str(lit) => {
return match lit.parse::<Ident>() {
Ok(ident) => Ok(Self::Ident(ident)),
Err(_) if lit.value().is_empty() => Ok(Self::Bool(false)),
Err(error) => Err(error),
}
},
_ => (),
}
}
Ok(Self::Ident(input.parse::<Ident>()?))
}
}
#[inline]
pub(crate) fn meta_name_value_2_ident(name_value: &MetaNameValue) -> syn::Result<Ident> {
match &name_value.value {
Expr::Lit(lit) => {
if let Lit::Str(lit) = &lit.lit {
return lit.parse();
}
},
Expr::Path(path) => {
if let Some(ident) = path.path.get_ident() {
return Ok(ident.clone());
}
},
_ => (),
}
Err(syn::Error::new(
name_value.value.span(),
format!("expected `{path} = Ident`", path = path_to_string(&name_value.path)),
))
}
#[inline]
pub(crate) fn meta_2_ident(meta: &Meta) -> syn::Result<Ident> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_ident(name_value),
Meta::List(list) => {
if let Ok(lit) = list.parse_args::<LitStr>() {
lit.parse()
} else {
list.parse_args()
}
},
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!("expected `{path} = Ident` or `{path}(Ident)`", path = path_to_string(path)),
)),
}
}
#[inline]
pub(crate) fn meta_name_value_2_bool(name_value: &MetaNameValue) -> syn::Result<bool> {
if let Expr::Lit(lit) = &name_value.value {
if let Lit::Bool(b) = &lit.lit {
return Ok(b.value);
}
}
Err(syn::Error::new(
name_value.value.span(),
format!("expected `{path} = false`", path = path_to_string(&name_value.path)),
))
}
#[inline]
pub(crate) fn meta_2_bool(meta: &Meta) -> syn::Result<bool> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_bool(name_value),
Meta::List(list) => Ok(list.parse_args::<LitBool>()?.value),
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!("expected `{path} = false` or `{path}(false)`", path = path_to_string(path)),
)),
}
}
#[inline]
pub(crate) fn meta_2_bool_allow_path(meta: &Meta) -> syn::Result<bool> {
match &meta {
Meta::Path(_) => Ok(true),
Meta::NameValue(name_value) => meta_name_value_2_bool(name_value),
Meta::List(list) => Ok(list.parse_args::<LitBool>()?.value),
}
}
#[inline]
pub(crate) fn meta_name_value_2_ident_and_bool(
name_value: &MetaNameValue,
) -> syn::Result<IdentOrBool> {
match &name_value.value {
Expr::Lit(lit) => match &lit.lit {
Lit::Str(lit) => match lit.parse::<Ident>() {
Ok(ident) => return Ok(IdentOrBool::Ident(ident)),
Err(_) if lit.value().is_empty() => {
return Ok(IdentOrBool::Bool(false));
},
Err(error) => {
return Err(error);
},
},
Lit::Bool(lit) => {
return Ok(IdentOrBool::Bool(lit.value));
},
_ => (),
},
Expr::Path(path) => {
if let Some(ident) = path.path.get_ident() {
return Ok(IdentOrBool::Ident(ident.clone()));
}
},
_ => (),
}
Err(syn::Error::new(
name_value.value.span(),
format!(
"expected `{path} = Ident` or `{path} = false`",
path = path_to_string(&name_value.path)
),
))
}
#[inline]
pub(crate) fn meta_2_ident_and_bool(meta: &Meta) -> syn::Result<IdentOrBool> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_ident_and_bool(name_value),
Meta::List(list) => list.parse_args::<IdentOrBool>(),
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!(
"expected `{path} = Ident`, `{path}(Ident)`, `{path} = false`, or `{path}(false)`",
path = path_to_string(path)
),
)),
}
}

56
vendor/educe/src/common/ident_index.rs vendored Normal file
View File

@@ -0,0 +1,56 @@
use quote::ToTokens;
use syn::{Ident, Index};
pub(crate) enum IdentOrIndex {
Ident(Ident),
Index(Index),
}
impl From<Ident> for IdentOrIndex {
#[inline]
fn from(value: Ident) -> Self {
Self::Ident(value)
}
}
impl From<Index> for IdentOrIndex {
#[inline]
fn from(value: Index) -> Self {
Self::Index(value)
}
}
impl From<&Ident> for IdentOrIndex {
#[inline]
fn from(value: &Ident) -> Self {
Self::Ident(value.clone())
}
}
impl From<usize> for IdentOrIndex {
#[inline]
fn from(value: usize) -> Self {
Self::Index(Index::from(value))
}
}
impl ToTokens for IdentOrIndex {
#[inline]
fn to_tokens(&self, token_stream: &mut proc_macro2::TokenStream) {
match self {
Self::Ident(ident) => ToTokens::to_tokens(ident, token_stream),
Self::Index(index) => ToTokens::to_tokens(index, token_stream),
}
}
}
impl IdentOrIndex {
#[inline]
pub(crate) fn from_ident_with_index(ident: Option<&Ident>, index: usize) -> IdentOrIndex {
if let Some(ident) = ident {
Self::from(ident)
} else {
Self::from(index)
}
}
}

63
vendor/educe/src/common/int.rs vendored Normal file
View File

@@ -0,0 +1,63 @@
use syn::{spanned::Spanned, Expr, Lit, Meta, MetaNameValue, UnOp};
use super::path::path_to_string;
#[inline]
pub(crate) fn meta_name_value_2_isize(name_value: &MetaNameValue) -> syn::Result<isize> {
match &name_value.value {
Expr::Lit(lit) => match &lit.lit {
Lit::Str(lit) => {
return lit
.value()
.parse::<isize>()
.map_err(|error| syn::Error::new(lit.span(), error))
},
Lit::Int(lit) => return lit.base10_parse(),
_ => (),
},
Expr::Unary(unary) => {
if let UnOp::Neg(_) = unary.op {
if let Expr::Lit(lit) = unary.expr.as_ref() {
if let Lit::Int(lit) = &lit.lit {
let s = format!("-{}", lit.base10_digits());
return s
.parse::<isize>()
.map_err(|error| syn::Error::new(lit.span(), error));
}
}
}
},
_ => (),
}
Err(syn::Error::new(
name_value.value.span(),
format!("expected `{path} = integer`", path = path_to_string(&name_value.path)),
))
}
#[inline]
pub(crate) fn meta_2_isize(meta: &Meta) -> syn::Result<isize> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_isize(name_value),
Meta::List(list) => {
let lit = list.parse_args::<Lit>()?;
match &lit {
Lit::Str(lit) => {
lit.value().parse::<isize>().map_err(|error| syn::Error::new(lit.span(), error))
},
Lit::Int(lit) => lit.base10_parse(),
_ => Err(syn::Error::new(lit.span(), "not an integer")),
}
},
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!(
"expected `{path} = integer` or `{path}(integer)`",
path = path_to_string(path)
),
)),
}
}

43
vendor/educe/src/common/mod.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
#[allow(dead_code)]
pub(crate) mod bound;
#[allow(dead_code)]
pub(crate) mod path;
#[allow(dead_code)]
pub(crate) mod r#type;
#[allow(dead_code)]
pub(crate) mod where_predicates_bool;
#[cfg(feature = "Default")]
#[allow(dead_code)]
pub(crate) mod expr;
#[cfg(any(
feature = "Debug",
feature = "PartialEq",
feature = "PartialOrd",
feature = "Ord",
feature = "Hash",
feature = "Default"
))]
#[allow(dead_code)]
pub(crate) mod ident_bool;
#[cfg(any(
feature = "Debug",
feature = "PartialEq",
feature = "PartialOrd",
feature = "Ord",
feature = "Hash",
feature = "Deref",
feature = "DerefMut",
feature = "Into"
))]
#[allow(dead_code)]
pub(crate) mod ident_index;
#[cfg(any(feature = "PartialOrd", feature = "Ord"))]
#[allow(dead_code)]
pub(crate) mod int;
#[cfg(any(feature = "Debug", feature = "PartialEq", feature = "Hash"))]
#[allow(dead_code)]
pub(crate) mod unsafe_punctuated_meta;
#[cfg(any(feature = "PartialOrd", feature = "Ord", feature = "Into"))]
pub(crate) mod tools;

43
vendor/educe/src/common/path.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
use quote::ToTokens;
use syn::{spanned::Spanned, Expr, Lit, LitStr, Meta, MetaNameValue, Path};
#[inline]
pub(crate) fn meta_name_value_2_path(name_value: &MetaNameValue) -> syn::Result<Path> {
match &name_value.value {
Expr::Lit(lit) => {
if let Lit::Str(lit) = &lit.lit {
return lit.parse();
}
},
Expr::Path(path) => return Ok(path.path.clone()),
_ => (),
}
Err(syn::Error::new(
name_value.value.span(),
format!("expected `{path} = Path`", path = path_to_string(&name_value.path)),
))
}
#[inline]
pub(crate) fn meta_2_path(meta: &Meta) -> syn::Result<Path> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_path(name_value),
Meta::List(list) => {
if let Ok(lit) = list.parse_args::<LitStr>() {
lit.parse()
} else {
list.parse_args()
}
},
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!("expected `{path} = Path` or `{path}(Path)`", path = path_to_string(path)),
)),
}
}
#[inline]
pub(crate) fn path_to_string(path: &Path) -> String {
path.into_token_stream().to_string().replace(' ', "")
}

View File

@@ -0,0 +1,169 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use syn::{
punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Expr, Lit, Meta, Token, UnOp,
};
#[derive(Debug)]
pub(crate) enum DiscriminantType {
ISize,
I8,
I16,
I32,
I64,
I128,
USize,
U8,
U16,
U32,
U64,
U128,
}
impl DiscriminantType {
#[inline]
pub(crate) fn parse_str<S: AsRef<str>>(s: S) -> Option<Self> {
match s.as_ref() {
"i8" => Some(Self::I8),
"i16" => Some(Self::I16),
"i32" => Some(Self::I32),
"i64" => Some(Self::I64),
"i128" => Some(Self::I128),
"isize" => Some(Self::ISize),
"u8" => Some(Self::U8),
"u16" => Some(Self::U16),
"u32" => Some(Self::U32),
"u64" => Some(Self::U64),
"u128" => Some(Self::U128),
"usize" => Some(Self::USize),
_ => None,
}
}
#[inline]
pub(crate) const fn as_str(&self) -> &'static str {
match self {
Self::ISize => "isize",
Self::I8 => "i8",
Self::I16 => "i16",
Self::I32 => "i32",
Self::I64 => "i64",
Self::I128 => "i128",
Self::USize => "usize",
Self::U8 => "u8",
Self::U16 => "u16",
Self::U32 => "u32",
Self::U64 => "u64",
Self::U128 => "u128",
}
}
}
impl ToTokens for DiscriminantType {
#[inline]
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append(Ident::new(self.as_str(), Span::call_site()));
}
}
impl DiscriminantType {
pub(crate) fn from_ast(ast: &DeriveInput) -> syn::Result<Self> {
if let Data::Enum(data) = &ast.data {
for attr in ast.attrs.iter() {
if attr.path().is_ident("repr") {
// #[repr(u8)], #[repr(u16)], ..., etc.
if let Meta::List(list) = &attr.meta {
let result =
list.parse_args_with(Punctuated::<Ident, Token![,]>::parse_terminated)?;
if let Some(value) = result.into_iter().next() {
if let Some(t) = Self::parse_str(value.to_string()) {
return Ok(t);
}
}
}
}
}
let mut min = i128::MAX;
let mut max = i128::MIN;
let mut counter = 0i128;
for variant in data.variants.iter() {
if let Some((_, exp)) = variant.discriminant.as_ref() {
match exp {
Expr::Lit(lit) => {
if let Lit::Int(lit) = &lit.lit {
counter = lit
.base10_parse()
.map_err(|error| syn::Error::new(lit.span(), error))?;
} else {
return Err(syn::Error::new(lit.span(), "not an integer"));
}
},
Expr::Unary(unary) => {
if let UnOp::Neg(_) = unary.op {
if let Expr::Lit(lit) = unary.expr.as_ref() {
if let Lit::Int(lit) = &lit.lit {
match lit.base10_parse::<i128>() {
Ok(i) => {
counter = -i;
},
Err(error) => {
// overflow
if lit.base10_digits()
== "170141183460469231731687303715884105728"
{
counter = i128::MIN;
} else {
return Err(syn::Error::new(lit.span(), error));
}
},
}
} else {
return Err(syn::Error::new(lit.span(), "not an integer"));
}
} else {
return Err(syn::Error::new(
unary.expr.span(),
"not a literal",
));
}
} else {
return Err(syn::Error::new(
unary.op.span(),
"this operation is not allow here",
));
}
},
_ => return Err(syn::Error::new(exp.span(), "not a literal")),
}
}
if min > counter {
min = counter;
}
if max < counter {
max = counter;
}
counter = counter.saturating_add(1);
}
Ok(if min >= i8::MIN as i128 && max <= i8::MAX as i128 {
Self::I8
} else if min >= i16::MIN as i128 && max <= i16::MAX as i128 {
Self::I16
} else if min >= i32::MIN as i128 && max <= i32::MAX as i128 {
Self::I32
} else if min >= i64::MIN as i128 && max <= i64::MAX as i128 {
Self::I64
} else {
Self::I128
})
} else {
Err(syn::Error::new(ast.span(), "not an enum"))
}
}
}

View File

@@ -0,0 +1,100 @@
use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
hash::{Hash, Hasher},
str::FromStr,
};
use proc_macro2::Span;
use quote::ToTokens;
use syn::{spanned::Spanned, Path, Type};
#[derive(Debug, Clone)]
pub(crate) struct HashType(String, Span);
impl PartialEq for HashType {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl Eq for HashType {}
impl PartialOrd for HashType {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for HashType {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl Hash for HashType {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&self.0, state);
}
}
impl Display for HashType {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0.replace("& '", "&'"), f)
}
}
impl From<Type> for HashType {
#[inline]
fn from(value: Type) -> Self {
Self::from(&value)
}
}
impl From<&Type> for HashType {
#[inline]
fn from(value: &Type) -> Self {
Self(value.into_token_stream().to_string(), value.span())
}
}
impl From<Path> for HashType {
#[inline]
fn from(value: Path) -> Self {
Self::from(&value)
}
}
impl From<&Path> for HashType {
#[inline]
fn from(value: &Path) -> Self {
Self(value.into_token_stream().to_string(), value.span())
}
}
#[allow(dead_code)]
impl HashType {
#[inline]
pub(crate) fn to_type(&self) -> Type {
syn::parse_str(self.0.as_str()).unwrap()
}
#[inline]
pub(crate) fn span(&self) -> Span {
self.1
}
}
impl ToTokens for HashType {
#[inline]
fn to_tokens(&self, token_stream: &mut proc_macro2::TokenStream) {
let ty = proc_macro2::TokenStream::from_str(self.0.as_str()).unwrap();
token_stream.extend(ty);
}
}

11
vendor/educe/src/common/tools/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
#[cfg(any(feature = "PartialOrd", feature = "Ord"))]
mod discriminant_type;
#[cfg(any(feature = "PartialOrd", feature = "Ord"))]
pub(crate) use discriminant_type::*;
#[cfg(feature = "Into")]
mod hash_type;
#[cfg(feature = "Into")]
pub(crate) use hash_type::*;

51
vendor/educe/src/common/type.rs vendored Normal file
View File

@@ -0,0 +1,51 @@
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
Meta, Token, Type,
};
pub(crate) struct TypeWithPunctuatedMeta {
pub(crate) ty: Type,
pub(crate) list: Punctuated<Meta, Token![,]>,
}
impl Parse for TypeWithPunctuatedMeta {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
let ty = input.parse::<Type>()?;
if input.is_empty() {
return Ok(Self {
ty,
list: Punctuated::new(),
});
}
input.parse::<Token![,]>()?;
let list = input.parse_terminated(Meta::parse, Token![,])?;
Ok(Self {
ty,
list,
})
}
}
#[inline]
pub(crate) fn dereference(ty: &Type) -> &Type {
if let Type::Reference(ty) = ty {
dereference(ty.elem.as_ref())
} else {
ty
}
}
#[inline]
pub(crate) fn dereference_changed(ty: &Type) -> (&Type, bool) {
if let Type::Reference(ty) = ty {
(dereference(ty.elem.as_ref()), true)
} else {
(ty, false)
}
}

View File

@@ -0,0 +1,35 @@
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
Meta, Token,
};
pub(crate) struct UnsafePunctuatedMeta {
pub(crate) list: Punctuated<Meta, Token![,]>,
pub(crate) has_unsafe: bool,
}
impl Parse for UnsafePunctuatedMeta {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
let has_unsafe = input.parse::<Token![unsafe]>().is_ok();
if input.is_empty() {
return Ok(Self {
list: Punctuated::new(),
has_unsafe,
});
}
if has_unsafe {
input.parse::<Token![,]>()?;
}
let list = input.parse_terminated(Meta::parse, Token![,])?;
Ok(Self {
list,
has_unsafe,
})
}
}

View File

@@ -0,0 +1,122 @@
use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
Expr, GenericParam, Lit, Meta, MetaNameValue, Path, Token, Type, WherePredicate,
};
use super::path::path_to_string;
pub(crate) type WherePredicates = Punctuated<WherePredicate, Token![,]>;
pub(crate) enum WherePredicatesOrBool {
WherePredicates(WherePredicates),
Bool(bool),
All,
}
impl WherePredicatesOrBool {
fn from_lit(lit: &Lit) -> syn::Result<Self> {
Ok(match lit {
Lit::Bool(lit) => Self::Bool(lit.value),
Lit::Str(lit) => match lit.parse_with(WherePredicates::parse_terminated) {
Ok(where_predicates) => Self::WherePredicates(where_predicates),
Err(_) if lit.value().is_empty() => Self::Bool(false),
Err(error) => return Err(error),
},
other => {
return Err(syn::Error::new(
other.span(),
"unexpected kind of literal (only boolean or string allowed)",
))
},
})
}
}
impl Parse for WherePredicatesOrBool {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
if let Ok(lit) = input.parse::<Lit>() {
return Self::from_lit(&lit);
}
if let Ok(_star) = input.parse::<Token![*]>() {
return Ok(Self::All);
}
Ok(Self::WherePredicates(input.parse_terminated(WherePredicate::parse, Token![,])?))
}
}
#[inline]
pub(crate) fn meta_name_value_2_where_predicates_bool(
name_value: &MetaNameValue,
) -> syn::Result<WherePredicatesOrBool> {
if let Expr::Lit(lit) = &name_value.value {
return WherePredicatesOrBool::from_lit(&lit.lit);
}
Err(syn::Error::new(
name_value.value.span(),
format!(
"expected `{path} = \"where_predicates\"` or `{path} = false`",
path = path_to_string(&name_value.path)
),
))
}
#[inline]
pub(crate) fn meta_2_where_predicates(meta: &Meta) -> syn::Result<WherePredicatesOrBool> {
match &meta {
Meta::NameValue(name_value) => meta_name_value_2_where_predicates_bool(name_value),
Meta::List(list) => list.parse_args::<WherePredicatesOrBool>(),
Meta::Path(path) => Err(syn::Error::new(
path.span(),
format!(
"expected `{path} = \"where_predicates\"`, `{path}(where_predicates)`, `{path} = \
false`, or `{path}(false)`",
path = path.clone().into_token_stream()
),
)),
}
}
#[inline]
pub(crate) fn create_where_predicates_from_all_generic_parameters(
params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
) -> WherePredicates {
let mut where_predicates = Punctuated::new();
for param in params {
if let GenericParam::Type(ty) = param {
let ident = &ty.ident;
where_predicates.push(syn::parse2(quote! { #ident: #bound_trait }).unwrap());
}
}
where_predicates
}
#[inline]
pub(crate) fn create_where_predicates_from_generic_parameters_check_types(
bound_trait: &Path,
types: &[&Type],
supertraits: &[proc_macro2::TokenStream],
) -> WherePredicates {
let mut where_predicates = Punctuated::new();
for t in types {
where_predicates.push(syn::parse2(quote! { #t: #bound_trait }).unwrap());
}
for supertrait in supertraits {
where_predicates.push(syn::parse2(quote! { Self: #supertrait }).unwrap());
}
where_predicates
}

2135
vendor/educe/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

130
vendor/educe/src/panic.rs vendored Normal file
View File

@@ -0,0 +1,130 @@
use core::fmt::{self, Display, Formatter};
use proc_macro2::Span;
use syn::{spanned::Spanned, Ident, Path, Variant};
use crate::{common::path::path_to_string, Trait};
struct DisplayStringSlice<'a>(&'a [&'static str]);
impl<'a> Display for DisplayStringSlice<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if !self.0.is_empty() {
f.write_str(", which should be reformatted as follows:")?;
for &s in self.0 {
f.write_str("\n ")?;
f.write_str(s)?;
}
}
Ok(())
}
}
struct DisplayTraits;
impl Display for DisplayTraits {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for t in &Trait::VARIANTS[..Trait::VARIANTS.len() - 1] {
f.write_str("\n ")?;
f.write_fmt(format_args!("{t:?}"))?;
}
Ok(())
}
}
#[inline]
pub(crate) fn derive_attribute_not_set_up_yet() -> syn::Error {
syn::Error::new(
Span::call_site(),
"you are using `Educe` in the `derive` attribute, but it has not been set up yet",
)
}
#[inline]
pub(crate) fn attribute_incorrect_place(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), format!("the `{name}` attribute cannot be placed here"))
}
#[inline]
pub(crate) fn attribute_incorrect_format_with_span(
name: &Ident,
span: Span,
correct_usage: &[&'static str],
) -> syn::Error {
if correct_usage.is_empty() {
attribute_incorrect_place(name)
} else {
syn::Error::new(
span,
format!(
"you are using an incorrect format of the `{name}` attribute{}",
DisplayStringSlice(correct_usage)
),
)
}
}
#[inline]
pub(crate) fn attribute_incorrect_format(
name: &Ident,
correct_usage: &[&'static str],
) -> syn::Error {
attribute_incorrect_format_with_span(name, name.span(), correct_usage)
}
#[inline]
pub(crate) fn parameter_reset(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), format!("you are trying to reset the `{name}` parameter"))
}
#[inline]
pub(crate) fn educe_format_incorrect(name: &Ident) -> syn::Error {
attribute_incorrect_format(name, &[stringify!(#[educe(Trait1, Trait2, ..., TraitN)])])
}
#[inline]
pub(crate) fn unsupported_trait(name: &Path) -> syn::Error {
let span = name.span();
match name.get_ident() {
Some(name) => syn::Error::new(
span,
format!("unsupported trait `{name}`, available traits:{DisplayTraits}"),
),
None => {
let name = path_to_string(name);
syn::Error::new(
span,
format!("unsupported trait `{name}`, available traits:{DisplayTraits}"),
)
},
}
}
#[inline]
pub(crate) fn reuse_a_trait(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), format!("the trait `{name}` is used repeatedly"))
}
#[inline]
pub(crate) fn trait_not_used(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), format!("the trait `{name}` is not used"))
}
#[inline]
pub(crate) fn trait_not_support_union(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), format!("the trait `{name}` does not support to a union"))
}
#[inline]
pub(crate) fn trait_not_support_unit_variant(name: &Ident, variant: &Variant) -> syn::Error {
syn::Error::new(
variant.span(),
format!("the trait `{name}` cannot be implemented for an enum which has unit variants"),
)
}

88
vendor/educe/src/supported_traits.rs vendored Normal file
View File

@@ -0,0 +1,88 @@
#[cfg(not(any(
feature = "Debug",
feature = "Clone",
feature = "Copy",
feature = "PartialEq",
feature = "Eq",
feature = "PartialOrd",
feature = "Ord",
feature = "Hash",
feature = "Default",
feature = "Deref",
feature = "DerefMut",
feature = "Into",
)))]
compile_error!("at least one of the trait features must be enabled");
use enum_ordinalize::Ordinalize;
use syn::Path;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ordinalize)]
#[ordinalize(impl_trait = false)]
#[ordinalize(variants(pub(crate) const VARIANTS))]
pub(crate) enum Trait {
#[cfg(feature = "Debug")]
Debug,
#[cfg(feature = "Clone")]
Clone,
#[cfg(feature = "Copy")]
Copy,
#[cfg(feature = "PartialEq")]
PartialEq,
#[cfg(feature = "Eq")]
Eq,
#[cfg(feature = "PartialOrd")]
PartialOrd,
#[cfg(feature = "Ord")]
Ord,
#[cfg(feature = "Hash")]
Hash,
#[cfg(feature = "Default")]
Default,
#[cfg(feature = "Deref")]
Deref,
#[cfg(feature = "DerefMut")]
DerefMut,
#[cfg(feature = "Into")]
Into,
_Nothing,
}
impl Trait {
#[inline]
pub(crate) fn from_path(path: &Path) -> Option<Self> {
let ident_string = match path.get_ident() {
Some(ident) => ident.to_string(),
None => return None,
};
match ident_string.as_str() {
#[cfg(feature = "Debug")]
"Debug" => Some(Self::Debug),
#[cfg(feature = "Clone")]
"Clone" => Some(Self::Clone),
#[cfg(feature = "Copy")]
"Copy" => Some(Self::Copy),
#[cfg(feature = "PartialEq")]
"PartialEq" => Some(Self::PartialEq),
#[cfg(feature = "Eq")]
"Eq" => Some(Self::Eq),
#[cfg(feature = "PartialOrd")]
"PartialOrd" => Some(Self::PartialOrd),
#[cfg(feature = "Ord")]
"Ord" => Some(Self::Ord),
#[cfg(feature = "Hash")]
"Hash" => Some(Self::Hash),
#[cfg(feature = "Default")]
"Default" => Some(Self::Default),
#[cfg(feature = "Deref")]
"Deref" => Some(Self::Deref),
#[cfg(feature = "DerefMut")]
"DerefMut" => Some(Self::DerefMut),
#[cfg(feature = "Into")]
"Into" => Some(Self::Into),
_ => None,
}
}
}

View File

@@ -0,0 +1,272 @@
use quote::{format_ident, quote};
use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Meta, Type, Variant};
use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder};
use crate::{
common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler,
};
pub(crate) struct CloneEnumHandler;
impl TraitHandler for CloneEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true, enable_bound: true
}
.build_from_clone_meta(meta)?;
let mut bound: WherePredicates = Punctuated::new();
let mut clone_token_stream = proc_macro2::TokenStream::new();
let mut clone_from_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
type Variants<'a> = Vec<(&'a Variant, Vec<(&'a Field, FieldAttribute)>)>;
let mut variants: Variants = Vec::new();
#[cfg(feature = "Copy")]
let mut has_custom_clone_method = false;
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false, enable_bound: false
}
.build_from_attributes(&variant.attrs, traits)?;
let mut variant_fields: Vec<(&Field, FieldAttribute)> = Vec::new();
for field in variant.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_method: true
}
.build_from_attributes(&field.attrs, traits)?;
#[cfg(feature = "Copy")]
if field_attribute.method.is_some() {
has_custom_clone_method = true;
}
variant_fields.push((field, field_attribute));
}
variants.push((variant, variant_fields));
}
#[cfg(feature = "Copy")]
let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy);
#[cfg(not(feature = "Copy"))]
let contains_copy = false;
if contains_copy {
clone_token_stream.extend(quote!(*self));
}
let mut clone_types: Vec<&Type> = Vec::new();
if variants.is_empty() {
if !contains_copy {
clone_token_stream.extend(quote!(unreachable!()));
clone_from_token_stream.extend(quote!(let _ = source;));
}
} else {
let mut clone_variants_token_stream = proc_macro2::TokenStream::new();
let mut clone_from_variants_token_stream = proc_macro2::TokenStream::new();
for (variant, variant_fields) in variants {
let variant_ident = &variant.ident;
match &variant.fields {
Fields::Unit => {
clone_variants_token_stream.extend(quote! {
Self::#variant_ident => Self::#variant_ident,
});
clone_from_variants_token_stream.extend(quote! {
Self::#variant_ident => {
if let Self::#variant_ident = source {
// same
} else {
*self = ::core::clone::Clone::clone(source);
}
},
});
},
Fields::Named(_) => {
let mut pattern_src_token_stream = proc_macro2::TokenStream::new();
let mut pattern_dst_token_stream = proc_macro2::TokenStream::new();
let mut cl_fields_token_stream = proc_macro2::TokenStream::new();
let mut cf_body_token_stream = proc_macro2::TokenStream::new();
for (field, field_attribute) in variant_fields {
let field_name_real = field.ident.as_ref().unwrap();
let field_name_src = format_ident!("_s_{}", field_name_real);
let field_name_dst = format_ident!("_d_{}", field_name_real);
pattern_src_token_stream
.extend(quote!(#field_name_real: #field_name_src,));
pattern_dst_token_stream
.extend(quote!(#field_name_real: #field_name_dst,));
if let Some(clone) = field_attribute.method.as_ref() {
cl_fields_token_stream.extend(quote! {
#field_name_real: #clone(#field_name_src),
});
cf_body_token_stream.extend(
quote!(*#field_name_dst = #clone(#field_name_src);),
);
} else {
clone_types.push(&field.ty);
cl_fields_token_stream.extend(quote! {
#field_name_real: ::core::clone::Clone::clone(#field_name_src),
});
cf_body_token_stream.extend(
quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ),
);
}
}
clone_variants_token_stream.extend(quote! {
Self::#variant_ident { #pattern_src_token_stream } => Self::#variant_ident { #cl_fields_token_stream },
});
clone_from_variants_token_stream.extend(quote! {
Self::#variant_ident { #pattern_dst_token_stream } => {
if let Self::#variant_ident { #pattern_src_token_stream } = source {
#cf_body_token_stream
} else {
*self = ::core::clone::Clone::clone(source);
}
},
});
},
Fields::Unnamed(_) => {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut pattern2_token_stream = proc_macro2::TokenStream::new();
let mut fields_token_stream = proc_macro2::TokenStream::new();
let mut body_token_stream = proc_macro2::TokenStream::new();
for (index, (field, field_attribute)) in
variant_fields.into_iter().enumerate()
{
let field_name_src = format_ident!("_{}", index);
pattern_token_stream.extend(quote!(#field_name_src,));
let field_name_dst = format_ident!("_{}", field_name_src);
pattern2_token_stream.extend(quote!(#field_name_dst,));
if let Some(clone) = field_attribute.method.as_ref() {
fields_token_stream.extend(quote! (#clone(#field_name_src),));
body_token_stream.extend(
quote!(*#field_name_src = #clone(#field_name_dst);),
);
} else {
clone_types.push(&field.ty);
fields_token_stream.extend(
quote! ( ::core::clone::Clone::clone(#field_name_src), ),
);
body_token_stream.extend(
quote!( ::core::clone::Clone::clone_from(#field_name_src, #field_name_dst); ),
);
}
}
clone_variants_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => Self::#variant_ident ( #fields_token_stream ),
});
clone_from_variants_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => {
if let Self::#variant_ident ( #pattern2_token_stream ) = source {
#body_token_stream
} else {
*self = ::core::clone::Clone::clone(source);
}
},
});
},
}
}
if !contains_copy {
clone_token_stream.extend(quote! {
match self {
#clone_variants_token_stream
}
});
clone_from_token_stream.extend(quote! {
match self {
#clone_from_variants_token_stream
}
});
}
}
bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(if contains_copy {
quote!(::core::marker::Copy)
} else {
quote!(::core::clone::Clone)
})
.unwrap(),
&clone_types,
&[],
);
}
let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() {
None
} else {
Some(quote! {
#[inline]
fn clone_from(&mut self, source: &Self) {
#clone_from_token_stream
}
})
};
let ident = &ast.ident;
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::clone::Clone for #ident #ty_generics #where_clause {
#[inline]
fn clone(&self) -> Self {
#clone_token_stream
}
#clone_from_fn_token_stream
}
});
#[cfg(feature = "Copy")]
if traits.contains(&Trait::Copy) {
token_stream.extend(quote! {
impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,195 @@
use quote::quote;
use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Index, Meta, Type};
use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder};
use crate::{
common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler,
};
pub(crate) struct CloneStructHandler;
impl TraitHandler for CloneStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true, enable_bound: true
}
.build_from_clone_meta(meta)?;
let mut bound: WherePredicates = Punctuated::new();
let mut clone_token_stream = proc_macro2::TokenStream::new();
let mut clone_from_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
let mut fields: Vec<(&Field, FieldAttribute)> = Vec::new();
#[cfg(feature = "Copy")]
let contains_copy = traits.contains(&Trait::Copy);
#[cfg(not(feature = "Copy"))]
let contains_copy = false;
if contains_copy {
clone_token_stream.extend(quote!(*self));
}
for field in data.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_method: !contains_copy
}
.build_from_attributes(&field.attrs, traits)?;
fields.push((field, field_attribute));
}
let mut clone_types: Vec<&Type> = Vec::new();
match &data.fields {
Fields::Unit => {
if !contains_copy {
clone_token_stream.extend(quote!(Self));
clone_from_token_stream.extend(quote!(let _ = source;));
}
},
Fields::Named(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
let mut clone_from_body_token_stream = proc_macro2::TokenStream::new();
if fields.is_empty() {
clone_from_body_token_stream.extend(quote!(let _ = source;));
} else {
for (field, field_attribute) in fields {
let field_name = field.ident.as_ref().unwrap();
if let Some(clone) = field_attribute.method.as_ref() {
fields_token_stream.extend(quote! {
#field_name: #clone(&self.#field_name),
});
clone_from_body_token_stream.extend(
quote!(self.#field_name = #clone(&source.#field_name);),
);
} else {
clone_types.push(&field.ty);
fields_token_stream.extend(quote! {
#field_name: ::core::clone::Clone::clone(&self.#field_name),
});
clone_from_body_token_stream.extend(
quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ),
);
}
}
}
if !contains_copy {
clone_token_stream.extend(quote! {
Self {
#fields_token_stream
}
});
clone_from_token_stream.extend(clone_from_body_token_stream);
}
},
Fields::Unnamed(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
let mut clone_from_body_token_stream = proc_macro2::TokenStream::new();
if fields.is_empty() {
clone_from_body_token_stream.extend(quote!(let _ = source;));
} else {
for (index, (field, field_attribute)) in fields.into_iter().enumerate() {
let field_name = Index::from(index);
if let Some(clone) = field_attribute.method.as_ref() {
fields_token_stream.extend(quote!(#clone(&self.#field_name),));
clone_from_body_token_stream.extend(
quote!(self.#field_name = #clone(&source.#field_name);),
);
} else {
clone_types.push(&field.ty);
fields_token_stream.extend(
quote! ( ::core::clone::Clone::clone(&self.#field_name), ),
);
clone_from_body_token_stream.extend(
quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ),
);
}
}
}
if !contains_copy {
clone_token_stream.extend(quote!(Self ( #fields_token_stream )));
clone_from_token_stream.extend(clone_from_body_token_stream);
}
},
}
bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(if contains_copy {
quote!(::core::marker::Copy)
} else {
quote!(::core::clone::Clone)
})
.unwrap(),
&clone_types,
&[],
);
}
let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() {
None
} else {
Some(quote! {
#[inline]
fn clone_from(&mut self, source: &Self) {
#clone_from_token_stream
}
})
};
let ident = &ast.ident;
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::clone::Clone for #ident #ty_generics #where_clause {
#[inline]
fn clone(&self) -> Self {
#clone_token_stream
}
#clone_from_fn_token_stream
}
});
#[cfg(feature = "Copy")]
if traits.contains(&Trait::Copy) {
token_stream.extend(quote! {
impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,73 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::supported_traits::Trait;
pub(crate) struct CloneUnionHandler;
impl TraitHandler for CloneUnionHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true, enable_bound: true
}
.build_from_clone_meta(meta)?;
let mut field_types = vec![];
if let Data::Union(data) = &ast.data {
for field in data.fields.named.iter() {
field_types.push(&field.ty);
let _ = FieldAttributeBuilder {
enable_method: false
}
.build_from_attributes(&field.attrs, traits)?;
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::marker::Copy)).unwrap(),
&field_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::clone::Clone for #ident #ty_generics #where_clause {
#[inline]
fn clone(&self) -> Self {
*self
}
}
});
#[cfg(feature = "Copy")]
if traits.contains(&Trait::Copy) {
token_stream.extend(quote! {
impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,36 @@
mod clone_enum;
mod clone_struct;
mod clone_union;
mod models;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct CloneHandler;
impl TraitHandler for CloneHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => clone_struct::CloneStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => {
clone_enum::CloneEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
clone_union::CloneUnionHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
}
}
}

View File

@@ -0,0 +1,125 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Path, Token};
use crate::{common::path::meta_2_path, panic, supported_traits::Trait};
pub(crate) struct FieldAttribute {
pub(crate) method: Option<Path>,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_method: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_clone_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Clone"));
let mut method = None;
let correct_usage_for_clone_attribute = {
let mut usage = vec![];
if self.enable_method {
usage.push(stringify!(#[educe(Clone(method(path_to_method)))]));
}
usage
};
match meta {
Meta::Path(_) | Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_clone_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut method_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "method" {
if !self.enable_method {
return Ok(false);
}
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_clone_attribute,
));
}
}
},
}
Ok(FieldAttribute {
method,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Clone {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_clone_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
method: None
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,140 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{common::bound::Bound, panic, Trait};
pub(crate) struct TypeAttribute {
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_clone_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Clone"));
let mut bound = Bound::Auto;
let correct_usage_for_clone_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Clone)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Clone(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Clone(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_clone_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_clone_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_clone_attribute,
));
}
}
},
}
Ok(TypeAttribute {
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Clone {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_clone_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,94 @@
mod models;
use models::{FieldAttributeBuilder, TypeAttributeBuilder};
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct CopyHandler;
impl TraitHandler for CopyHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
#[cfg(feature = "Clone")]
let contains_clone = traits.contains(&Trait::Clone);
#[cfg(not(feature = "Clone"))]
let contains_clone = false;
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_bound: !contains_clone,
}
.build_from_copy_meta(meta)?;
let mut field_types = vec![];
// if `contains_clone` is true, the implementation is handled by the `Clone` attribute, and field attributes is also handled by the `Clone` attribute
if !contains_clone {
match &ast.data {
Data::Struct(data) => {
for field in data.fields.iter() {
field_types.push(&field.ty);
let _ =
FieldAttributeBuilder.build_from_attributes(&field.attrs, traits)?;
}
},
Data::Enum(data) => {
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false, enable_bound: false
}
.build_from_attributes(&variant.attrs, traits)?;
for field in variant.fields.iter() {
field_types.push(&field.ty);
let _ = FieldAttributeBuilder
.build_from_attributes(&field.attrs, traits)?;
}
}
},
Data::Union(data) => {
for field in data.fields.named.iter() {
field_types.push(&field.ty);
let _ =
FieldAttributeBuilder.build_from_attributes(&field.attrs, traits)?;
}
},
}
let ident = &ast.ident;
let bound =
type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::marker::Copy)).unwrap(),
&field_types,
&[quote! {::core::clone::Clone}],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,57 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{panic, supported_traits::Trait};
pub(crate) struct FieldAttribute;
pub(crate) struct FieldAttributeBuilder;
impl FieldAttributeBuilder {
pub(crate) fn build_from_copy_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Copy"));
return Err(panic::attribute_incorrect_place(meta.path().get_ident().unwrap()));
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Copy {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_copy_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,140 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{common::bound::Bound, panic, Trait};
pub(crate) struct TypeAttribute {
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_copy_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Copy"));
let mut bound = Bound::Auto;
let correct_usage_for_copy_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Copy)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Copy(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Copy(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
}
}
},
}
Ok(TypeAttribute {
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Copy {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_copy_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,55 @@
use quote::quote;
use syn::{DeriveInput, Path, Type};
#[inline]
pub(crate) fn create_debug_map_builder() -> proc_macro2::TokenStream {
quote!(
#[allow(non_camel_case_types)] // We're using __ to help avoid clashes.
struct Educe__RawString(&'static str);
impl ::core::fmt::Debug for Educe__RawString {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str(self.0)
}
}
let mut builder = f.debug_map();
)
}
#[inline]
pub(crate) fn create_format_arg(
ast: &DeriveInput,
field_ty: &Type,
format_method: &Path,
field_expr: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let ty_ident = &ast.ident;
// We use the complete original generics, not filtered by field,
// and include a PhantomData<Self> in our wrapper struct to use the generics.
//
// This avoids having to try to calculate the right *subset* of the generics
// relevant for this field, which is nontrivial and maybe impossible.
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
quote!(
let arg = {
#[allow(non_camel_case_types)] // We're using __ to help avoid clashes.
struct Educe__DebugField<V, M>(V, ::core::marker::PhantomData<M>);
impl #impl_generics ::core::fmt::Debug
for Educe__DebugField<&#field_ty, #ty_ident #ty_generics>
#where_clause
{
#[inline]
fn fmt(&self, educe__f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#format_method(self.0, educe__f)
}
}
Educe__DebugField(#field_expr, ::core::marker::PhantomData::<Self>)
};
)
}

View File

@@ -0,0 +1,371 @@
use quote::{format_ident, quote, ToTokens};
use syn::{Data, DeriveInput, Fields, Meta, Type};
use super::models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName};
use crate::{common::path::path_to_string, supported_traits::Trait, trait_handlers::TraitHandler};
pub(crate) struct DebugEnumHandler;
impl TraitHandler for DebugEnumHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_unsafe: false,
enable_name: true,
enable_named_field: false,
enable_bound: true,
name: TypeName::Disable,
named_field: false,
}
.build_from_debug_meta(meta)?;
let name = type_attribute.name.to_ident_by_ident(&ast.ident);
let mut debug_types: Vec<&Type> = Vec::new();
let mut builder_token_stream = proc_macro2::TokenStream::new();
let mut arms_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
for variant in data.variants.iter() {
let type_attribute = TypeAttributeBuilder {
enable_flag: false,
enable_unsafe: false,
enable_name: true,
enable_named_field: true,
enable_bound: false,
name: TypeName::Default,
named_field: matches!(&variant.fields, Fields::Named(_)),
}
.build_from_attributes(&variant.attrs, traits)?;
let variant_ident = &variant.ident;
let variant_name = type_attribute.name.to_ident_by_ident(variant_ident);
let named_field = type_attribute.named_field;
let name_string = if let Some(name) = name {
if let Some(variant_name) = variant_name {
Some(path_to_string(&syn::parse2(quote!(#name::#variant_name)).unwrap()))
} else {
Some(name.into_token_stream().to_string())
}
} else {
variant_name.map(|variant_name| variant_name.into_token_stream().to_string())
};
match &variant.fields {
Fields::Unit => {
if name_string.is_none() {
return Err(super::panic::unit_variant_need_name(variant));
}
arms_token_stream
.extend(quote!( Self::#variant_ident => f.write_str(#name_string), ));
},
Fields::Named(fields) => {
let mut has_fields = false;
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
if named_field {
block_token_stream
.extend(create_named_field_builder(name_string.as_deref()));
for field in fields.named.iter() {
let field_attribute = FieldAttributeBuilder {
enable_name: true,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_real = field.ident.as_ref().unwrap();
let field_name_var = format_ident!("_{}", field_name_real);
if field_attribute.ignore {
pattern_token_stream.extend(quote!(#field_name_real: _,));
continue;
}
let key = match field_attribute.name {
FieldName::Custom(name) => name,
FieldName::Default => field_name_real.clone(),
};
pattern_token_stream
.extend(quote!(#field_name_real: #field_name_var,));
let ty = &field.ty;
if let Some(method) = field_attribute.method {
block_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(#field_name_var),
));
block_token_stream.extend(if name_string.is_some() {
quote! (builder.field(stringify!(#key), &arg);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
});
} else {
debug_types.push(ty);
block_token_stream.extend(if name_string.is_some() {
quote! (builder.field(stringify!(#key), #field_name_var);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);)
});
}
has_fields = true;
}
} else {
block_token_stream
.extend(quote!(let mut builder = f.debug_tuple(#name_string);));
for field in fields.named.iter() {
let field_attribute = FieldAttributeBuilder {
enable_name: false,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_real = field.ident.as_ref().unwrap();
let field_name_var = format_ident!("_{}", field_name_real);
if field_attribute.ignore {
pattern_token_stream.extend(quote!(#field_name_real: _,));
continue;
}
pattern_token_stream
.extend(quote!(#field_name_real: #field_name_var,));
let ty = &field.ty;
if let Some(method) = field_attribute.method {
block_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(#field_name_var),
));
block_token_stream.extend(quote! (builder.field(&arg);));
} else {
debug_types.push(ty);
block_token_stream
.extend(quote! (builder.field(#field_name_var);));
}
has_fields = true;
}
}
if !has_fields && name_string.is_none() {
return Err(super::panic::unit_struct_need_name(variant_ident));
}
arms_token_stream.extend(quote! {
Self::#variant_ident { #pattern_token_stream } => {
#block_token_stream
builder.finish()
},
});
},
Fields::Unnamed(fields) => {
let mut has_fields = false;
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
if named_field {
block_token_stream
.extend(create_named_field_builder(name_string.as_deref()));
for (index, field) in fields.unnamed.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_name: true,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
pattern_token_stream.extend(quote!(_,));
continue;
}
let field_name_var = format_ident!("_{}", index);
let key = match field_attribute.name {
FieldName::Custom(name) => name,
FieldName::Default => field_name_var.clone(),
};
pattern_token_stream.extend(quote!(#field_name_var,));
let ty = &field.ty;
if let Some(method) = field_attribute.method {
block_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(#field_name_var),
));
block_token_stream.extend(if name_string.is_some() {
quote! (builder.field(stringify!(#key), &arg);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
});
} else {
debug_types.push(ty);
block_token_stream.extend(if name_string.is_some() {
quote! (builder.field(stringify!(#key), #field_name_var);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);)
});
}
has_fields = true;
}
} else {
block_token_stream
.extend(quote!(let mut builder = f.debug_tuple(#name_string);));
for (index, field) in fields.unnamed.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_name: false,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
pattern_token_stream.extend(quote!(_,));
continue;
}
let field_name_var = format_ident!("_{}", index);
pattern_token_stream.extend(quote!(#field_name_var,));
let ty = &field.ty;
if let Some(method) = field_attribute.method {
block_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(#field_name_var),
));
block_token_stream.extend(quote! (builder.field(&arg);));
} else {
debug_types.push(ty);
block_token_stream
.extend(quote! (builder.field(#field_name_var);));
}
has_fields = true;
}
}
if !has_fields && name_string.is_none() {
return Err(super::panic::unit_struct_need_name(variant_ident));
}
arms_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => {
#block_token_stream
builder.finish()
},
});
},
}
}
}
let ident = &ast.ident;
if arms_token_stream.is_empty() {
if let Some(ident) = name {
builder_token_stream.extend(quote! {
f.write_str(stringify!(#ident))
});
} else {
return Err(super::panic::unit_enum_need_name(ident));
}
} else {
builder_token_stream.extend(quote! {
match self {
#arms_token_stream
}
});
}
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::fmt::Debug)).unwrap(),
&debug_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
#builder_token_stream
}
}
});
Ok(())
}
}
#[inline]
fn create_named_field_builder(name_string: Option<&str>) -> proc_macro2::TokenStream {
if let Some(name_string) = name_string {
quote!(let mut builder = f.debug_struct(#name_string);)
} else {
super::common::create_debug_map_builder()
}
}

View File

@@ -0,0 +1,185 @@
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, Fields, Meta, Type};
use super::{
models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName},
TraitHandler,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub struct DebugStructHandler;
impl TraitHandler for DebugStructHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let is_tuple = {
if let Data::Struct(data) = &ast.data {
matches!(data.fields, Fields::Unnamed(_))
} else {
true
}
};
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_unsafe: false,
enable_name: true,
enable_named_field: true,
enable_bound: true,
name: TypeName::Default,
named_field: !is_tuple,
}
.build_from_debug_meta(meta)?;
let name = type_attribute.name.to_ident_by_ident(&ast.ident);
let mut debug_types: Vec<&Type> = Vec::new();
let mut builder_token_stream = proc_macro2::TokenStream::new();
let mut has_fields = false;
if type_attribute.named_field {
builder_token_stream.extend(if let Some(name) = name {
quote!(let mut builder = f.debug_struct(stringify!(#name));)
} else {
super::common::create_debug_map_builder()
});
if let Data::Struct(data) = &ast.data {
for (index, field) in data.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_name: true,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
continue;
}
let (key, field_name) = match field_attribute.name {
FieldName::Custom(name) => {
(name, IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index))
},
FieldName::Default => {
if let Some(ident) = field.ident.as_ref() {
(ident.clone(), IdentOrIndex::from(ident))
} else {
(format_ident!("_{}", index), IdentOrIndex::from(index))
}
},
};
let ty = &field.ty;
if let Some(method) = field_attribute.method {
builder_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(&self.#field_name),
));
builder_token_stream.extend(if name.is_some() {
quote! (builder.field(stringify!(#key), &arg);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
});
} else {
debug_types.push(ty);
builder_token_stream.extend(if name.is_some() {
quote! (builder.field(stringify!(#key), &self.#field_name);)
} else {
quote! (builder.entry(&Educe__RawString(stringify!(#key)), &self.#field_name);)
});
}
has_fields = true;
}
}
} else {
builder_token_stream
.extend(quote!(let mut builder = f.debug_tuple(stringify!(#name));));
if let Data::Struct(data) = &ast.data {
for (index, field) in data.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_name: false,
enable_ignore: true,
enable_method: true,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
continue;
}
let field_name =
IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
let ty = &field.ty;
if let Some(method) = field_attribute.method {
builder_token_stream.extend(super::common::create_format_arg(
ast,
ty,
&method,
quote!(&self.#field_name),
));
builder_token_stream.extend(quote! (builder.field(&arg);));
} else {
debug_types.push(ty);
builder_token_stream.extend(quote! (builder.field(&self.#field_name);));
}
has_fields = true;
}
}
}
let ident = &ast.ident;
if !has_fields && name.is_none() {
return Err(super::panic::unit_struct_need_name(ident));
}
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::fmt::Debug)).unwrap(),
&debug_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
#builder_token_stream
builder.finish()
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,86 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::{
models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName},
TraitHandler,
};
use crate::supported_traits::Trait;
pub(crate) struct DebugUnionHandler;
impl TraitHandler for DebugUnionHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_unsafe: true,
enable_name: true,
enable_named_field: false,
enable_bound: false,
name: TypeName::Default,
named_field: false,
}
.build_from_debug_meta(meta)?;
if !type_attribute.has_unsafe {
return Err(super::panic::union_without_unsafe(meta));
}
let name = type_attribute.name.to_ident_by_ident(&ast.ident);
let mut builder_token_stream = proc_macro2::TokenStream::new();
if let Data::Union(data) = &ast.data {
for field in data.fields.named.iter() {
let _ = FieldAttributeBuilder {
enable_name: false,
enable_ignore: false,
enable_method: false,
name: FieldName::Default,
}
.build_from_attributes(&field.attrs, traits)?;
}
if let Some(name) = name {
builder_token_stream.extend(quote!(
let mut builder = f.debug_tuple(stringify!(#name));
let size = ::core::mem::size_of::<Self>();
let data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) };
builder.field(&data);
builder.finish()
));
} else {
builder_token_stream.extend(quote!(
let size = ::core::mem::size_of::<Self>();
let data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) };
::core::fmt::Debug::fmt(data, f)
));
}
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#builder_token_stream
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,38 @@
mod common;
mod debug_enum;
mod debug_struct;
mod debug_union;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct DebugHandler;
impl TraitHandler for DebugHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => debug_struct::DebugStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => {
debug_enum::DebugEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
debug_union::DebugUnionHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
}
}
}

View File

@@ -0,0 +1,224 @@
use syn::{punctuated::Punctuated, Attribute, Ident, Meta, Path, Token};
use crate::{
common::{
ident_bool::{
meta_2_bool_allow_path, meta_2_ident, meta_name_value_2_bool, meta_name_value_2_ident,
meta_name_value_2_ident_and_bool, IdentOrBool,
},
path::meta_2_path,
},
panic,
supported_traits::Trait,
};
#[derive(Debug, Clone)]
pub(crate) enum FieldName {
Default,
Custom(Ident),
}
pub(crate) struct FieldAttribute {
pub(crate) name: FieldName,
pub(crate) ignore: bool,
pub(crate) method: Option<Path>,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_name: bool,
pub(crate) enable_ignore: bool,
pub(crate) enable_method: bool,
pub(crate) name: FieldName,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_debug_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Debug"));
let mut name = self.name.clone();
let mut ignore = false;
let mut method = None;
let correct_usage_for_debug_attribute = {
let mut usage = vec![];
if self.enable_name {
usage.push(stringify!(#[educe(Debug = NewName)]));
usage.push(stringify!(#[educe(Debug(name(NewName)))]));
}
if self.enable_ignore {
usage.push(stringify!(#[educe(Debug = false)]));
usage.push(stringify!(#[educe(Debug(ignore))]));
}
if self.enable_method {
usage.push(stringify!(#[educe(Debug(method(path_to_method)))]));
}
usage
};
match meta {
Meta::Path(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
},
Meta::NameValue(name_value) => {
if self.enable_name {
if self.enable_ignore {
match meta_name_value_2_ident_and_bool(name_value)? {
IdentOrBool::Ident(ident) => {
name = FieldName::Custom(ident);
},
IdentOrBool::Bool(b) => {
ignore = !b;
},
}
} else {
name = FieldName::Custom(meta_name_value_2_ident(name_value)?);
}
} else if self.enable_ignore {
ignore = !meta_name_value_2_bool(name_value)?;
} else {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
}
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut name_is_set = false;
let mut ignore_is_set = false;
let mut method_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"name" | "rename" => {
if !self.enable_name {
return Ok(false);
}
let v = meta_2_ident(&meta)?;
if name_is_set {
return Err(panic::parameter_reset(ident));
}
name_is_set = true;
name = FieldName::Custom(v);
return Ok(true);
},
"ignore" => {
if !self.enable_ignore {
return Ok(false);
}
let v = meta_2_bool_allow_path(&meta)?;
if ignore_is_set {
return Err(panic::parameter_reset(ident));
}
ignore_is_set = true;
ignore = v;
return Ok(true);
},
"method" => {
if !self.enable_method {
return Ok(false);
}
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
}
}
},
}
Ok(FieldAttribute {
name,
ignore,
method,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Debug {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_debug_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
name: self.name.clone(),
ignore: false,
method: None,
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,264 @@
use proc_macro2::Ident;
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{
common::{
bound::Bound,
ident_bool::{meta_2_bool, meta_2_ident_and_bool, meta_name_value_2_ident, IdentOrBool},
unsafe_punctuated_meta::UnsafePunctuatedMeta,
},
panic, Trait,
};
#[derive(Debug, Clone)]
pub(crate) enum TypeName {
Disable,
Default,
Custom(Ident),
}
impl TypeName {
#[inline]
pub(crate) fn to_ident_by_ident<'a, 'b: 'a>(&'a self, ident: &'b Ident) -> Option<&'a Ident> {
match self {
Self::Disable => None,
Self::Default => Some(ident),
Self::Custom(ident) => Some(ident),
}
}
}
pub(crate) struct TypeAttribute {
pub(crate) has_unsafe: bool,
pub(crate) name: TypeName,
pub(crate) named_field: bool,
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_unsafe: bool,
pub(crate) enable_name: bool,
pub(crate) enable_named_field: bool,
pub(crate) enable_bound: bool,
pub(crate) name: TypeName,
pub(crate) named_field: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_debug_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Debug"));
let mut has_unsafe = false;
let mut name = self.name.clone();
let mut named_field = self.named_field;
let mut bound = Bound::Auto;
let correct_usage_for_debug_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Debug)]));
}
if self.enable_name {
if !self.enable_unsafe {
usage.push(stringify!(#[educe(Debug = NewName)]));
}
usage.push(stringify!(#[educe(Debug(name(NewName)))]));
if let TypeName::Disable = &name {
usage.push(stringify!(#[educe(Debug(name = true))]));
} else {
usage.push(stringify!(#[educe(Debug(name = false))]));
}
}
if self.enable_named_field {
if !self.named_field {
usage.push(stringify!(#[educe(Debug(named_field = true))]));
} else {
usage.push(stringify!(#[educe(Debug(named_field = false))]));
}
}
if self.enable_bound {
usage.push(stringify!(#[educe(Debug(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Debug(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
}
},
Meta::NameValue(name_value) => {
if !self.enable_name {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
}
name = TypeName::Custom(meta_name_value_2_ident(name_value)?);
},
Meta::List(list) => {
let result = if self.enable_unsafe {
let result: UnsafePunctuatedMeta = list.parse_args()?;
has_unsafe = result.has_unsafe;
result.list
} else {
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?
};
let mut name_is_set = false;
let mut named_field_is_set = false;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"name" | "rename" => {
if !self.enable_name {
return Ok(false);
}
let v = meta_2_ident_and_bool(&meta)?;
if name_is_set {
return Err(panic::parameter_reset(ident));
}
name_is_set = true;
name = match v {
IdentOrBool::Ident(ident) => TypeName::Custom(ident),
IdentOrBool::Bool(b) => {
if b {
TypeName::Default
} else {
TypeName::Disable
}
},
};
return Ok(true);
},
"named_field" => {
if !self.enable_named_field {
return Ok(false);
}
let v = meta_2_bool(&meta)?;
if named_field_is_set {
return Err(panic::parameter_reset(ident));
}
named_field_is_set = true;
named_field = v;
return Ok(true);
},
"bound" => {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_debug_attribute,
));
}
}
},
}
Ok(TypeAttribute {
has_unsafe,
name,
named_field,
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Debug {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_debug_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
has_unsafe: false,
name: self.name.clone(),
named_field: self.named_field,
bound: Bound::Auto,
}))
}
}

View File

@@ -0,0 +1,43 @@
use quote::ToTokens;
use syn::{spanned::Spanned, Ident, Meta, Variant};
#[inline]
pub(crate) fn unit_struct_need_name(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), "a unit struct needs to have a name")
}
#[inline]
pub(crate) fn unit_variant_need_name(variant: &Variant) -> syn::Error {
syn::Error::new(
variant.span(),
"a unit variant which doesn't use an enum name needs to have a name",
)
}
#[inline]
pub(crate) fn unit_enum_need_name(name: &Ident) -> syn::Error {
syn::Error::new(name.span(), "a unit enum needs to have a name")
}
#[inline]
pub(crate) fn union_without_unsafe(meta: &Meta) -> syn::Error {
let mut s = meta.into_token_stream().to_string().replace(" , ", ", ");
match s.len() {
5 => s.push_str("(unsafe)"),
7 => s.insert_str(6, "unsafe"),
_ => s.insert_str(6, "unsafe, "),
}
syn::Error::new(
meta.span(),
format!(
"a union's `Debug` implementation may expose uninitialized memory\n* It is \
recommended that, for a union where `Debug` is implemented, types that allow \
uninitialized memory should not be used in it.\n* If you can ensure that the union \
uses no such types, use `#[educe({s})]` to implement the `Debug` trait for it.\n* \
The `unsafe` keyword should be placed as the first parameter of the `Debug` \
attribute."
),
)
}

View File

@@ -0,0 +1,230 @@
use quote::quote;
use syn::{spanned::Spanned, Data, DeriveInput, Fields, Meta, Type, Variant};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::Trait;
pub(crate) struct DefaultEnumHandler;
impl TraitHandler for DefaultEnumHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_new: true,
enable_expression: true,
enable_bound: true,
}
.build_from_default_meta(meta)?;
let mut default_types: Vec<&Type> = Vec::new();
let mut default_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
if let Some(expression) = type_attribute.expression {
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false,
enable_new: false,
enable_expression: false,
enable_bound: false,
}
.build_from_attributes(&variant.attrs, traits)?;
ensure_fields_no_attribute(&variant.fields, traits)?;
}
default_token_stream.extend(quote!(#expression));
} else {
let variant = {
let variants = &data.variants;
if variants.len() == 1 {
let variant = &variants[0];
let _ = TypeAttributeBuilder {
enable_flag: true,
enable_new: false,
enable_expression: false,
enable_bound: false,
}
.build_from_attributes(&variant.attrs, traits)?;
variant
} else {
let mut default_variant: Option<&Variant> = None;
for variant in variants {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_new: false,
enable_expression: false,
enable_bound: false,
}
.build_from_attributes(&variant.attrs, traits)?;
if type_attribute.flag {
if default_variant.is_some() {
return Err(super::panic::multiple_default_variants(
type_attribute.span,
));
}
default_variant = Some(variant);
} else {
ensure_fields_no_attribute(&variant.fields, traits)?;
}
}
if let Some(default_variant) = default_variant {
default_variant
} else {
return Err(super::panic::no_default_variant(meta.span()));
}
}
};
let variant_ident = &variant.ident;
match &variant.fields {
Fields::Unit => {
default_token_stream.extend(quote!(Self::#variant_ident));
},
Fields::Named(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
for field in variant.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_flag: false,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
let field_name = field.ident.as_ref().unwrap();
if let Some(expression) = field_attribute.expression {
fields_token_stream.extend(quote! {
#field_name: #expression,
});
} else {
let ty = &field.ty;
default_types.push(ty);
fields_token_stream.extend(quote! {
#field_name: <#ty as ::core::default::Default>::default(),
});
}
}
default_token_stream.extend(quote! {
Self::#variant_ident {
#fields_token_stream
}
});
},
Fields::Unnamed(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
for field in variant.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_flag: false,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
if let Some(expression) = field_attribute.expression {
fields_token_stream.extend(quote!(#expression,));
} else {
let ty = &field.ty;
default_types.push(ty);
fields_token_stream
.extend(quote!(<#ty as ::core::default::Default>::default(),));
}
}
default_token_stream
.extend(quote!(Self::#variant_ident ( #fields_token_stream )));
},
}
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::default::Default)).unwrap(),
&default_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
#[inline]
fn default() -> Self {
#default_token_stream
}
}
});
if type_attribute.new {
token_stream.extend(quote! {
impl #impl_generics #ident #ty_generics #where_clause {
/// Returns the "default value" for a type.
#[inline]
pub fn new() -> Self {
<Self as ::core::default::Default>::default()
}
}
});
}
Ok(())
}
}
fn ensure_fields_no_attribute(fields: &Fields, traits: &[Trait]) -> syn::Result<()> {
match fields {
Fields::Unit => (),
Fields::Named(fields) => {
for field in fields.named.iter() {
let _ = FieldAttributeBuilder {
enable_flag: false,
enable_expression: false,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
}
},
Fields::Unnamed(fields) => {
for field in fields.unnamed.iter() {
let _ = FieldAttributeBuilder {
enable_flag: false,
enable_expression: false,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
}
},
}
Ok(())
}

View File

@@ -0,0 +1,149 @@
use quote::quote;
use syn::{Data, DeriveInput, Fields, Meta, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::Trait;
pub(crate) struct DefaultStructHandler;
impl TraitHandler for DefaultStructHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_new: true,
enable_expression: true,
enable_bound: true,
}
.build_from_default_meta(meta)?;
let mut default_types: Vec<&Type> = Vec::new();
let mut default_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
if let Some(expression) = type_attribute.expression {
for field in data.fields.iter() {
let _ = FieldAttributeBuilder {
enable_flag: false,
enable_expression: false,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
}
default_token_stream.extend(quote!(#expression));
} else {
match &data.fields {
Fields::Unit => {
default_token_stream.extend(quote!(Self));
},
Fields::Named(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
for field in data.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_flag: false,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
let field_name = field.ident.as_ref().unwrap();
if let Some(expression) = field_attribute.expression {
fields_token_stream.extend(quote! {
#field_name: #expression,
});
} else {
let ty = &field.ty;
default_types.push(ty);
fields_token_stream.extend(quote! {
#field_name: <#ty as ::core::default::Default>::default(),
});
}
}
default_token_stream.extend(quote! {
Self {
#fields_token_stream
}
});
},
Fields::Unnamed(_) => {
let mut fields_token_stream = proc_macro2::TokenStream::new();
for field in data.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_flag: false,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
if let Some(expression) = field_attribute.expression {
fields_token_stream.extend(quote!(#expression,));
} else {
let ty = &field.ty;
default_types.push(ty);
fields_token_stream
.extend(quote!(<#ty as ::core::default::Default>::default(),));
}
}
default_token_stream.extend(quote!(Self ( #fields_token_stream )));
},
}
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::default::Default)).unwrap(),
&default_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
#[inline]
fn default() -> Self {
#default_token_stream
}
}
});
if type_attribute.new {
token_stream.extend(quote! {
impl #impl_generics #ident #ty_generics #where_clause {
/// Returns the "default value" for a type.
#[inline]
pub fn new() -> Self {
<Self as ::core::default::Default>::default()
}
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,153 @@
use quote::quote;
use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta, Type};
use super::{
models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::Trait;
pub(crate) struct DefaultUnionHandler;
impl TraitHandler for DefaultUnionHandler {
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_new: true,
enable_expression: true,
enable_bound: true,
}
.build_from_default_meta(meta)?;
let mut default_types: Vec<&Type> = Vec::new();
let mut default_token_stream = proc_macro2::TokenStream::new();
if let Data::Union(data) = &ast.data {
if let Some(expression) = type_attribute.expression {
for field in data.fields.named.iter() {
let _ = FieldAttributeBuilder {
enable_flag: false,
enable_expression: false,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
}
default_token_stream.extend(quote!(#expression));
} else {
let (field, field_attribute) =
{
let fields = &data.fields.named;
if fields.len() == 1 {
let field = &fields[0];
let field_attribute = FieldAttributeBuilder {
enable_flag: true,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
(field, field_attribute)
} else {
let mut default_field: Option<(&Field, FieldAttribute)> = None;
for field in fields {
let field_attribute = FieldAttributeBuilder {
enable_flag: true,
enable_expression: true,
}
.build_from_attributes(&field.attrs, traits, &field.ty)?;
if field_attribute.flag || field_attribute.expression.is_some() {
if default_field.is_some() {
return Err(super::panic::multiple_default_fields(
field_attribute.span,
));
}
default_field = Some((field, field_attribute));
}
}
if let Some(default_field) = default_field {
default_field
} else {
return Err(super::panic::no_default_field(meta.span()));
}
}
};
let mut fields_token_stream = proc_macro2::TokenStream::new();
let field_name = field.ident.as_ref().unwrap();
if let Some(expression) = field_attribute.expression {
fields_token_stream.extend(quote! {
#field_name: #expression,
});
} else {
let ty = &field.ty;
default_types.push(ty);
fields_token_stream.extend(quote! {
#field_name: <#ty as ::core::default::Default>::default(),
});
}
default_token_stream.extend(quote! {
Self {
#fields_token_stream
}
});
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::default::Default)).unwrap(),
&default_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
#[inline]
fn default() -> Self {
#default_token_stream
}
}
});
if type_attribute.new {
token_stream.extend(quote! {
impl #impl_generics #ident #ty_generics #where_clause {
/// Returns the "default value" for a type.
#[inline]
pub fn new() -> Self {
<Self as ::core::default::Default>::default()
}
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,43 @@
mod default_enum;
mod default_struct;
mod default_union;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct DefaultHandler;
impl TraitHandler for DefaultHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => default_struct::DefaultStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => default_enum::DefaultEnumHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Union(_) => default_union::DefaultUnionHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
}
}
}

View File

@@ -0,0 +1,166 @@
use proc_macro2::Span;
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Expr, Meta, Token, Type};
use crate::{
common::expr::{auto_adjust_expr, meta_2_expr},
panic,
supported_traits::Trait,
};
pub(crate) struct FieldAttribute {
pub(crate) flag: bool,
pub(crate) expression: Option<Expr>,
pub(crate) span: Span,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_expression: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_default_meta(
&self,
meta: &Meta,
ty: &Type,
) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Default"));
let mut flag = false;
let mut expression = None;
let correct_usage_for_default_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Default)]));
}
if self.enable_expression {
usage.push(stringify!(#[educe(Default = expr)]));
usage.push(stringify!(#[educe(Default(expression = expr))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
}
flag = true;
},
Meta::NameValue(name_value) => {
if !self.enable_expression {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
}
expression = Some(auto_adjust_expr(name_value.value.clone(), Some(ty)));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut expression_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"expression" | "expr" => {
if !self.enable_expression {
return Ok(false);
}
let v = meta_2_expr(&meta)?;
if expression_is_set {
return Err(panic::parameter_reset(ident));
}
expression_is_set = true;
expression = Some(auto_adjust_expr(v, Some(ty)));
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
}
}
},
}
Ok(FieldAttribute {
flag,
expression,
span: meta.span(),
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
ty: &Type,
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Default {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_default_meta(&meta, ty)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
flag: false,
expression: None,
span: Span::call_site(),
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,214 @@
use proc_macro2::Span;
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Expr, Meta, Token};
use crate::{
common::{
bound::Bound,
expr::{auto_adjust_expr, meta_2_expr},
ident_bool::meta_2_bool_allow_path,
},
panic, Trait,
};
pub(crate) struct TypeAttribute {
pub(crate) flag: bool,
pub(crate) new: bool,
pub(crate) expression: Option<Expr>,
pub(crate) bound: Bound,
pub(crate) span: Span,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_new: bool,
pub(crate) enable_expression: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_default_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Default"));
let mut flag = false;
let mut new = false;
let mut expression = None;
let mut bound = Bound::Auto;
let correct_usage_for_default_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Default)]));
}
if self.enable_new {
usage.push(stringify!(#[educe(Default(new))]));
}
if self.enable_expression {
usage.push(stringify!(#[educe(Default(expression = expr))]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Default(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Default(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
}
flag = true;
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut new_is_set = false;
let mut expression_is_set = false;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"new" => {
if !self.enable_new {
return Ok(false);
}
let v = meta_2_bool_allow_path(&meta)?;
if new_is_set {
return Err(panic::parameter_reset(ident));
}
new_is_set = true;
new = v;
return Ok(true);
},
"expression" | "expr" => {
if !self.enable_expression {
return Ok(false);
}
let v = meta_2_expr(&meta)?;
if expression_is_set {
return Err(panic::parameter_reset(ident));
}
expression_is_set = true;
expression = Some(auto_adjust_expr(v, None));
return Ok(true);
},
"bound" => {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_default_attribute,
));
}
}
},
}
Ok(TypeAttribute {
flag,
new,
expression,
bound,
span: meta.span(),
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Default {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_default_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
flag: false,
new: false,
expression: None,
bound: Bound::Auto,
span: Span::call_site(),
}))
}
}

View File

@@ -0,0 +1,21 @@
use proc_macro2::Span;
#[inline]
pub(crate) fn multiple_default_fields(span: Span) -> syn::Error {
syn::Error::new(span, "multiple default fields are set")
}
#[inline]
pub(crate) fn no_default_field(span: Span) -> syn::Error {
syn::Error::new(span, "there is no field set as default")
}
#[inline]
pub(crate) fn multiple_default_variants(span: Span) -> syn::Error {
syn::Error::new(span, "multiple default variants are set")
}
#[inline]
pub(crate) fn no_default_variant(span: Span) -> syn::Error {
syn::Error::new(span, "there is no variant set as default")
}

View File

@@ -0,0 +1,144 @@
use quote::{format_ident, quote};
use syn::{spanned::Spanned, Data, DeriveInput, Field, Fields, Ident, Meta, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::r#type::dereference, panic, supported_traits::Trait};
pub(crate) struct DerefEnumHandler;
impl TraitHandler for DerefEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let _ = TypeAttributeBuilder {
enable_flag: true
}
.build_from_deref_meta(meta)?;
let mut target_token_stream = proc_macro2::TokenStream::new();
let mut arms_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
type Variants<'a> = Vec<(&'a Ident, bool, usize, Ident, &'a Type)>;
let mut variants: Variants = Vec::new();
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false
}
.build_from_attributes(&variant.attrs, traits)?;
if let Fields::Unit = &variant.fields {
return Err(panic::trait_not_support_unit_variant(
meta.path().get_ident().unwrap(),
variant,
));
}
let fields = &variant.fields;
let (index, field) = if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let _ = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
(0usize, field)
} else {
let mut deref_field: Option<(usize, &Field)> = None;
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.flag {
if deref_field.is_some() {
return Err(super::panic::multiple_deref_fields_of_variant(
field_attribute.span,
variant,
));
}
deref_field = Some((index, field));
}
}
if let Some(deref_field) = deref_field {
deref_field
} else {
return Err(super::panic::no_deref_field_of_variant(meta.span(), variant));
}
};
let (field_name, is_tuple): (Ident, bool) = match field.ident.as_ref() {
Some(ident) => (ident.clone(), false),
None => (format_ident!("_{}", index), true),
};
variants.push((&variant.ident, is_tuple, index, field_name, &field.ty));
}
if variants.is_empty() {
return Err(super::panic::no_deref_field(meta.span()));
}
let ty = variants[0].4;
let dereference_ty = dereference(ty);
target_token_stream.extend(quote!(#dereference_ty));
for (variant_ident, is_tuple, index, field_name, _) in variants {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
if is_tuple {
for _ in 0..index {
pattern_token_stream.extend(quote!(_,));
}
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident ( #pattern_token_stream ) => #field_name, ),
);
} else {
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident { #pattern_token_stream } => #field_name, ),
);
}
}
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::ops::Deref for #ident #ty_generics #where_clause {
type Target = #target_token_stream;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
#arms_token_stream
}
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,103 @@
use quote::quote;
use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{
common::{ident_index::IdentOrIndex, r#type::dereference_changed},
Trait,
};
pub(crate) struct DerefStructHandler;
impl TraitHandler for DerefStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let _ = TypeAttributeBuilder {
enable_flag: true
}
.build_from_deref_meta(meta)?;
let mut target_token_stream = proc_macro2::TokenStream::new();
let mut deref_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
let (index, field) = {
let fields = &data.fields;
if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let _ = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
(0usize, field)
} else {
let mut deref_field: Option<(usize, &Field)> = None;
for (index, field) in fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.flag {
if deref_field.is_some() {
return Err(super::panic::multiple_deref_fields(
field_attribute.span,
));
}
deref_field = Some((index, field));
}
}
if let Some(deref_field) = deref_field {
deref_field
} else {
return Err(super::panic::no_deref_field(meta.span()));
}
}
};
let ty = &field.ty;
let (dereference_ty, is_ref) = dereference_changed(ty);
target_token_stream.extend(quote!(#dereference_ty));
let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
deref_token_stream.extend(if is_ref {
quote! (self.#field_name)
} else {
quote! (&self.#field_name)
});
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::ops::Deref for #ident #ty_generics #where_clause {
type Target = #target_token_stream;
#[inline]
fn deref(&self) -> &Self::Target {
#deref_token_stream
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,36 @@
mod deref_enum;
mod deref_struct;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct DerefHandler;
impl TraitHandler for DerefHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => deref_struct::DerefStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => {
deref_enum::DerefEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
Err(crate::panic::trait_not_support_union(meta.path().get_ident().unwrap()))
},
}
}
}

View File

@@ -0,0 +1,94 @@
use proc_macro2::Span;
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Meta, Token};
use crate::{panic, supported_traits::Trait};
pub(crate) struct FieldAttribute {
pub(crate) flag: bool,
pub(crate) span: Span,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_flag: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_deref_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Deref"));
let correct_usage_for_deref_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Deref)]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_attribute,
));
}
},
Meta::NameValue(_) | Meta::List(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_attribute,
));
},
}
Ok(FieldAttribute {
flag: true, span: meta.span()
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Deref {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_deref_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
flag: false, span: Span::call_site()
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,87 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{panic, Trait};
pub(crate) struct TypeAttribute;
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_deref_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Deref"));
let correct_usage_for_deref_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Deref)]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_attribute,
));
}
},
Meta::NameValue(_) | Meta::List(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_attribute,
));
},
}
Ok(TypeAttribute)
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Deref {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_deref_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute))
}
}

View File

@@ -0,0 +1,31 @@
use proc_macro2::Span;
use syn::Variant;
#[inline]
pub(crate) fn multiple_deref_fields(span: Span) -> syn::Error {
syn::Error::new(span, "multiple fields are set for `Deref`")
}
#[inline]
pub(crate) fn multiple_deref_fields_of_variant(span: Span, variant: &Variant) -> syn::Error {
syn::Error::new(
span,
format!("multiple fields of the `{}` variant are set for `Deref`", variant.ident),
)
}
#[inline]
pub(crate) fn no_deref_field(span: Span) -> syn::Error {
syn::Error::new(span, "there is no field which is assigned for `Deref`")
}
#[inline]
pub(crate) fn no_deref_field_of_variant(span: Span, variant: &Variant) -> syn::Error {
syn::Error::new(
span,
format!(
"there is no field for the `{}` variant which is assigned for `Deref`",
variant.ident
),
)
}

View File

@@ -0,0 +1,139 @@
use quote::{format_ident, quote};
use syn::{spanned::Spanned, Data, DeriveInput, Field, Fields, Ident, Meta};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{panic, supported_traits::Trait};
pub(crate) struct DerefMutEnumHandler;
impl TraitHandler for DerefMutEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let _ = TypeAttributeBuilder {
enable_flag: true
}
.build_from_deref_mut_meta(meta)?;
let mut arms_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
type Variants<'a> = Vec<(&'a Ident, bool, usize, Ident)>;
let mut variants: Variants = Vec::new();
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false
}
.build_from_attributes(&variant.attrs, traits)?;
if let Fields::Unit = &variant.fields {
return Err(panic::trait_not_support_unit_variant(
meta.path().get_ident().unwrap(),
variant,
));
}
let fields = &variant.fields;
let (index, field) = if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let _ = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
(0usize, field)
} else {
let mut deref_field: Option<(usize, &Field)> = None;
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.flag {
if deref_field.is_some() {
return Err(super::panic::multiple_deref_mut_fields_of_variant(
field_attribute.span,
variant,
));
}
deref_field = Some((index, field));
}
}
if let Some(deref_field) = deref_field {
deref_field
} else {
return Err(super::panic::no_deref_mut_field_of_variant(
meta.span(),
variant,
));
}
};
let (field_name, is_tuple): (Ident, bool) = match field.ident.as_ref() {
Some(ident) => (ident.clone(), false),
None => (format_ident!("_{}", index), true),
};
variants.push((&variant.ident, is_tuple, index, field_name));
}
if variants.is_empty() {
return Err(super::panic::no_deref_mut_field(meta.span()));
}
for (variant_ident, is_tuple, index, field_name) in variants {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
if is_tuple {
for _ in 0..index {
pattern_token_stream.extend(quote!(_,));
}
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident ( #pattern_token_stream ) => #field_name, ),
);
} else {
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident { #pattern_token_stream } => #field_name, ),
);
}
}
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::ops::DerefMut for #ident #ty_generics #where_clause {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
#arms_token_stream
}
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,92 @@
use quote::quote;
use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub(crate) struct DerefMutStructHandler;
impl TraitHandler for DerefMutStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let _ = TypeAttributeBuilder {
enable_flag: true
}
.build_from_deref_mut_meta(meta)?;
let mut deref_mut_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
let (index, field) = {
let fields = &data.fields;
if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let _ = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
(0usize, field)
} else {
let mut deref_field: Option<(usize, &Field)> = None;
for (index, field) in fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_flag: true
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.flag {
if deref_field.is_some() {
return Err(super::panic::multiple_deref_mut_fields(
field_attribute.span,
));
}
deref_field = Some((index, field));
}
}
if let Some(deref_field) = deref_field {
deref_field
} else {
return Err(super::panic::no_deref_mut_field(meta.span()));
}
}
};
let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
deref_mut_token_stream.extend(if let Type::Reference(_) = &field.ty {
quote! (self.#field_name)
} else {
quote! (&mut self.#field_name)
});
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::ops::DerefMut for #ident #ty_generics #where_clause {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
#deref_mut_token_stream
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,39 @@
mod deref_mut_enum;
mod deref_mut_struct;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct DerefMutHandler;
impl TraitHandler for DerefMutHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => deref_mut_struct::DerefMutStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => deref_mut_enum::DerefMutEnumHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Union(_) => {
Err(crate::panic::trait_not_support_union(meta.path().get_ident().unwrap()))
},
}
}
}

View File

@@ -0,0 +1,94 @@
use proc_macro2::Span;
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Meta, Token};
use crate::{panic, supported_traits::Trait};
pub(crate) struct FieldAttribute {
pub(crate) flag: bool,
pub(crate) span: Span,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_flag: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_deref_mut_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("DerefMut"));
let correct_usage_for_deref_mut_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(DerefMut)]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_mut_attribute,
));
}
},
Meta::NameValue(_) | Meta::List(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_mut_attribute,
));
},
}
Ok(FieldAttribute {
flag: true, span: meta.span()
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::DerefMut {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_deref_mut_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
flag: false, span: Span::call_site()
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,87 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{panic, Trait};
pub(crate) struct TypeAttribute;
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_deref_mut_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("DerefMut"));
let correct_usage_for_deref_mut_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(DerefMut)]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_mut_attribute,
));
}
},
Meta::NameValue(_) | Meta::List(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_deref_mut_attribute,
));
},
}
Ok(TypeAttribute)
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::DerefMut {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_deref_mut_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute))
}
}

View File

@@ -0,0 +1,31 @@
use proc_macro2::Span;
use syn::Variant;
#[inline]
pub(crate) fn multiple_deref_mut_fields(span: Span) -> syn::Error {
syn::Error::new(span, "multiple fields are set for `DerefMut`")
}
#[inline]
pub(crate) fn multiple_deref_mut_fields_of_variant(span: Span, variant: &Variant) -> syn::Error {
syn::Error::new(
span,
format!("multiple fields of the `{}` variant are set for `DerefMut`", variant.ident),
)
}
#[inline]
pub(crate) fn no_deref_mut_field(span: Span) -> syn::Error {
syn::Error::new(span, "there is no field which is assigned for `DerefMut`")
}
#[inline]
pub(crate) fn no_deref_mut_field_of_variant(span: Span, variant: &Variant) -> syn::Error {
syn::Error::new(
span,
format!(
"there is no field for the `{}` variant which is assigned for `DerefMut`",
variant.ident
),
)
}

View File

@@ -0,0 +1,106 @@
mod models;
use models::{FieldAttributeBuilder, TypeAttributeBuilder};
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct EqHandler;
impl TraitHandler for EqHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
#[cfg(feature = "PartialEq")]
let contains_partial_eq = traits.contains(&Trait::PartialEq);
#[cfg(not(feature = "PartialEq"))]
let contains_partial_eq = false;
let type_attribute = TypeAttributeBuilder {
enable_flag: true,
enable_bound: !contains_partial_eq,
}
.build_from_eq_meta(meta)?;
let mut field_types = vec![];
// if `contains_partial_eq` is true, the implementation is handled by the `PartialEq` attribute, and field attributes is also handled by the `PartialEq` attribute
if !contains_partial_eq {
match &ast.data {
Data::Struct(data) => {
for field in data.fields.iter() {
field_types.push(&field.ty);
let _ =
FieldAttributeBuilder.build_from_attributes(&field.attrs, traits)?;
}
},
Data::Enum(data) => {
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false, enable_bound: false
}
.build_from_attributes(&variant.attrs, traits)?;
for field in variant.fields.iter() {
field_types.push(&field.ty);
let _ = FieldAttributeBuilder
.build_from_attributes(&field.attrs, traits)?;
}
}
},
Data::Union(data) => {
for field in data.fields.named.iter() {
field_types.push(&field.ty);
let _ =
FieldAttributeBuilder.build_from_attributes(&field.attrs, traits)?;
}
},
}
let ident = &ast.ident;
/*
#[derive(PartialEq)]
struct B<T> {
f1: PhantomData<T>,
}
impl<T> Eq for B<T> {
}
// The above code will throw a compile error because T have to be bound to `PartialEq`. However, it seems not to be necessary logically.
*/
let bound =
type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::cmp::PartialEq)).unwrap(),
&field_types,
&[quote! {::core::cmp::PartialEq}],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Eq for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,57 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{panic, supported_traits::Trait};
pub(crate) struct FieldAttribute;
pub(crate) struct FieldAttributeBuilder;
impl FieldAttributeBuilder {
pub(crate) fn build_from_eq_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Eq"));
return Err(panic::attribute_incorrect_place(meta.path().get_ident().unwrap()));
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Eq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_eq_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,140 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{common::bound::Bound, panic, Trait};
pub(crate) struct TypeAttribute {
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_eq_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Eq"));
let mut bound = Bound::Auto;
let correct_usage_for_copy_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Eq)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Eq(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Eq(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_copy_attribute,
));
}
}
},
}
Ok(TypeAttribute {
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Eq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_eq_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,169 @@
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, Fields, Meta, Path, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::Trait;
pub(crate) struct HashEnumHandler;
impl TraitHandler for HashEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: false, enable_bound: true
}
.build_from_hash_meta(meta)?;
let mut hash_types: Vec<&Type> = Vec::new();
let mut hash_token_stream = proc_macro2::TokenStream::new();
let mut arms_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
for (variant_index, variant) in data.variants.iter().enumerate() {
let _ = TypeAttributeBuilder {
enable_flag: false,
enable_unsafe: false,
enable_bound: false,
}
.build_from_attributes(&variant.attrs, traits)?;
let variant_ident = &variant.ident;
let built_in_hash: Path = syn::parse2(quote!(::core::hash::Hash::hash)).unwrap();
match &variant.fields {
Fields::Unit => {
arms_token_stream.extend(quote! {
Self::#variant_ident => {
::core::hash::Hash::hash(&#variant_index, state);
}
});
},
Fields::Named(_) => {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
for field in variant.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_real = field.ident.as_ref().unwrap();
let field_name_var = format_ident!("v_{}", field_name_real);
if field_attribute.ignore {
pattern_token_stream.extend(quote!(#field_name_real: _,));
continue;
}
pattern_token_stream.extend(quote!(#field_name_real: #field_name_var,));
let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
hash_types.push(&field.ty);
&built_in_hash
});
block_token_stream.extend(quote!( #hash(#field_name_var, state); ));
}
arms_token_stream.extend(quote! {
Self::#variant_ident { #pattern_token_stream } => {
::core::hash::Hash::hash(&#variant_index, state);
#block_token_stream
}
});
},
Fields::Unnamed(_) => {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_var = format_ident!("_{}", index);
if field_attribute.ignore {
pattern_token_stream.extend(quote!(_,));
continue;
}
pattern_token_stream.extend(quote!(#field_name_var,));
let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
hash_types.push(&field.ty);
&built_in_hash
});
block_token_stream.extend(quote!( #hash(#field_name_var, state); ));
}
arms_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => {
::core::hash::Hash::hash(&#variant_index, state);
#block_token_stream
}
});
},
}
}
}
if !arms_token_stream.is_empty() {
hash_token_stream.extend(quote! {
match self {
#arms_token_stream
}
});
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::hash::Hash)).unwrap(),
&hash_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause {
#[inline]
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
#hash_token_stream
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,88 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta, Path, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub(crate) struct HashStructHandler;
impl TraitHandler for HashStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: false, enable_bound: true
}
.build_from_hash_meta(meta)?;
let mut hash_types: Vec<&Type> = Vec::new();
let mut hash_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
let built_in_hash: Path = syn::parse2(quote!(::core::hash::Hash::hash)).unwrap();
for (index, field) in data.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
continue;
}
let field_name = if let Some(ident) = field.ident.as_ref() {
IdentOrIndex::from(ident)
} else {
IdentOrIndex::from(index)
};
let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
hash_types.push(&field.ty);
&built_in_hash
});
hash_token_stream.extend(quote!( #hash(&self.#field_name, state); ));
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::hash::Hash)).unwrap(),
&hash_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause {
#[inline]
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
#hash_token_stream
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,54 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::models::{FieldAttributeBuilder, TypeAttributeBuilder};
use crate::{supported_traits::Trait, trait_handlers::TraitHandler};
pub(crate) struct HashUnionHandler;
impl TraitHandler for HashUnionHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: true, enable_bound: false
}
.build_from_hash_meta(meta)?;
if !type_attribute.has_unsafe {
return Err(super::panic::union_without_unsafe(meta));
}
if let Data::Union(data) = &ast.data {
for field in data.fields.named.iter() {
let _ = FieldAttributeBuilder {
enable_ignore: false, enable_method: false
}
.build_from_attributes(&field.attrs, traits)?;
}
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause {
#[inline]
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
let size = ::core::mem::size_of::<Self>();
let data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) };
::core::hash::Hash::hash(data, state)
}
}
});
Ok(())
}
}

View File

@@ -0,0 +1,34 @@
mod hash_enum;
mod hash_struct;
mod hash_union;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct HashHandler;
impl TraitHandler for HashHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => {
hash_struct::HashStructHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Enum(_) => {
hash_enum::HashEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
hash_union::HashUnionHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
}
}
}

View File

@@ -0,0 +1,172 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Path, Token};
use crate::{
common::{
ident_bool::{meta_2_bool_allow_path, meta_name_value_2_bool},
path::meta_2_path,
},
panic,
supported_traits::Trait,
};
pub(crate) struct FieldAttribute {
pub(crate) ignore: bool,
pub(crate) method: Option<Path>,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_ignore: bool,
pub(crate) enable_method: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_hash_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Hash"));
let mut ignore = false;
let mut method = None;
let correct_usage_for_partial_eq_attribute = {
let mut usage = vec![];
if self.enable_ignore {
usage.push(stringify!(#[educe(Hash = false)]));
usage.push(stringify!(#[educe(Hash(ignore))]));
}
if self.enable_method {
usage.push(stringify!(#[educe(Hash(method(path_to_method)))]));
}
usage
};
match meta {
Meta::Path(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
},
Meta::NameValue(name_value) => {
if self.enable_ignore {
ignore = !meta_name_value_2_bool(name_value)?;
} else {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut ignore_is_set = false;
let mut method_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"ignore" => {
if !self.enable_ignore {
return Ok(false);
}
let v = meta_2_bool_allow_path(&meta)?;
if ignore_is_set {
return Err(panic::parameter_reset(ident));
}
ignore_is_set = true;
ignore = v;
return Ok(true);
},
"method" => {
if !self.enable_method {
return Ok(false);
}
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
}
},
}
Ok(FieldAttribute {
ignore,
method,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Hash {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_hash_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
ignore: false, method: None
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,154 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{
common::{bound::Bound, unsafe_punctuated_meta::UnsafePunctuatedMeta},
panic, Trait,
};
pub(crate) struct TypeAttribute {
pub(crate) has_unsafe: bool,
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_unsafe: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_hash_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Hash"));
let mut has_unsafe = false;
let mut bound = Bound::Auto;
let correct_usage_for_hash_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Hash)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Hash(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Hash(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_hash_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_hash_attribute,
));
},
Meta::List(list) => {
let result = if self.enable_unsafe {
let result: UnsafePunctuatedMeta = list.parse_args()?;
has_unsafe = result.has_unsafe;
result.list
} else {
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?
};
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_hash_attribute,
));
}
}
},
}
Ok(TypeAttribute {
has_unsafe,
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Hash {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_hash_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
has_unsafe: false, bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,22 @@
use quote::ToTokens;
use syn::{spanned::Spanned, Meta};
#[inline]
pub(crate) fn union_without_unsafe(meta: &Meta) -> syn::Error {
let mut s = meta.into_token_stream().to_string();
match s.len() {
4 => s.push_str("(unsafe)"),
6 => s.insert_str(10, "unsafe"),
_ => unreachable!(),
}
syn::Error::new(
meta.span(),
format!(
"a union's `Hash` implementation is not precise, because it ignores the type of \
fields\n* If your union doesn't care about that, use `#[educe({s})]` to implement \
the `Hash` trait for it."
),
)
}

View File

@@ -0,0 +1,17 @@
use quote::quote_spanned;
use syn::{spanned::Spanned, Type};
use crate::common::{r#type::dereference_changed, tools::HashType};
#[inline]
pub(crate) fn to_hash_type(ty: &Type) -> HashType {
let (ty, is_ref) = dereference_changed(ty);
let ty = if is_ref {
syn::parse2(quote_spanned!( ty.span() => &'static #ty )).unwrap()
} else {
ty.clone()
};
HashType::from(ty)
}

View File

@@ -0,0 +1,224 @@
use std::collections::HashMap;
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, Field, Fields, Ident, Meta, Path, Type};
use super::{
models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandlerMultiple,
};
use crate::{panic, Trait};
pub(crate) struct IntoEnumHandler;
impl TraitHandlerMultiple for IntoEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &[Meta],
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_types: true
}
.build_from_into_meta(meta)?;
if let Data::Enum(data) = &ast.data {
let field_attributes: Vec<HashMap<usize, FieldAttribute>> = {
let mut map = Vec::new();
for variant in data.variants.iter() {
let mut field_map = HashMap::new();
let _ = TypeAttributeBuilder {
enable_types: false
}
.build_from_attributes(&variant.attrs, traits)?;
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_types: true
}
.build_from_attributes(&field.attrs, traits)?;
for ty in field_attribute.types.keys() {
if !type_attribute.types.contains_key(ty) {
return Err(super::panic::no_into_impl(ty));
}
}
field_map.insert(index, field_attribute);
}
map.push(field_map);
}
map
};
for (target_ty, bound) in type_attribute.types {
let mut into_types: Vec<&Type> = Vec::new();
let mut arms_token_stream = proc_macro2::TokenStream::new();
type Variants<'a> =
Vec<(&'a Ident, bool, usize, Ident, &'a Type, Option<&'a Path>)>;
let mut variants: Variants = Vec::new();
for (variant, field_attributes) in data.variants.iter().zip(field_attributes.iter())
{
if let Fields::Unit = &variant.fields {
return Err(panic::trait_not_support_unit_variant(
meta[0].path().get_ident().unwrap(),
variant,
));
}
let (index, field, method) = {
let fields = &variant.fields;
if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let method = if let Some(field_attribute) = field_attributes.get(&0) {
if let Some(method) = field_attribute.types.get(&target_ty) {
method.as_ref()
} else {
None
}
} else {
None
};
(0usize, field, method)
} else {
let mut into_field: Option<(usize, &Field, Option<&Path>)> = None;
for (index, field) in fields.iter().enumerate() {
if let Some(field_attribute) = field_attributes.get(&index) {
if let Some((key, method)) =
field_attribute.types.get_key_value(&target_ty)
{
if into_field.is_some() {
return Err(super::panic::multiple_into_fields(key));
}
into_field = Some((index, field, method.as_ref()));
}
}
}
if into_field.is_none() {
// search the same type
for (index, field) in fields.iter().enumerate() {
let field_ty = super::common::to_hash_type(&field.ty);
if target_ty.eq(&field_ty) {
if into_field.is_some() {
// multiple candidates
into_field = None;
break;
}
into_field = Some((index, field, None));
}
}
}
if let Some(into_field) = into_field {
into_field
} else {
return Err(super::panic::no_into_field(&target_ty));
}
}
};
let (field_name, is_tuple): (Ident, bool) = match field.ident.as_ref() {
Some(ident) => (ident.clone(), false),
None => (format_ident!("_{}", index), true),
};
variants.push((&variant.ident, is_tuple, index, field_name, &field.ty, method));
}
if variants.is_empty() {
return Err(super::panic::no_into_field(&target_ty));
}
for (variant_ident, is_tuple, index, field_name, ty, method) in variants {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut body_token_stream = proc_macro2::TokenStream::new();
if let Some(method) = method {
body_token_stream.extend(quote!( #method(#field_name) ));
} else {
let field_ty = super::common::to_hash_type(ty);
if target_ty.eq(&field_ty) {
body_token_stream.extend(quote!( #field_name ));
} else {
into_types.push(ty);
body_token_stream
.extend(quote!( ::core::convert::Into::into(#field_name) ));
}
}
if is_tuple {
for _ in 0..index {
pattern_token_stream.extend(quote!(_,));
}
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident ( #pattern_token_stream ) => #body_token_stream, ),
);
} else {
pattern_token_stream.extend(quote!( #field_name, .. ));
arms_token_stream.extend(
quote!( Self::#variant_ident { #pattern_token_stream } => #body_token_stream, ),
);
}
}
let ident = &ast.ident;
let bound = bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::convert::Into<#target_ty>)).unwrap(),
&into_types,
&[],
);
// clone generics in order to not to affect other Into<T> implementations
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, _) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::convert::Into<#target_ty> for #ident #ty_generics #where_clause {
#[inline]
fn into(self) -> #target_ty {
match self {
#arms_token_stream
}
}
}
});
}
}
Ok(())
}
}

View File

@@ -0,0 +1,168 @@
use std::collections::HashMap;
use quote::quote;
use syn::{Data, DeriveInput, Field, Meta, Path, Type};
use super::{
models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandlerMultiple,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub(crate) struct IntoStructHandler;
impl TraitHandlerMultiple for IntoStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &[Meta],
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_types: true
}
.build_from_into_meta(meta)?;
if let Data::Struct(data) = &ast.data {
let fields = &data.fields;
let field_attributes: HashMap<usize, FieldAttribute> = {
let mut map = HashMap::new();
for (index, field) in fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_types: true
}
.build_from_attributes(&field.attrs, traits)?;
for ty in field_attribute.types.keys() {
if !type_attribute.types.contains_key(ty) {
return Err(super::panic::no_into_impl(ty));
}
}
map.insert(index, field_attribute);
}
map
};
for (target_ty, bound) in type_attribute.types {
let mut into_types: Vec<&Type> = Vec::new();
let mut into_token_stream = proc_macro2::TokenStream::new();
let (index, field, method) = {
let fields = &data.fields;
if fields.len() == 1 {
let field = fields.into_iter().next().unwrap();
let method = if let Some(field_attribute) = field_attributes.get(&0) {
if let Some(method) = field_attribute.types.get(&target_ty) {
method.as_ref()
} else {
None
}
} else {
None
};
(0usize, field, method)
} else {
let mut into_field: Option<(usize, &Field, Option<&Path>)> = None;
for (index, field) in fields.iter().enumerate() {
if let Some(field_attribute) = field_attributes.get(&index) {
if let Some((key, method)) =
field_attribute.types.get_key_value(&target_ty)
{
if into_field.is_some() {
return Err(super::panic::multiple_into_fields(key));
}
into_field = Some((index, field, method.as_ref()));
}
}
}
if into_field.is_none() {
// search the same type
for (index, field) in fields.iter().enumerate() {
let field_ty = super::common::to_hash_type(&field.ty);
if target_ty.eq(&field_ty) {
if into_field.is_some() {
// multiple candidates
into_field = None;
break;
}
into_field = Some((index, field, None));
}
}
}
if let Some(into_field) = into_field {
into_field
} else {
return Err(super::panic::no_into_field(&target_ty));
}
}
};
let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
if let Some(method) = method {
into_token_stream.extend(quote!( #method(self.#field_name) ));
} else {
let ty = &field.ty;
let field_ty = super::common::to_hash_type(ty);
if target_ty.eq(&field_ty) {
into_token_stream.extend(quote!( self.#field_name ));
} else {
into_types.push(ty);
into_token_stream
.extend(quote!( ::core::convert::Into::into(self.#field_name) ));
}
}
let ident = &ast.ident;
let bound = bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::convert::Into<#target_ty>)).unwrap(),
&into_types,
&[],
);
// clone generics in order to not to affect other Into<T> implementations
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, _) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::convert::Into<#target_ty> for #ident #ty_generics #where_clause {
#[inline]
fn into(self) -> #target_ty {
#into_token_stream
}
}
});
}
}
Ok(())
}
}

View File

@@ -0,0 +1,34 @@
mod common;
mod into_enum;
mod into_struct;
mod models;
mod panic;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandlerMultiple;
use crate::Trait;
pub(crate) struct IntoHandler;
impl TraitHandlerMultiple for IntoHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &[Meta],
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => {
into_struct::IntoStructHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Enum(_) => {
into_enum::IntoEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
Err(crate::panic::trait_not_support_union(meta[0].path().get_ident().unwrap()))
},
}
}
}

View File

@@ -0,0 +1,152 @@
use std::collections::HashMap;
use syn::{punctuated::Punctuated, Attribute, Meta, Path, Token};
use crate::{
common::{path::meta_2_path, r#type::TypeWithPunctuatedMeta, tools::HashType},
panic, Trait,
};
pub(crate) struct FieldAttribute {
pub(crate) types: HashMap<HashType, Option<Path>>,
}
#[derive(Debug)]
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_types: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_into_meta(&self, meta: &[Meta]) -> syn::Result<FieldAttribute> {
debug_assert!(!meta.is_empty());
let mut types = HashMap::new();
for meta in meta {
debug_assert!(meta.path().is_ident("Into"));
let correct_usage_for_into_attribute = {
let mut usage = vec![];
if self.enable_types {
usage.push(stringify!(#[educe(Into(type))]));
usage.push(stringify!(#[educe(Into(type, method(path_to_method)))]));
}
usage
};
match meta {
Meta::Path(_) | Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
},
Meta::List(list) => {
if !self.enable_types {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
}
let TypeWithPunctuatedMeta {
ty,
list: result,
} = list.parse_args()?;
let ty = super::super::common::to_hash_type(&ty);
let mut method = None;
let mut method_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "method" {
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
}
}
if types.contains_key(&ty) {
return Err(super::super::panic::reset_a_type(&ty));
}
types.insert(ty, method);
},
}
}
Ok(FieldAttribute {
types,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output: Option<FieldAttribute> = None;
let mut v_meta = Vec::new();
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Into {
v_meta.push(meta);
}
}
}
}
}
if !v_meta.is_empty() {
output = Some(self.build_from_into_meta(&v_meta)?);
}
Ok(output.unwrap_or_else(|| FieldAttribute {
types: HashMap::new()
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,153 @@
use std::collections::HashMap;
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{
common::{bound::Bound, r#type::TypeWithPunctuatedMeta, tools::HashType},
panic, Trait,
};
pub(crate) struct TypeAttribute {
pub(crate) types: HashMap<HashType, Bound>,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_types: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_into_meta(&self, meta: &[Meta]) -> syn::Result<TypeAttribute> {
debug_assert!(!meta.is_empty());
let mut types = HashMap::new();
for meta in meta {
debug_assert!(meta.path().is_ident("Into"));
let correct_usage_for_into_attribute = {
let mut usage = vec![];
if self.enable_types {
usage.push(stringify!(#[educe(Into(type))]));
usage.push(stringify!(#[educe(Into(type, bound = false))]));
usage.push(stringify!(#[educe(Into(type, bound(where_predicates)))]));
}
usage
};
match meta {
Meta::Path(_) | Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
},
Meta::List(list) => {
if !self.enable_types {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
}
let TypeWithPunctuatedMeta {
ty,
list: result,
} = list.parse_args()?;
let ty = super::super::common::to_hash_type(&ty);
let mut bound = Bound::Auto;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_into_attribute,
));
}
}
if types.contains_key(&ty) {
return Err(super::super::panic::reset_a_type(&ty));
}
types.insert(ty, bound);
},
}
}
Ok(TypeAttribute {
types,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output: Option<TypeAttribute> = None;
let mut v_meta = Vec::new();
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Into {
v_meta.push(meta);
}
}
}
}
}
if !v_meta.is_empty() {
output = Some(self.build_from_into_meta(&v_meta)?);
}
Ok(output.unwrap_or(TypeAttribute {
types: HashMap::new()
}))
}
}

View File

@@ -0,0 +1,27 @@
use crate::common::tools::HashType;
#[inline]
pub(crate) fn reset_a_type(ty: &HashType) -> syn::Error {
syn::Error::new(ty.span(), format!("the type `{ty}` is repeatedly set"))
}
#[inline]
pub(crate) fn no_into_field(ty: &HashType) -> syn::Error {
syn::Error::new(ty.span(), format!("there is no field which is assigned for `Into<{ty}>`"))
}
#[inline]
pub(crate) fn no_into_impl(ty: &HashType) -> syn::Error {
syn::Error::new(
ty.span(),
format!(
"if you want to impl `Into<{ty}>` for this type, you should write \
`#[educe(Into({ty}))]` outside"
),
)
}
#[inline]
pub(crate) fn multiple_into_fields(ty: &HashType) -> syn::Error {
syn::Error::new(ty.span(), format!("multiple fields are set for `Into<{ty}>`"))
}

48
vendor/educe/src/trait_handlers/mod.rs vendored Normal file
View File

@@ -0,0 +1,48 @@
use syn::{DeriveInput, Meta};
use crate::Trait;
#[cfg(feature = "Clone")]
pub(crate) mod clone;
#[cfg(feature = "Copy")]
pub(crate) mod copy;
#[cfg(feature = "Debug")]
pub(crate) mod debug;
#[cfg(feature = "Default")]
pub(crate) mod default;
#[cfg(feature = "Deref")]
pub(crate) mod deref;
#[cfg(feature = "DerefMut")]
pub(crate) mod deref_mut;
#[cfg(feature = "Eq")]
pub(crate) mod eq;
#[cfg(feature = "Hash")]
pub(crate) mod hash;
#[cfg(feature = "Into")]
pub(crate) mod into;
#[cfg(feature = "Ord")]
pub(crate) mod ord;
#[cfg(feature = "PartialEq")]
pub(crate) mod partial_eq;
#[cfg(feature = "PartialOrd")]
pub(crate) mod partial_ord;
pub(crate) trait TraitHandler {
#[allow(dead_code)]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()>;
}
pub(crate) trait TraitHandlerMultiple {
#[allow(dead_code)]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &[Meta],
) -> syn::Result<()>;
}

View File

@@ -0,0 +1,49 @@
mod models;
mod ord_enum;
mod ord_struct;
mod panic;
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct OrdHandler;
impl TraitHandler for OrdHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => {
ord_struct::OrdStructHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Enum(_) => {
ord_enum::OrdEnumHandler::trait_meta_handler(ast, token_stream, traits, meta)
},
Data::Union(_) => {
Err(crate::panic::trait_not_support_union(meta.path().get_ident().unwrap()))
},
}
}
}
fn supertraits(#[allow(unused_variables)] traits: &[Trait]) -> Vec<proc_macro2::TokenStream> {
let mut supertraits = vec![];
supertraits.push(quote! {::core::cmp::Eq});
// We mustn't add the PartialOrd bound to the educed PartialOrd impl.
// When we're educing PartialOrd we can leave it off the Ord impl too,
// since we *know* Self is going to be PartialOrd.
#[cfg(feature = "PartialOrd")]
if !traits.contains(&Trait::PartialOrd) {
supertraits.push(quote! {::core::cmp::PartialOrd});
};
supertraits
}

View File

@@ -0,0 +1,217 @@
use proc_macro2::Span;
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Meta, Path, Token};
use crate::{
common::{
ident_bool::{meta_2_bool_allow_path, meta_name_value_2_bool},
int::meta_2_isize,
path::meta_2_path,
},
panic,
supported_traits::Trait,
};
pub(crate) struct FieldAttribute {
pub(crate) ignore: bool,
pub(crate) method: Option<Path>,
pub(crate) rank: isize,
pub(crate) rank_span: Option<Span>,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_ignore: bool,
pub(crate) enable_method: bool,
pub(crate) enable_rank: bool,
pub(crate) rank: isize,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_ord_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("Ord") || meta.path().is_ident("PartialOrd"));
let mut ignore = false;
let mut method = None;
let mut rank = self.rank;
let mut rank_span = None;
let correct_usage_for_partial_eq_attribute = {
let mut usage = vec![];
if self.enable_ignore {
usage.push(stringify!(#[educe(Ord = false)]));
usage.push(stringify!(#[educe(Ord(ignore))]));
}
if self.enable_method {
usage.push(stringify!(#[educe(Ord(method(path_to_method)))]));
}
if self.enable_rank {
usage.push(stringify!(#[educe(Ord(rank = integer))]));
}
usage
};
match meta {
Meta::Path(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
},
Meta::NameValue(name_value) => {
if self.enable_ignore {
ignore = !meta_name_value_2_bool(name_value)?;
} else {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut ignore_is_set = false;
let mut method_is_set = false;
let mut rank_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"ignore" => {
if !self.enable_ignore {
return Ok(false);
}
let v = meta_2_bool_allow_path(&meta)?;
if ignore_is_set {
return Err(panic::parameter_reset(ident));
}
ignore_is_set = true;
ignore = v;
return Ok(true);
},
"method" => {
if !self.enable_method {
return Ok(false);
}
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
},
"rank" => {
if !self.enable_rank {
return Ok(false);
}
let v = meta_2_isize(&meta)?;
if rank_is_set {
return Err(panic::parameter_reset(ident));
}
rank_is_set = true;
rank = v;
rank_span = Some(meta.span());
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
}
},
}
Ok(FieldAttribute {
ignore,
method,
rank,
rank_span,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Ord {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_ord_meta(&meta)?);
}
#[cfg(feature = "PartialOrd")]
if traits.contains(&Trait::PartialOrd) && t == Trait::PartialOrd {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_ord_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
ignore: false,
method: None,
rank: self.rank,
rank_span: None,
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,149 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{common::bound::Bound, panic, Trait};
pub(crate) struct TypeAttribute {
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_ord_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("Ord") || meta.path().is_ident("PartialOrd"));
let mut bound = Bound::Auto;
let correct_usage_for_partial_eq_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(Ord)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(Ord(bound(where_predicates)))]));
usage.push(stringify!(#[educe(Ord(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
}
},
}
Ok(TypeAttribute {
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::Ord {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_ord_meta(&meta)?);
}
#[cfg(feature = "PartialOrd")]
if traits.contains(&Trait::PartialOrd) && t == Trait::PartialOrd {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_ord_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,283 @@
use std::collections::BTreeMap;
use quote::{format_ident, quote};
use syn::{spanned::Spanned, Data, DeriveInput, Field, Fields, Ident, Meta, Path, Type};
use super::{
models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::tools::DiscriminantType, Trait};
pub(crate) struct OrdEnumHandler;
impl TraitHandler for OrdEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true, enable_bound: true
}
.build_from_ord_meta(meta)?;
let mut ord_types: Vec<&Type> = Vec::new();
let mut cmp_token_stream = proc_macro2::TokenStream::new();
let discriminant_type = DiscriminantType::from_ast(ast)?;
let mut arms_token_stream = proc_macro2::TokenStream::new();
let mut all_unit = true;
if let Data::Enum(data) = &ast.data {
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false, enable_bound: false
}
.build_from_attributes(&variant.attrs, traits)?;
let variant_ident = &variant.ident;
let built_in_cmp: Path = syn::parse2(quote!(::core::cmp::Ord::cmp)).unwrap();
match &variant.fields {
Fields::Unit => {
arms_token_stream.extend(quote! {
Self::#variant_ident => {
return ::core::cmp::Ordering::Equal;
}
});
},
Fields::Named(_) => {
all_unit = false;
let mut pattern_self_token_stream = proc_macro2::TokenStream::new();
let mut pattern_other_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
let mut fields: BTreeMap<isize, (&Field, Ident, Ident, FieldAttribute)> =
BTreeMap::new();
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
enable_rank: true,
rank: isize::MIN + index as isize,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_real = field.ident.as_ref().unwrap();
let field_name_var_self = format_ident!("_s_{}", field_name_real);
let field_name_var_other = format_ident!("_o_{}", field_name_real);
if field_attribute.ignore {
pattern_self_token_stream.extend(quote!(#field_name_real: _,));
pattern_other_token_stream.extend(quote!(#field_name_real: _,));
continue;
}
pattern_self_token_stream
.extend(quote!(#field_name_real: #field_name_var_self,));
pattern_other_token_stream
.extend(quote!(#field_name_real: #field_name_var_other,));
let rank = field_attribute.rank;
if fields.contains_key(&rank) {
return Err(super::panic::reuse_a_rank(
field_attribute.rank_span.unwrap_or_else(|| field.span()),
rank,
));
}
fields.insert(
rank,
(field, field_name_var_self, field_name_var_other, field_attribute),
);
}
for (field, field_name_var_self, field_name_var_other, field_attribute) in
fields.values()
{
let cmp = field_attribute.method.as_ref().unwrap_or_else(|| {
ord_types.push(&field.ty);
&built_in_cmp
});
block_token_stream.extend(quote! {
match #cmp(#field_name_var_self, #field_name_var_other) {
::core::cmp::Ordering::Equal => (),
::core::cmp::Ordering::Greater => return ::core::cmp::Ordering::Greater,
::core::cmp::Ordering::Less => return ::core::cmp::Ordering::Less,
}
});
}
arms_token_stream.extend(quote! {
Self::#variant_ident { #pattern_self_token_stream } => {
if let Self::#variant_ident { #pattern_other_token_stream } = other {
#block_token_stream
}
}
});
},
Fields::Unnamed(_) => {
all_unit = false;
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut pattern2_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
let mut fields: BTreeMap<isize, (&Field, Ident, Ident, FieldAttribute)> =
BTreeMap::new();
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
enable_rank: true,
rank: isize::MIN + index as isize,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_var_self = format_ident!("_{}", index);
if field_attribute.ignore {
pattern_token_stream.extend(quote!(_,));
pattern2_token_stream.extend(quote!(_,));
continue;
}
let field_name_var_other = format_ident!("_{}", field_name_var_self);
pattern_token_stream.extend(quote!(#field_name_var_self,));
pattern2_token_stream.extend(quote!(#field_name_var_other,));
let rank = field_attribute.rank;
if fields.contains_key(&rank) {
return Err(super::panic::reuse_a_rank(
field_attribute.rank_span.unwrap_or_else(|| field.span()),
rank,
));
}
fields.insert(
rank,
(field, field_name_var_self, field_name_var_other, field_attribute),
);
}
for (field, field_name, field_name2, field_attribute) in fields.values() {
let cmp = field_attribute.method.as_ref().unwrap_or_else(|| {
ord_types.push(&field.ty);
&built_in_cmp
});
block_token_stream.extend(quote! {
match #cmp(#field_name, #field_name2) {
::core::cmp::Ordering::Equal => (),
::core::cmp::Ordering::Greater => return ::core::cmp::Ordering::Greater,
::core::cmp::Ordering::Less => return ::core::cmp::Ordering::Less,
}
});
}
arms_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => {
if let Self::#variant_ident ( #pattern2_token_stream ) = other {
#block_token_stream
}
}
});
},
}
}
}
if arms_token_stream.is_empty() {
cmp_token_stream.extend(quote!(::core::cmp::Ordering::Equal));
} else {
let discriminant_cmp = quote! {
unsafe {
::core::cmp::Ord::cmp(&*<*const _>::from(self).cast::<#discriminant_type>(), &*<*const _>::from(other).cast::<#discriminant_type>())
}
};
cmp_token_stream.extend(if all_unit {
quote! {
match #discriminant_cmp {
::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal,
::core::cmp::Ordering::Greater => ::core::cmp::Ordering::Greater,
::core::cmp::Ordering::Less => ::core::cmp::Ordering::Less,
}
}
} else {
quote! {
match #discriminant_cmp {
::core::cmp::Ordering::Equal => {
match self {
#arms_token_stream
}
::core::cmp::Ordering::Equal
},
::core::cmp::Ordering::Greater => ::core::cmp::Ordering::Greater,
::core::cmp::Ordering::Less => ::core::cmp::Ordering::Less,
}
}
});
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::cmp::Ord)).unwrap(),
&ord_types,
&crate::trait_handlers::ord::supertraits(traits),
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Ord for #ident #ty_generics #where_clause {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
#cmp_token_stream
}
}
});
#[cfg(feature = "PartialOrd")]
if traits.contains(&Trait::PartialOrd) {
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::PartialOrd for #ident #ty_generics #where_clause {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
Some(::core::cmp::Ord::cmp(self, other))
}
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,123 @@
use std::collections::BTreeMap;
use quote::quote;
use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta, Path, Type};
use super::{
models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub(crate) struct OrdStructHandler;
impl TraitHandler for OrdStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute = TypeAttributeBuilder {
enable_flag: true, enable_bound: true
}
.build_from_ord_meta(meta)?;
let mut ord_types: Vec<&Type> = Vec::new();
let mut cmp_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
let mut fields: BTreeMap<isize, (usize, &Field, FieldAttribute)> = BTreeMap::new();
for (index, field) in data.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
enable_rank: true,
rank: isize::MIN + index as isize,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
continue;
}
let rank = field_attribute.rank;
if fields.contains_key(&rank) {
return Err(super::panic::reuse_a_rank(
field_attribute.rank_span.unwrap_or_else(|| field.span()),
rank,
));
}
fields.insert(rank, (index, field, field_attribute));
}
let built_in_cmp: Path = syn::parse2(quote!(::core::cmp::Ord::cmp)).unwrap();
for (index, field, field_attribute) in fields.values() {
let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), *index);
let cmp = field_attribute.method.as_ref().unwrap_or_else(|| {
ord_types.push(&field.ty);
&built_in_cmp
});
cmp_token_stream.extend(quote! {
match #cmp(&self.#field_name, &other.#field_name) {
::core::cmp::Ordering::Equal => (),
::core::cmp::Ordering::Greater => return ::core::cmp::Ordering::Greater,
::core::cmp::Ordering::Less => return ::core::cmp::Ordering::Less,
}
});
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::cmp::Ord)).unwrap(),
&ord_types,
&crate::trait_handlers::ord::supertraits(traits),
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Ord for #ident #ty_generics #where_clause {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
#cmp_token_stream
::core::cmp::Ordering::Equal
}
}
});
#[cfg(feature = "PartialOrd")]
if traits.contains(&Trait::PartialOrd) {
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::PartialOrd for #ident #ty_generics #where_clause {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
Some(::core::cmp::Ord::cmp(self, other))
}
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,6 @@
use proc_macro2::Span;
#[inline]
pub(crate) fn reuse_a_rank(span: Span, rank: isize) -> syn::Error {
syn::Error::new(span, format!("the rank `{rank}` is repeatedly used"))
}

View File

@@ -0,0 +1,43 @@
mod models;
mod panic;
mod partial_eq_enum;
mod partial_eq_struct;
mod partial_eq_union;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct PartialEqHandler;
impl TraitHandler for PartialEqHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
match ast.data {
Data::Struct(_) => partial_eq_struct::PartialEqStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => partial_eq_enum::PartialEqEnumHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Union(_) => partial_eq_union::PartialEqUnionHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
}
}
}

View File

@@ -0,0 +1,181 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Path, Token};
use crate::{
common::{
ident_bool::{meta_2_bool_allow_path, meta_name_value_2_bool},
path::meta_2_path,
},
panic,
supported_traits::Trait,
};
pub(crate) struct FieldAttribute {
pub(crate) ignore: bool,
pub(crate) method: Option<Path>,
}
pub(crate) struct FieldAttributeBuilder {
pub(crate) enable_ignore: bool,
pub(crate) enable_method: bool,
}
impl FieldAttributeBuilder {
pub(crate) fn build_from_partial_eq_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
debug_assert!(meta.path().is_ident("PartialEq") || meta.path().is_ident("Eq"));
let mut ignore = false;
let mut method = None;
let correct_usage_for_partial_eq_attribute = {
let mut usage = vec![];
if self.enable_ignore {
usage.push(stringify!(#[educe(PartialEq = false)]));
usage.push(stringify!(#[educe(PartialEq(ignore))]));
}
if self.enable_method {
usage.push(stringify!(#[educe(PartialEq(method(path_to_method)))]));
}
usage
};
match meta {
Meta::Path(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
},
Meta::NameValue(name_value) => {
if self.enable_ignore {
ignore = !meta_name_value_2_bool(name_value)?;
} else {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
},
Meta::List(list) => {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
let mut ignore_is_set = false;
let mut method_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
match ident.to_string().as_str() {
"ignore" => {
if !self.enable_ignore {
return Ok(false);
}
let v = meta_2_bool_allow_path(&meta)?;
if ignore_is_set {
return Err(panic::parameter_reset(ident));
}
ignore_is_set = true;
ignore = v;
return Ok(true);
},
"method" => {
if !self.enable_method {
return Ok(false);
}
let v = meta_2_path(&meta)?;
if method_is_set {
return Err(panic::parameter_reset(ident));
}
method_is_set = true;
method = Some(v);
return Ok(true);
},
_ => (),
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
}
},
}
Ok(FieldAttribute {
ignore,
method,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<FieldAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::PartialEq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_partial_eq_meta(&meta)?);
}
#[cfg(feature = "Eq")]
if traits.contains(&Trait::Eq) && t == Trait::Eq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_partial_eq_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(FieldAttribute {
ignore: false, method: None
}))
}
}

View File

@@ -0,0 +1,5 @@
mod field_attribute;
mod type_attribute;
pub(crate) use field_attribute::*;
pub(crate) use type_attribute::*;

View File

@@ -0,0 +1,163 @@
use syn::{punctuated::Punctuated, Attribute, Meta, Token};
use crate::{
common::{bound::Bound, unsafe_punctuated_meta::UnsafePunctuatedMeta},
panic, Trait,
};
pub(crate) struct TypeAttribute {
pub(crate) has_unsafe: bool,
pub(crate) bound: Bound,
}
#[derive(Debug)]
pub(crate) struct TypeAttributeBuilder {
pub(crate) enable_flag: bool,
pub(crate) enable_unsafe: bool,
pub(crate) enable_bound: bool,
}
impl TypeAttributeBuilder {
pub(crate) fn build_from_partial_eq_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
debug_assert!(meta.path().is_ident("PartialEq") || meta.path().is_ident("Eq"));
let mut has_unsafe = false;
let mut bound = Bound::Auto;
let correct_usage_for_partial_eq_attribute = {
let mut usage = vec![];
if self.enable_flag {
usage.push(stringify!(#[educe(PartialEq)]));
}
if self.enable_bound {
usage.push(stringify!(#[educe(PartialEq(bound(where_predicates)))]));
usage.push(stringify!(#[educe(PartialEq(bound = false))]));
}
usage
};
match meta {
Meta::Path(_) => {
if !self.enable_flag {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
},
Meta::NameValue(_) => {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
},
Meta::List(list) => {
let result = if self.enable_unsafe {
let result: UnsafePunctuatedMeta = list.parse_args()?;
has_unsafe = result.has_unsafe;
result.list
} else {
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?
};
let mut bound_is_set = false;
let mut handler = |meta: Meta| -> syn::Result<bool> {
if let Some(ident) = meta.path().get_ident() {
if ident == "bound" {
if !self.enable_bound {
return Ok(false);
}
let v = Bound::from_meta(&meta)?;
if bound_is_set {
return Err(panic::parameter_reset(ident));
}
bound_is_set = true;
bound = v;
return Ok(true);
}
}
Ok(false)
};
for p in result {
if !handler(p)? {
return Err(panic::attribute_incorrect_format(
meta.path().get_ident().unwrap(),
&correct_usage_for_partial_eq_attribute,
));
}
}
},
}
Ok(TypeAttribute {
has_unsafe,
bound,
})
}
pub(crate) fn build_from_attributes(
&self,
attributes: &[Attribute],
traits: &[Trait],
) -> syn::Result<TypeAttribute> {
let mut output = None;
for attribute in attributes.iter() {
let path = attribute.path();
if path.is_ident("educe") {
if let Meta::List(list) = &attribute.meta {
let result =
list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in result {
let path = meta.path();
let t = match Trait::from_path(path) {
Some(t) => t,
None => return Err(panic::unsupported_trait(meta.path())),
};
if !traits.contains(&t) {
return Err(panic::trait_not_used(path.get_ident().unwrap()));
}
if t == Trait::PartialEq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_partial_eq_meta(&meta)?);
}
#[cfg(feature = "Eq")]
if traits.contains(&Trait::Eq) && t == Trait::Eq {
if output.is_some() {
return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
}
output = Some(self.build_from_partial_eq_meta(&meta)?);
}
}
}
}
}
Ok(output.unwrap_or(TypeAttribute {
has_unsafe: false, bound: Bound::Auto
}))
}
}

View File

@@ -0,0 +1,22 @@
use quote::ToTokens;
use syn::{spanned::Spanned, Meta};
#[inline]
pub(crate) fn union_without_unsafe(meta: &Meta) -> syn::Error {
let mut s = meta.into_token_stream().to_string();
match s.len() {
9 => s.push_str("(unsafe)"),
11 => s.insert_str(10, "unsafe"),
_ => unreachable!(),
}
syn::Error::new(
meta.span(),
format!(
"a union's `PartialEq` implementation is not precise, because it ignores the type of \
fields\n* If your union doesn't care about that, use `#[educe({s})]` to implement \
the `PartialEq` trait for it."
),
)
}

View File

@@ -0,0 +1,218 @@
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, Fields, Meta, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::Trait;
pub(crate) struct PartialEqEnumHandler;
impl TraitHandler for PartialEqEnumHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: false, enable_bound: true
}
.build_from_partial_eq_meta(meta)?;
let mut partial_eq_types: Vec<&Type> = Vec::new();
let mut eq_token_stream = proc_macro2::TokenStream::new();
let mut arms_token_stream = proc_macro2::TokenStream::new();
if let Data::Enum(data) = &ast.data {
for variant in data.variants.iter() {
let _ = TypeAttributeBuilder {
enable_flag: false,
enable_unsafe: false,
enable_bound: false,
}
.build_from_attributes(&variant.attrs, traits)?;
let variant_ident = &variant.ident;
match &variant.fields {
Fields::Unit => {
arms_token_stream.extend(quote! {
Self::#variant_ident => {
if let Self::#variant_ident = other {
// same
} else {
return false;
}
}
});
},
Fields::Named(_) => {
let mut pattern_self_token_stream = proc_macro2::TokenStream::new();
let mut pattern_other_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
for field in variant.fields.iter() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
let field_name_real = field.ident.as_ref().unwrap();
let field_name_var_self = format_ident!("_s_{}", field_name_real);
let field_name_var_other = format_ident!("_o_{}", field_name_real);
if field_attribute.ignore {
pattern_self_token_stream.extend(quote!(#field_name_real: _,));
pattern_other_token_stream.extend(quote!(#field_name_real: _,));
continue;
}
pattern_self_token_stream
.extend(quote!(#field_name_real: #field_name_var_self,));
pattern_other_token_stream
.extend(quote!(#field_name_real: #field_name_var_other,));
if let Some(method) = field_attribute.method {
block_token_stream.extend(quote! {
if !#method(#field_name_var_self, #field_name_var_other) {
return false;
}
});
} else {
let ty = &field.ty;
partial_eq_types.push(ty);
block_token_stream.extend(quote! {
if ::core::cmp::PartialEq::ne(#field_name_var_self, #field_name_var_other) {
return false;
}
});
}
}
arms_token_stream.extend(quote! {
Self::#variant_ident { #pattern_self_token_stream } => {
if let Self::#variant_ident { #pattern_other_token_stream } = other {
#block_token_stream
} else {
return false;
}
}
});
},
Fields::Unnamed(_) => {
let mut pattern_token_stream = proc_macro2::TokenStream::new();
let mut pattern2_token_stream = proc_macro2::TokenStream::new();
let mut block_token_stream = proc_macro2::TokenStream::new();
for (index, field) in variant.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
pattern_token_stream.extend(quote!(_,));
pattern2_token_stream.extend(quote!(_,));
continue;
}
let field_name_var_self = format_ident!("_{}", index);
let field_name_var_other = format_ident!("_{}", field_name_var_self);
pattern_token_stream.extend(quote!(#field_name_var_self,));
pattern2_token_stream.extend(quote!(#field_name_var_other,));
if let Some(method) = field_attribute.method {
block_token_stream.extend(quote! {
if !#method(#field_name_var_self, #field_name_var_other) {
return false;
}
});
} else {
let ty = &field.ty;
partial_eq_types.push(ty);
block_token_stream.extend(quote! {
if ::core::cmp::PartialEq::ne(#field_name_var_self, #field_name_var_other) {
return false;
}
});
}
}
arms_token_stream.extend(quote! {
Self::#variant_ident ( #pattern_token_stream ) => {
if let Self::#variant_ident ( #pattern2_token_stream ) = other {
#block_token_stream
} else {
return false;
}
}
});
},
}
}
}
if !arms_token_stream.is_empty() {
eq_token_stream.extend(quote! {
match self {
#arms_token_stream
}
});
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::cmp::PartialEq)).unwrap(),
&partial_eq_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::PartialEq for #ident #ty_generics #where_clause {
#[inline]
fn eq(&self, other: &Self) -> bool {
#eq_token_stream
true
}
}
});
#[cfg(feature = "Eq")]
if traits.contains(&Trait::Eq) {
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Eq for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,103 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta, Type};
use super::{
models::{FieldAttributeBuilder, TypeAttributeBuilder},
TraitHandler,
};
use crate::{common::ident_index::IdentOrIndex, Trait};
pub(crate) struct PartialEqStructHandler;
impl TraitHandler for PartialEqStructHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: false, enable_bound: true
}
.build_from_partial_eq_meta(meta)?;
let mut partial_eq_types: Vec<&Type> = Vec::new();
let mut eq_token_stream = proc_macro2::TokenStream::new();
if let Data::Struct(data) = &ast.data {
for (index, field) in data.fields.iter().enumerate() {
let field_attribute = FieldAttributeBuilder {
enable_ignore: true,
enable_method: true,
}
.build_from_attributes(&field.attrs, traits)?;
if field_attribute.ignore {
continue;
}
let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
if let Some(method) = field_attribute.method {
eq_token_stream.extend(quote! {
if !#method(&self.#field_name, &other.#field_name) {
return false;
}
});
} else {
let ty = &field.ty;
partial_eq_types.push(ty);
eq_token_stream.extend(quote! {
if ::core::cmp::PartialEq::ne(&self.#field_name, &other.#field_name) {
return false;
}
});
}
}
}
let ident = &ast.ident;
let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
&ast.generics.params,
&syn::parse2(quote!(::core::cmp::PartialEq)).unwrap(),
&partial_eq_types,
&[],
);
let mut generics = ast.generics.clone();
let where_clause = generics.make_where_clause();
for where_predicate in bound {
where_clause.predicates.push(where_predicate);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::PartialEq for #ident #ty_generics #where_clause {
#[inline]
fn eq(&self, other: &Self) -> bool {
#eq_token_stream
true
}
}
});
#[cfg(feature = "Eq")]
if traits.contains(&Trait::Eq) {
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Eq for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,63 @@
use quote::quote;
use syn::{Data, DeriveInput, Meta};
use super::models::{FieldAttributeBuilder, TypeAttributeBuilder};
use crate::{supported_traits::Trait, trait_handlers::TraitHandler};
pub(crate) struct PartialEqUnionHandler;
impl TraitHandler for PartialEqUnionHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
let type_attribute =
TypeAttributeBuilder {
enable_flag: true, enable_unsafe: true, enable_bound: false
}
.build_from_partial_eq_meta(meta)?;
if !type_attribute.has_unsafe {
return Err(super::panic::union_without_unsafe(meta));
}
if let Data::Union(data) = &ast.data {
for field in data.fields.named.iter() {
let _ = FieldAttributeBuilder {
enable_ignore: false, enable_method: false
}
.build_from_attributes(&field.attrs, traits)?;
}
}
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::PartialEq for #ident #ty_generics #where_clause {
#[inline]
fn eq(&self, other: &Self) -> bool {
let size = ::core::mem::size_of::<Self>();
let self_data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) };
let other_data = unsafe { ::core::slice::from_raw_parts(other as *const Self as *const u8, size) };
::core::cmp::PartialEq::eq(self_data, other_data)
}
}
});
#[cfg(feature = "Eq")]
if traits.contains(&Trait::Eq) {
token_stream.extend(quote! {
impl #impl_generics ::core::cmp::Eq for #ident #ty_generics #where_clause {
}
});
}
Ok(())
}
}

View File

@@ -0,0 +1,58 @@
mod models;
mod panic;
mod partial_ord_enum;
mod partial_ord_struct;
use models::TypeAttributeBuilder;
use syn::{Data, DeriveInput, Meta};
use super::TraitHandler;
use crate::Trait;
pub(crate) struct PartialOrdHandler;
impl TraitHandler for PartialOrdHandler {
#[inline]
fn trait_meta_handler(
ast: &DeriveInput,
token_stream: &mut proc_macro2::TokenStream,
traits: &[Trait],
meta: &Meta,
) -> syn::Result<()> {
#[cfg(feature = "Ord")]
let contains_ord = traits.contains(&Trait::Ord);
#[cfg(not(feature = "Ord"))]
let contains_ord = false;
// if `contains_ord` is true, the implementation is handled by the `Ord` attribute
if contains_ord {
let _ = TypeAttributeBuilder {
enable_flag: true, enable_bound: false
}
.build_from_partial_ord_meta(meta)?;
// field attributes is also handled by the `Ord` attribute
Ok(())
} else {
match ast.data {
Data::Struct(_) => partial_ord_struct::PartialOrdStructHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Enum(_) => partial_ord_enum::PartialOrdEnumHandler::trait_meta_handler(
ast,
token_stream,
traits,
meta,
),
Data::Union(_) => {
Err(crate::panic::trait_not_support_union(meta.path().get_ident().unwrap()))
},
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More