educe-0.4.19/.cargo_vcs_info.json0000644000000001360000000000100122140ustar { "git": { "sha1": "db545bda7f0cdbb9a75f83c3c5a122d8a296fd40" }, "path_in_vcs": "" }educe-0.4.19/Cargo.toml0000644000000027300000000000100102140ustar # 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" name = "educe" version = "0.4.19" authors = ["Magic Len "] include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] description = "This crate provides procedural macros to help you implement Rust-built-in traits quickly." 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" resolver = "2" [lib] proc-macro = true [dependencies.enum-ordinalize] version = "3.1" [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" features = ["full"] [dev-dependencies.assert_approx_eq] version = "1.1" [features] Clone = [] Copy = [] Debug = [] Default = [] Deref = [] DerefMut = [] Eq = [] Hash = [] Ord = [] PartialEq = [] PartialOrd = [] default = ["Debug", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Clone", "Copy", "Deref", "DerefMut"] educe-0.4.19/Cargo.toml.orig000064400000000000000000000016570072674642500137340ustar 00000000000000[package] name = "educe" version = "0.4.19" authors = ["Magic Len "] edition = "2021" repository = "https://github.com/magiclen/educe" homepage = "https://magiclen.org/educe" keywords = ["derive", "macro", "trait", "field", "procedural"] categories = ["no-std", "rust-patterns"] description = "This crate provides procedural macros to help you implement Rust-built-in traits quickly." readme = "README.md" license = "MIT" include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] [lib] proc-macro = true [dependencies] proc-macro2 = "1" syn = { version = "1", features = ["full"] } quote = "1" enum-ordinalize = "3.1" [dev-dependencies] assert_approx_eq = "1.1" [features] default = ["Debug", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Clone", "Copy", "Deref", "DerefMut"] Debug = [] PartialEq = [] Eq = [] PartialOrd = [] Ord = [] Hash = [] Default = [] Clone = [] Copy = [] Deref = [] DerefMut = []educe-0.4.19/LICENSE000064400000000000000000000020660072674642500120450ustar 00000000000000MIT License Copyright (c) 2018 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. educe-0.4.19/README.md000064400000000000000000000703070072674642500123220ustar 00000000000000Educe ==================== [![CI](https://github.com/magiclen/educe/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/educe/actions/workflows/ci.yml) This crate provides procedural macros to help you implement Rust-built-in traits quickly. ## Features By default, every trait this crate supports will be enabled. You can disable all of them by disabling the default features and enable only the traits that you want to use by adding them to `features` explictly. For example, ```toml [dependencies.educe] version = "*" features = ["Debug", "Default", "Hash", "Clone", "Copy"] default-features = false ``` ## Debug Use `#[derive(Educe)]` and `#[educe(Debug)]` to implement the `Debug` trait for a struct, an enum, or a union. It supports to change the name of your types, variants and fields. You can also ignore some fields, or set a trait and/or a method to replace the `Debug` trait used by default. Also, you can even format a struct to a tuple, and vice versa. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Change the Name of a Type, a Variant or a Field The `name` attribute can help you rename a type, a variant or a field. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(name = "Struct2"))] struct Struct { #[educe(Debug(name = "f"))] f1: u8 } #[derive(Educe)] #[educe(Debug(name = true))] enum Enum { #[educe(Debug(name = false))] V1, #[educe(Debug(name = "V"))] V2 { #[educe(Debug(name = "f"))] f1: u8, }, #[educe(Debug(name = false))] V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Struct { #[educe(Debug(ignore))] f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { #[educe(Debug(ignore))] f1: u8, }, V3( #[educe(Debug(ignore))] u8 ), } ``` #### Fake Structs and Tuples With the `named_field` attribute, structs can be formatted as tuples and tuples can be formatted as structs. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(named_field = false))] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, #[educe(Debug(named_field = false))] V2 { f1: u8, }, #[educe(Debug(named_field = true))] V3( u8, #[educe(Debug(name = "value"))] i32 ), } ``` #### Use Another Method or Trait to Do the Format Thing The `trait` and `method` attributes can be used to replace the `Debug` trait for fields. If you only set the `trait` parameter, the `method` will be set to `fmt` automatically by default. ```rust #[macro_use] extern crate educe; use std::fmt::{self, Formatter}; fn fmt(_s: &u8, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } trait A { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { #[educe(Debug(method = "fmt"))] f1: u8, }, V3( #[educe(Debug(trait = "std::fmt::UpperHex"))] u8, #[educe(Debug(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Debug` Trait or Others The `#[educe(Debug(bound))]` attribute can be used to add the `Debug` trait bound to all generic parameters for the `Debug` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; use std::fmt::{self, Formatter}; fn fmt(_s: &u8, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } trait A { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Debug(bound = "T: std::fmt::Debug, K: A"))] enum Enum { V1, V2 { #[educe(Debug(trait = "A"))] f1: K, }, V3( T ), } ``` #### Union A union will be formatted to a `u8` slice, because we don't know it's field at runtime. The fields of a union cannot be ignored, renamed or formated with other methods or traits. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Union { f1: u8, f2: i32, } ``` ## PartialEq Use `#[derive(Educe)]` and `#[educe(ParitalEq)]` to implement the `ParitalEq` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `ParitalEq` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq)] struct Struct { #[educe(PartialEq(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { #[educe(PartialEq(ignore))] f1: u8, }, V3( #[educe(PartialEq(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `PartialEq` trait for fields. If you only set the `trait` parameter, the `method` will be set to `eq` automatically by default. ```rust #[macro_use] extern crate educe; fn eq(a: &u8, b: &u8) -> bool { a + 1 == *b } trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { #[educe(PartialEq(method = "eq"))] f1: u8, }, V3( #[educe(PartialEq(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `PartialEq` Trait or Others The `#[educe(PartialEq(bound))]` attribute can be used to add the `PartialEq` trait bound to all generic parameters for the `PartialEq` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"))] enum Enum { V1, V2 { #[educe(PartialEq(trait = "A"))] f1: K, }, V3( T ), } ``` ## Eq Use `#[derive(Educe)]` and `#[educe(Eq)]` to implement the `Eq` trait for a struct, an enum or a union. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Generic Parameters Bound to the `Eq` Trait or Others The `#[educe(Eq(bound))]` attribute can be used to add the `Eq` trait bound to all generic parameters for the `Eq` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `Eq` trait depends on the `PartialEq` (`PartialEq`) trait.) ```rust #[macro_use] extern crate educe; trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"), Eq(bound = "T: std::cmp::PartialEq, K: A"))] enum Enum { V1, V2 { #[educe(PartialEq(trait = "A"))] f1: K, }, V3( T ), } ``` ## PartialOrd Use `#[derive(Educe)]` and `#[educe(PartialOrd)]` to implement the `PartialOrd` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `PartialOrd` trait used by default. The rank of variants and fields can also be modified. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { #[educe(PartialOrd(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { #[educe(PartialOrd(ignore))] f1: u8, }, V3( #[educe(PartialOrd(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `PartialOrd` trait for fields. If you only set the `trait` parameter, the `method` will be set to `partial_cmp` automatically by default. ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; fn partial_cmp(a: &u8, b: &u8) -> Option { if a > b { Some(Ordering::Less) } else if a < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } trait A { fn partial_cmp(&self, b: &Self) -> Option; } impl A for i32 { fn partial_cmp(&self, b: &i32) -> Option { if self > b { Some(Ordering::Less) } else if self < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { #[educe(PartialOrd(method = "partial_cmp"))] f1: u8, }, V3( #[educe(PartialOrd(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `PartialOrd` Trait or Others The `#[educe(PartialOrd(bound))]` attribute can be used to add the `PartialOrd` trait bound to all generic parameters for the `PartialOrd` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), PartialOrd(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `PartialOrd` trait depends on the `PartialEq` (`PartialEq`) trait.) ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; trait A { fn partial_cmp(&self, b: &Self) -> Option; } impl A for i32 { fn partial_cmp(&self, b: &i32) -> Option { if self > b { Some(Ordering::Less) } else if self < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } } #[derive(Educe)] #[educe(PartialEq(bound), PartialOrd(bound = "T: std::cmp::PartialOrd, K: std::cmp::PartialOrd + A"))] enum Enum { V1, V2 { #[educe(PartialOrd(trait = "A"))] f1: K, }, V3( T ), } ``` #### Ranking Each field can add a `#[educe(PartialOrd(rank = priority_value))]` attribute where `priority_value` is a positive integer value to determine their comparing precedence (lower `priority_value` leads to higher priority). The default `priority_value` for a field dependends on its ordinal (the lower the front) and is always lower than any custom `priority_value`. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { #[educe(PartialOrd(rank = 1))] f1: u8, #[educe(PartialOrd(rank = 0))] f2: u8, } ``` Each variant can add a `#[educe(PartialOrd(rank = comparison_value))]` attribute where `comparison_value` is a positive integer value to override the value or the ordinal of a variant for comparison. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { #[educe(PartialOrd(rank = 2))] Two, #[educe(PartialOrd(rank = 1))] One, } ``` ## Ord Use `#[derive(Educe)]` and `#[educe(Ord)]` to implement the `Ord` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `Ord` trait used by default. The rank of variants and fields can also be modified. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { #[educe(Ord(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { #[educe(Ord(ignore))] f1: u8, }, V3( #[educe(Ord(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `Ord` trait for fields. If you only set the `trait` parameter, the `method` will be set to `cmp` automatically by default. ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; fn cmp(a: &u8, b: &u8) -> Ordering { if a > b { Ordering::Less } else if a < b { Ordering::Greater } else { Ordering::Equal } } trait A { fn cmp(&self, b: &Self) -> Ordering; } impl A for i32 { fn cmp(&self, b: &i32) -> Ordering { if self > b { Ordering::Less } else if self < b { Ordering::Greater } else { Ordering::Equal } } } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { #[educe(Ord(method = "cmp"))] f1: u8, }, V3( #[educe(Ord(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Ord` Trait or Others The `#[educe(Ord(bound))]` attribute can be used to add the `Ord` trait bound to all generic parameters for the `Ord` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `Ord` trait depends on the `PartialOrd` (`PartialOrd`) trait and the `Eq` trait.) ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; trait A { fn cmp(&self, b: &Self) -> Ordering; } impl A for i32 { fn cmp(&self, b: &i32) -> Ordering { if self > b { Ordering::Less } else if self < b { Ordering::Greater } else { Ordering::Equal } } } #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound = "T: std::cmp::Ord, K: std::cmp::Ord + A"))] enum Enum { V1, V2 { #[educe(Ord(trait = "A"))] f1: K, }, V3( T ), } ``` #### Ranking Each field can add a `#[educe(Ord(rank = priority_value))]` attribute where `priority_value` is a positive integer value to determine their comparing precedence (lower `priority_value` leads to higher priority). The default `priority_value` for a field dependends on its ordinal (the lower the front) and is always lower than any custom `priority_value`. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { #[educe(Ord(rank = 1))] f1: u8, #[educe(Ord(rank = 0))] f2: u8, } ``` Each variant can add a `#[educe(Ord(rank = comparison_value))]` attribute where `comparison_value` is a positive integer value to override the value or the ordinal of a variant for comparison. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { #[educe(Ord(rank = 2))] Two, #[educe(Ord(rank = 1))] One, } ``` ## Hash Use `#[derive(Educe)]` and `#[educe(Hash)]` to implement the `Hash` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `Hash` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash)] struct Struct { #[educe(Hash(ignore))] f1: u8 } #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { #[educe(Hash(ignore))] f1: u8, }, V3( #[educe(Hash(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Hashing The `trait` and `method` attributes can be used to replace the `Hash` trait for fields. If you only set the `trait` parameter, the `method` will be set to `hash` automatically by default. ```rust #[macro_use] extern crate educe; use std::hash::{Hash, Hasher}; fn hash(_s: &u8, state: &mut H) { Hash::hash(&100, state) } trait A { fn hash(&self, state: &mut H) { Hash::hash(&100, state) } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { #[educe(Hash(method = "hash"))] f1: u8, }, V3( #[educe(Hash(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Hash` Trait or Others The `#[educe(Hash(bound))]` attribute can be used to add the `Hash` trait bound to all generic parameters for the `Hash` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; use std::hash::{Hash, Hasher}; fn hash(_s: &u8, state: &mut H) { Hash::hash(&100, state) } trait A { fn hash(&self, state: &mut H) { Hash::hash(&100, state) } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Hash(bound = "T: std::hash::Hash, K: A"))] enum Enum { V1, V2 { #[educe(Hash(trait = "A"))] f1: K, }, V3( T ), } ``` ## Default Use `#[derive(Educe)]` and `#[educe(Default)]` to implement the `Default` trait for a struct, an enum, or a union. It supports to set the default value for your type directly, or set the default values for specific fields. #### Basic Usage For enums and unions, you need to assign a variant (of a enum) and a field (of a union) as default unless the number of variants of an enum or the number of fields of a union is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Default)] enum Enum { V1, #[educe(Default)] V2 { f1: u8, }, V3(u8), } #[derive(Educe)] #[educe(Default)] union Union { f1: u8, #[educe(Default)] f2: f64, } ``` #### The Default Value for the Whole Type The `#[educe(Default(expression = "expression"))]` attribute can be used to set the default value for your type by an expression. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(expression = "Struct { f1: 1 }"))] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Default(expression = "Enum::Struct { f1: 1 }"))] enum Enum { Unit, Struct { f1: u8 }, Tuple(u8), } #[derive(Educe)] #[educe(Default(expression = "Union { f1: 1 }"))] union Union { f1: u8, f2: f64, } ``` #### The Default Values for Specific Fields The `#[educe(Default = literal)]` attribute or the `#[educe(Default(expression = "expression"))]` attribute can be used to set the default value for a specific field by a literal value or an expression. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default)] struct Struct { #[educe(Default = 1)] f1: u8, #[educe(Default = 11111111111111111111111111111)] f2: i128, #[educe(Default = 1.1)] f3: f64, #[educe(Default = true)] f4: bool, #[educe(Default = "Hi")] f5: &'static str, #[educe(Default = "Hello")] f6: String, #[educe(Default = 'M')] f7: char, } #[derive(Educe)] #[educe(Default)] enum Enum { Unit, #[educe(Default)] Tuple( #[educe(Default(expression = "0 + 1"))] u8, #[educe(Default(expression = "-11111111111111111111111111111 * -1"))] i128, #[educe(Default(expression = "1.0 + 0.1"))] f64, #[educe(Default(expression = "!false"))] bool, #[educe(Default(expression = "\"Hi\""))] &'static str, #[educe(Default(expression = "String::from(\"Hello\")"))] String, #[educe(Default(expression = "'M'"))] char, ), } #[derive(Educe)] #[educe(Default)] union Union { f1: u8, f2: i128, f3: f64, f4: bool, #[educe(Default = "Hi")] f5: &'static str, f6: char, } ``` #### Generic Parameters Bound to the `Default` Trait or Others The `#[educe(Default(bound))]` attribute can be used to add the `Default` trait bound to all generic parameters for the `Default` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(bound))] enum Enum { Unit, #[educe(Default)] Struct { f1: T }, Tuple(T), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(bound = "T: std::default::Default"))] enum Enum { Unit, #[educe(Default)] Struct { f1: T }, Tuple(T), } ``` #### The `new` Associated Function With the `#[educe(Default(new))]` attribute, your type will have an extra associated function called `new`. That can be used to invoke the `default` method of the `Default` trait. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(new))] struct Struct { f1: u8 } ``` ## Clone Use `#[derive(Educe)]` and `#[educe(Clone)]` to implement the `Clone` trait for a struct, an enum, or a union. It supports to set a trait and/or a method to replace the `Clone` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Clone)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Clone)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Use Another Method or Trait to Do Cloning The `trait` and `method` attributes can be used to replace the `Clone` trait for fields. If you only set the `trait` parameter, the `method` will be set to `clone` automatically by default. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Clone)] enum Enum { V1, V2 { #[educe(Clone(method = "clone"))] f1: u8, }, V3( #[educe(Clone(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Clone` Trait or Others The `#[educe(Clone(bound))]` attribute can be used to add the `Clone` trait bound or the `Copy` trait bound (if the `#[educe(Copy)]` attribute exists) to all generic parameters for the `Clone` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Clone(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Clone(bound = "T: std::clone::Clone, K: A"))] enum Enum { V1, V2 { #[educe(Clone(trait = "A"))] f1: K, }, V3( T ), } ``` #### Union The `#[educe(Clone)]` attribute can be used for a union which also needs to implement the `Copy` trait. The fields of a union cannot be cloned with other methods or traits. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy, Clone)] union Union { f1: u8, } ``` ## Copy Use `#[derive(Educe)]` and `#[educe(Copy)]` to implement the `Copy` trait for a struct, an enum, or a union. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy, Clone)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Copy, Clone)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Generic Parameters Bound to the `Copy` Trait or Others The `#[educe(Copy(bound))]` attribute can be used to add the `Copy` trait bound to all generic parameters for the `Copy` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy(bound), Clone(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Copy(bound = "T: Copy, K: A + Copy"), Clone(bound = "T: Copy, K: A + Copy"))] enum Enum { V1, V2 { #[educe(Clone(trait = "A"))] f1: K, }, V3( T ), } ``` #### Copy and Clone If you implement both of the `Copy` trait and the `Clone` trait by Educe, the bound for the `Clone` trait needs to include the `Copy` trait due to `Copy, Clone` optimization. ## Deref Use `#[derive(Educe)]` and `#[educe(Deref)]` to implement the `Deref` trait for a struct or an enum. #### Basic Usage You need to assign a field as a default inmutable dereferencing field unless the number of fields is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Deref)] struct Struct { f1: u8, #[educe(Deref)] f2: u8, } #[derive(Educe)] #[educe(Deref)] enum Enum { Struct { f1: u8 }, Struct2 { f1: u8, #[educe(Deref)] f2: u8, }, Tuple(u8), Tuple2( u8, #[educe(Deref)] u8 ), } ``` ## DerefMut Use `#[derive(Educe)]` and `#[educe(DerefMut)]` to implement the `DerefMut` trait for a struct or an enum. #### Basic Usage You need to assign a field as a default mutable dereferencing field unless the number of fields is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Deref, DerefMut)] struct Struct { f1: u8, #[educe(Deref, DerefMut)] f2: u8, } #[derive(Educe)] #[educe(Deref, DerefMut)] enum Enum { Struct { f1: u8 }, Struct2 { f1: u8, #[educe(Deref, DerefMut)] f2: u8, }, Tuple(u8), Tuple2( #[educe(DerefMut)] u8, #[educe(Deref)] u8 ), } ``` The mutable dereferencing fields don't need to be the same as the inmutable dereferencing fields. But their type must be the same. ## TODO There is a lot of work to be done. Unimplemented traits are listed below: 1. `From` 1. `Into` 1. `FromStr` 1. `TryFrom` 1. `TryInto` ## Crates.io https://crates.io/crates/educe ## Documentation https://docs.rs/educe ## License [MIT](LICENSE) educe-0.4.19/src/lib.rs000064400000000000000000001017060072674642500127440ustar 00000000000000/*! # Educe This crate provides procedural macros to help you implement Rust-built-in traits quickly. ## Features By default, every trait this crate supports will be enabled. You can disable all of them by disabling the default features and enable only the traits that you want to use by adding them to `features` explictly. For example, ```toml [dependencies.educe] version = "*" features = ["Debug", "Default", "Hash", "Clone", "Copy"] default-features = false ``` ## Debug Use `#[derive(Educe)]` and `#[educe(Debug)]` to implement the `Debug` trait for a struct, an enum, or a union. It supports to change the name of your types, variants and fields. You can also ignore some fields, or set a trait and/or a method to replace the `Debug` trait used by default. Also, you can even format a struct to a tuple, and vice versa. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Change the Name of a Type, a Variant or a Field The `name` attribute can help you rename a type, a variant or a field. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(name = "Struct2"))] struct Struct { #[educe(Debug(name = "f"))] f1: u8 } #[derive(Educe)] #[educe(Debug(name = true))] enum Enum { #[educe(Debug(name = false))] V1, #[educe(Debug(name = "V"))] V2 { #[educe(Debug(name = "f"))] f1: u8, }, #[educe(Debug(name = false))] V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Struct { #[educe(Debug(ignore))] f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { #[educe(Debug(ignore))] f1: u8, }, V3( #[educe(Debug(ignore))] u8 ), } ``` #### Fake Structs and Tuples With the `named_field` attribute, structs can be formatted as tuples and tuples can be formatted as structs. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(named_field = false))] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Debug)] enum Enum { V1, #[educe(Debug(named_field = false))] V2 { f1: u8, }, #[educe(Debug(named_field = true))] V3( u8, #[educe(Debug(name = "value"))] i32 ), } ``` #### Use Another Method or Trait to Do the Format Thing The `trait` and `method` attributes can be used to replace the `Debug` trait for fields. If you only set the `trait` parameter, the `method` will be set to `fmt` automatically by default. ```rust #[macro_use] extern crate educe; use std::fmt::{self, Formatter}; fn fmt(_s: &u8, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } trait A { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Debug)] enum Enum { V1, V2 { #[educe(Debug(method = "fmt"))] f1: u8, }, V3( #[educe(Debug(trait = "std::fmt::UpperHex"))] u8, #[educe(Debug(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Debug` Trait or Others The `#[educe(Debug(bound))]` attribute can be used to add the `Debug` trait bound to all generic parameters for the `Debug` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; use std::fmt::{self, Formatter}; fn fmt(_s: &u8, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } trait A { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("Hi") } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Debug(bound = "T: std::fmt::Debug, K: A"))] enum Enum { V1, V2 { #[educe(Debug(trait = "A"))] f1: K, }, V3( T ), } ``` #### Union A union will be formatted to a `u8` slice, because we don't know it's field at runtime. The fields of a union cannot be ignored, renamed or formated with other methods or traits. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Debug)] struct Union { f1: u8, f2: i32, } ``` ## PartialEq Use `#[derive(Educe)]` and `#[educe(ParitalEq)]` to implement the `ParitalEq` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `ParitalEq` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq)] struct Struct { #[educe(PartialEq(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { #[educe(PartialEq(ignore))] f1: u8, }, V3( #[educe(PartialEq(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `PartialEq` trait for fields. If you only set the `trait` parameter, the `method` will be set to `eq` automatically by default. ```rust #[macro_use] extern crate educe; fn eq(a: &u8, b: &u8) -> bool { a + 1 == *b } trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq)] enum Enum { V1, V2 { #[educe(PartialEq(method = "eq"))] f1: u8, }, V3( #[educe(PartialEq(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `PartialEq` Trait or Others The `#[educe(PartialEq(bound))]` attribute can be used to add the `PartialEq` trait bound to all generaic parameters for the `PartialEq` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"))] enum Enum { V1, V2 { #[educe(PartialEq(trait = "A"))] f1: K, }, V3( T ), } ``` ## Eq Use `#[derive(Educe)]` and `#[educe(Eq)]` to implement the `Eq` trait for a struct, an enum or a union. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Generic Parameters Bound to the `Eq` Trait or Others The `#[educe(Eq(bound))]` attribute can be used to add the `Eq` trait bound to all generaic parameters for the `Eq` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `Eq` trait depends on the `PartialEq` (`PartialEq`) trait.) ```rust #[macro_use] extern crate educe; trait A { fn eq(&self, b: &Self) -> bool; } impl A for i32 { fn eq(&self, b: &i32) -> bool { self + 1 == *b } } impl A for u64 { fn eq(&self, b: &u64) -> bool { self + 1 == *b } } #[derive(Educe)] #[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"), Eq(bound = "T: std::cmp::PartialEq, K: A"))] enum Enum { V1, V2 { #[educe(PartialEq(trait = "A"))] f1: K, }, V3( T ), } ``` ## PartialOrd Use `#[derive(Educe)]` and `#[educe(PartialOrd)]` to implement the `PartialOrd` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `PartialOrd` trait used by default. The rank of variants and fields can also be modified. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { #[educe(PartialOrd(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { #[educe(PartialOrd(ignore))] f1: u8, }, V3( #[educe(PartialOrd(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `PartialOrd` trait for fields. If you only set the `trait` parameter, the `method` will be set to `partial_cmp` automatically by default. ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; fn partial_cmp(a: &u8, b: &u8) -> Option { if a > b { Some(Ordering::Less) } else if a < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } trait A { fn partial_cmp(&self, b: &Self) -> Option; } impl A for i32 { fn partial_cmp(&self, b: &i32) -> Option { if self > b { Some(Ordering::Less) } else if self < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } } #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { V1, V2 { #[educe(PartialOrd(method = "partial_cmp"))] f1: u8, }, V3( #[educe(PartialOrd(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `PartialOrd` Trait or Others The `#[educe(PartialOrd(bound))]` attribute can be used to add the `PartialOrd` trait bound to all generaic parameters for the `PartialOrd` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), PartialOrd(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `PartialOrd` trait depends on the `PartialEq` (`PartialEq`) trait.) ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; trait A { fn partial_cmp(&self, b: &Self) -> Option; } impl A for i32 { fn partial_cmp(&self, b: &i32) -> Option { if self > b { Some(Ordering::Less) } else if self < b { Some(Ordering::Greater) } else { Some(Ordering::Equal) } } } #[derive(Educe)] #[educe(PartialEq(bound), PartialOrd(bound = "T: std::cmp::PartialOrd, K: std::cmp::PartialOrd + A"))] enum Enum { V1, V2 { #[educe(PartialOrd(trait = "A"))] f1: K, }, V3( T ), } ``` #### Ranking Each field can add a `#[educe(PartialOrd(rank = priority_value))]` attribute where `priority_value` is a positive integer value to determine their comparing precedence (lower `priority_value` leads to higher priority). The default `priority_value` for a field dependends on its ordinal (the lower the front) and is always lower than any custom `priority_value`. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] struct Struct { #[educe(PartialOrd(rank = 1))] f1: u8, #[educe(PartialOrd(rank = 0))] f2: u8, } ``` Each variant can add a `#[educe(PartialOrd(rank = comparison_value))]` attribute where `comparison_value` is a positive integer value to override the value or the ordinal of a variant for comparison. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, PartialOrd)] enum Enum { #[educe(PartialOrd(rank = 2))] Two, #[educe(PartialOrd(rank = 1))] One, } ``` ## Ord Use `#[derive(Educe)]` and `#[educe(Ord)]` to implement the `Ord` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `Ord` trait used by default. The rank of variants and fields can also be modified. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { #[educe(Ord(ignore))] f1: u8 } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { #[educe(Ord(ignore))] f1: u8, }, V3( #[educe(Ord(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Comparing The `trait` and `method` attributes can be used to replace the `Ord` trait for fields. If you only set the `trait` parameter, the `method` will be set to `cmp` automatically by default. ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; fn cmp(a: &u8, b: &u8) -> Ordering { if a > b { Ordering::Less } else if a < b { Ordering::Greater } else { Ordering::Equal } } trait A { fn cmp(&self, b: &Self) -> Ordering; } impl A for i32 { fn cmp(&self, b: &i32) -> Ordering { if self > b { Ordering::Less } else if self < b { Ordering::Greater } else { Ordering::Equal } } } #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { V1, V2 { #[educe(Ord(method = "cmp"))] f1: u8, }, V3( #[educe(Ord(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Ord` Trait or Others The `#[educe(Ord(bound))]` attribute can be used to add the `Ord` trait bound to all generaic parameters for the `Ord` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. (NOTE: The `Ord` trait depends on the `PartialOrd` (`PartialOrd`) trait and the `Eq` trait.) ```rust #[macro_use] extern crate educe; use std::cmp::Ordering; trait A { fn cmp(&self, b: &Self) -> Ordering; } impl A for i32 { fn cmp(&self, b: &i32) -> Ordering { if self > b { Ordering::Less } else if self < b { Ordering::Greater } else { Ordering::Equal } } } #[derive(Educe)] #[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound = "T: std::cmp::Ord, K: std::cmp::Ord + A"))] enum Enum { V1, V2 { #[educe(Ord(trait = "A"))] f1: K, }, V3( T ), } ``` #### Ranking Each field can add a `#[educe(Ord(rank = priority_value))]` attribute where `priority_value` is a positive integer value to determine their comparing precedence (lower `priority_value` leads to higher priority). The default `priority_value` for a field dependends on its ordinal (the lower the front) and is always lower than any custom `priority_value`. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] struct Struct { #[educe(Ord(rank = 1))] f1: u8, #[educe(Ord(rank = 0))] f2: u8, } ``` Each variant can add a `#[educe(Ord(rank = comparison_value))]` attribute where `comparison_value` is a positive integer value to override the value or the ordinal of a variant for comparison. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(PartialEq, Eq, PartialOrd, Ord)] enum Enum { #[educe(Ord(rank = 2))] Two, #[educe(Ord(rank = 1))] One, } ``` ## Hash Use `#[derive(Educe)]` and `#[educe(Hash)]` to implement the `Hash` trait for a struct or an enum. It supports to ignore some fields, or set a trait and/or a method to replace the `Hash` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Ignore Fields The `ignore` attribute can ignore specific fields. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash)] struct Struct { #[educe(Hash(ignore))] f1: u8 } #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { #[educe(Hash(ignore))] f1: u8, }, V3( #[educe(Hash(ignore))] u8 ), } ``` #### Use Another Method or Trait to Do Hashing The `trait` and `method` attributes can be used to replace the `Hash` trait for fields. If you only set the `trait` parameter, the `method` will be set to `hash` automatically by default. ```rust #[macro_use] extern crate educe; use std::hash::{Hash, Hasher}; fn hash(_s: &u8, state: &mut H) { Hash::hash(&100, state) } trait A { fn hash(&self, state: &mut H) { Hash::hash(&100, state) } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Hash)] enum Enum { V1, V2 { #[educe(Hash(method = "hash"))] f1: u8, }, V3( #[educe(Hash(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Hash` Trait or Others The `#[educe(Hash(bound))]` attribute can be used to add the `Hash` trait bound to all generaic parameters for the `Hash` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Hash(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; use std::hash::{Hash, Hasher}; fn hash(_s: &u8, state: &mut H) { Hash::hash(&100, state) } trait A { fn hash(&self, state: &mut H) { Hash::hash(&100, state) } } impl A for i32 {}; impl A for u64 {}; #[derive(Educe)] #[educe(Hash(bound = "T: std::hash::Hash, K: A"))] enum Enum { V1, V2 { #[educe(Hash(trait = "A"))] f1: K, }, V3( T ), } ``` ## Default Use `#[derive(Educe)]` and `#[educe(Default)]` to implement the `Default` trait for a struct, an enum, or a union. It supports to set the default value for your type directly, or set the default values for specific fields. #### Basic Usage For enums and unions, you need to assign a variant (of a enum) and a field (of a union) as default unless the number of variants of an enum or the number of fields of a union is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Default)] enum Enum { V1, #[educe(Default)] V2 { f1: u8, }, V3(u8), } #[derive(Educe)] #[educe(Default)] union Union { f1: u8, #[educe(Default)] f2: f64, } ``` #### The Default Value for the Whole Type The `#[educe(Default(expression = "expression"))]` attribute can be used to set the default value for your type by an expression. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(expression = "Struct { f1: 1 }"))] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Default(expression = "Enum::Struct { f1: 1 }"))] enum Enum { Unit, Struct { f1: u8 }, Tuple(u8), } #[derive(Educe)] #[educe(Default(expression = "Union { f1: 1 }"))] union Union { f1: u8, f2: f64, } ``` #### The Default Values for Specific Fields The `#[educe(Default = literal)]` attribute or the `#[educe(Default(expression = "expression"))]` attribute can be used to set the default value for a specific field by a literal value or an expression. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default)] struct Struct { #[educe(Default = 1)] f1: u8, #[educe(Default = 11111111111111111111111111111)] f2: i128, #[educe(Default = 1.1)] f3: f64, #[educe(Default = true)] f4: bool, #[educe(Default = "Hi")] f5: &'static str, #[educe(Default = "Hello")] f6: String, #[educe(Default = 'M')] f7: char, } #[derive(Educe)] #[educe(Default)] enum Enum { Unit, #[educe(Default)] Tuple( #[educe(Default(expression = "0 + 1"))] u8, #[educe(Default(expression = "-11111111111111111111111111111 * -1"))] i128, #[educe(Default(expression = "1.0 + 0.1"))] f64, #[educe(Default(expression = "!false"))] bool, #[educe(Default(expression = "\"Hi\""))] &'static str, #[educe(Default(expression = "String::from(\"Hello\")"))] String, #[educe(Default(expression = "'M'"))] char, ), } #[derive(Educe)] #[educe(Default)] union Union { f1: u8, f2: i128, f3: f64, f4: bool, #[educe(Default = "Hi")] f5: &'static str, f6: char, } ``` #### Generic Parameters Bound to the `Default` Trait or Others The `#[educe(Default(bound))]` attribute can be used to add the `Default` trait bound to all generaic parameters for the `Default` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(bound))] enum Enum { Unit, #[educe(Default)] Struct { f1: T }, Tuple(T), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(bound = "T: std::default::Default"))] enum Enum { Unit, #[educe(Default)] Struct { f1: T }, Tuple(T), } ``` #### The `new` Associated Function With the `#[educe(Default(new))]` attribute, your type will have an extra associated function called `new`. That can be used to invoke the `default` method of the `Default` trait. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Default(new))] struct Struct { f1: u8 } ``` ## Clone Use `#[derive(Educe)]` and `#[educe(Clone)]` to implement the `Clone` trait for a struct, an enum, or a union. It supports to set a trait and/or a method to replace the `Clone` trait used by default. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Clone)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Clone)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Use Another Method or Trait to Do Cloning The `trait` and `method` attributes can be used to replace the `Clone` trait for fields. If you only set the `trait` parameter, the `method` will be set to `clone` automatically by default. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Clone)] enum Enum { V1, V2 { #[educe(Clone(method = "clone"))] f1: u8, }, V3( #[educe(Clone(trait = "A"))] T ), } ``` #### Generic Parameters Bound to the `Clone` Trait or Others The `#[educe(Clone(bound))]` attribute can be used to add the `Clone` trait bound or the `Copy` trait bound (if the `#[educe(Copy)]` attribute exists) to all generaic parameters for the `Clone` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Clone(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Clone(bound = "T: std::clone::Clone, K: A"))] enum Enum { V1, V2 { #[educe(Clone(trait = "A"))] f1: K, }, V3( T ), } ``` #### Union The `#[educe(Clone)]` attribute can be used for a union which also needs to implement the `Copy` trait. The fields of a union cannot be cloned with other methods or traits. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy, Clone)] union Union { f1: u8, } ``` ## Copy Use `#[derive(Educe)]` and `#[educe(Copy)]` to implement the `Copy` trait for a struct, an enum, or a union. #### Basic Usage ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy, Clone)] struct Struct { f1: u8 } #[derive(Educe)] #[educe(Copy, Clone)] enum Enum { V1, V2 { f1: u8, }, V3(u8), } ``` #### Generic Parameters Bound to the `Copy` Trait or Others The `#[educe(Copy(bound))]` attribute can be used to add the `Copy` trait bound to all generaic parameters for the `Copy` implementation. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Copy(bound), Clone(bound))] enum Enum { V1, V2 { f1: K, }, V3( T ), } ``` Or you can set the where predicates by yourself. ```rust #[macro_use] extern crate educe; fn clone(v: &u8) -> u8 { v + 100 } trait A { fn clone(&self) -> Self; } impl A for i32 { fn clone(&self) -> i32 { self + 100 } } impl A for u64 { fn clone(&self) -> u64 { self + 100 } } #[derive(Educe)] #[educe(Copy(bound = "T: Copy, K: A + Copy"), Clone(bound = "T: Copy, K: A + Copy"))] enum Enum { V1, V2 { #[educe(Clone(trait = "A"))] f1: K, }, V3( T ), } ``` #### Copy and Clone If you implement both of the `Copy` trait and the `Clone` trait by Educe, the bound for the `Clone` trait needs to include the `Copy` trait due to `Copy, Clone` optimization. ## Deref Use `#[derive(Educe)]` and `#[educe(Deref)]` to implement the `Deref` trait for a struct or an enum. #### Basic Usage You need to assign a field as a default inmutable dereferencing field unless the number of fields is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Deref)] struct Struct { f1: u8, #[educe(Deref)] f2: u8, } #[derive(Educe)] #[educe(Deref)] enum Enum { Struct { f1: u8 }, Struct2 { f1: u8, #[educe(Deref)] f2: u8, }, Tuple(u8), Tuple2( u8, #[educe(Deref)] u8 ), } ``` ## DerefMut Use `#[derive(Educe)]` and `#[educe(DerefMut)]` to implement the `DerefMut` trait for a struct or an enum. #### Basic Usage You need to assign a field as a default mutable dereferencing field unless the number of fields is exactly one. ```rust #[macro_use] extern crate educe; #[derive(Educe)] #[educe(Deref, DerefMut)] struct Struct { f1: u8, #[educe(Deref, DerefMut)] f2: u8, } #[derive(Educe)] #[educe(Deref, DerefMut)] enum Enum { Struct { f1: u8 }, Struct2 { f1: u8, #[educe(Deref, DerefMut)] f2: u8, }, Tuple(u8), Tuple2( #[educe(DerefMut)] u8, #[educe(Deref)] u8 ), } ``` The mutable dereferencing fields don't need to be the same as the inmutable dereferencing fields. But their type must be the same. ## TODO There is a lot of work to be done. Unimplemented traits are listed below: 1. `From` 1. `Into` 1. `FromStr` 1. `TryFrom` 1. `TryInto` */ #![recursion_limit = "128"] #[macro_use] extern crate enum_ordinalize; mod panic; mod support_traits; mod trait_handlers; use std::collections::BTreeMap; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{DeriveInput, Meta, NestedMeta}; use support_traits::Trait; use trait_handlers::TraitHandler; fn derive_input_handler(ast: DeriveInput) -> TokenStream { let mut tokens = TokenStream::new(); let mut trait_meta_map: BTreeMap = BTreeMap::new(); for attr in ast.attrs.iter() { if let Some(attr_meta_name) = attr.path.get_ident() { if attr_meta_name == "educe" { let attr_meta = attr.parse_meta().unwrap(); match attr_meta { Meta::List(list) => { for p in list.nested { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if trait_meta_map.contains_key(&t) { panic::reuse_a_trait(t); } trait_meta_map.insert(t, meta); } NestedMeta::Lit(_) => { panic::educe_format_incorrect(); } } } } _ => { panic::educe_format_incorrect(); } } } } } let traits: Vec = trait_meta_map.keys().copied().collect(); #[cfg(feature = "Debug")] { if let Some(meta) = trait_meta_map.get(&Trait::Debug) { trait_handlers::debug::DebugHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "PartialEq")] { if let Some(meta) = trait_meta_map.get(&Trait::PartialEq) { trait_handlers::partial_eq::PartialEqHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "Eq")] { if let Some(meta) = trait_meta_map.get(&Trait::Eq) { trait_handlers::eq::EqHandler::trait_meta_handler(&ast, &mut tokens, &traits, meta); } } #[cfg(feature = "PartialOrd")] { if let Some(meta) = trait_meta_map.get(&Trait::PartialOrd) { trait_handlers::partial_ord::PartialOrdHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "Ord")] { if let Some(meta) = trait_meta_map.get(&Trait::Ord) { trait_handlers::ord::OrdHandler::trait_meta_handler(&ast, &mut tokens, &traits, meta); } } #[cfg(feature = "Hash")] { if let Some(meta) = trait_meta_map.get(&Trait::Hash) { trait_handlers::hash::HashHandler::trait_meta_handler(&ast, &mut tokens, &traits, meta); } } #[cfg(feature = "Default")] { if let Some(meta) = trait_meta_map.get(&Trait::Default) { trait_handlers::default::DefaultHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "Clone")] { if let Some(meta) = trait_meta_map.get(&Trait::Clone) { trait_handlers::clone::CloneHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "Copy")] { if let Some(meta) = trait_meta_map.get(&Trait::Copy) { trait_handlers::copy::CopyHandler::trait_meta_handler(&ast, &mut tokens, &traits, meta); } } #[cfg(feature = "Deref")] { if let Some(meta) = trait_meta_map.get(&Trait::Deref) { trait_handlers::deref::DerefHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } #[cfg(feature = "DerefMut")] { if let Some(meta) = trait_meta_map.get(&Trait::DerefMut) { trait_handlers::deref_mut::DerefMutHandler::trait_meta_handler( &ast, &mut tokens, &traits, meta, ); } } if tokens.is_empty() { panic::derive_attribute_not_set_up_yet("Educe"); } tokens } #[proc_macro_derive(Educe, attributes(educe))] pub fn educe_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { derive_input_handler(syn::parse(input).unwrap()).into() } educe-0.4.19/src/panic.rs000064400000000000000000000130100072674642500132560ustar 00000000000000#![cfg_attr(not(feature = "default"), allow(dead_code))] use crate::Trait; #[inline] pub fn reuse_a_trait(t: Trait) -> ! { panic!("The trait `{:?}` is repeatedly used.", t) } #[inline] pub fn trait_not_used(t: Trait) -> ! { panic!("The `{:?}` trait is not used.", t) } #[inline] pub fn trait_not_support_union(t: Trait) -> ! { panic!("The `{:?}` trait does not support to a union.", t) } #[inline] pub fn attribute_incorrect_format(attribute_name: &str, correct_usage: &[&str]) -> ! { panic!( "You are using an incorrect format of the `{}` attribute.{}", attribute_name, concat_string_slice_array(correct_usage) ) } #[inline] pub fn parameter_incorrect_format(parameter_name: &str, correct_usage: &[&str]) -> ! { panic!( "You are using an incorrect format of the `{}` parameter.{}", parameter_name, concat_string_slice_array(correct_usage) ) } #[inline] pub fn derive_attribute_not_set_up_yet(attribute_name: &str) -> ! { panic!( "You are using `{}` in the `derive` attribute, but it has not been set up yet.", attribute_name ) } #[inline] pub fn reset_parameter(parameter_name: &str) -> ! { panic!("Try to reset the `{}` parameter.", parameter_name) } #[inline] pub fn unknown_parameter(attribute_name: &str, parameter_name: &str) -> ! { panic!("Unknown parameter `{}` used in the `{}` attribute.", parameter_name, attribute_name) } #[inline] pub fn set_value_expression() -> ! { panic!("The default value and the expression parameter can not be set at the same time.") } #[inline] pub fn set_expression_bound() -> ! { panic!("You don't need to set the expression and the bound at the same time.") } #[inline] pub fn no_default_field() -> ! { panic!("There is no field set as default.") } #[inline] pub fn multiple_default_fields() -> ! { panic!("Multiple default fields are set.") } #[inline] pub fn no_default_variant() -> ! { panic!("There is no variant set as default.") } #[inline] pub fn multiple_default_variants() -> ! { panic!("Multiple default variants are set.") } #[inline] pub fn no_deref_field() -> ! { panic!("There is no field which is assigned for `Deref`.") } #[inline] pub fn no_deref_field_of_variant(variant_name: &str) -> ! { panic!( "There is no field for the `{variant_name}` variant which is assigned for `Deref`.", variant_name = variant_name ) } #[inline] pub fn multiple_deref_fields() -> ! { panic!("Multiple fields are set for `Deref`.") } #[inline] pub fn multiple_deref_fields_of_variant(variant_name: &str) -> ! { panic!( "Multiple fields of the `{variant_name}` variant are set for deref.", variant_name = variant_name ) } #[inline] pub fn deref_cannot_support_unit_variant() -> ! { panic!("The `Deref` trait cannot be implemented for an enum which has unit variants.") } #[inline] pub fn no_deref_mut_field() -> ! { panic!("There is no field which is assigned for `DerefMut`.") } #[inline] pub fn no_deref_mut_field_of_variant(variant_name: &str) -> ! { panic!( "There is no field for the `{variant_name}` variant which is assigned for `DerefMut`.", variant_name = variant_name ) } #[inline] pub fn multiple_deref_mut_fields() -> ! { panic!("Multiple fields are set for `DerefMut`.") } #[inline] pub fn multiple_deref_mut_fields_of_variant(variant_name: &str) -> ! { panic!( "Multiple fields of the `{variant_name}` variant are set for `DerefMut`.", variant_name = variant_name ) } #[inline] pub fn deref_mut_cannot_support_unit_variant() -> ! { panic!("The `DerefMut` trait cannot be implemented for an enum which has unit variants.") } #[inline] pub fn disable_named_field_name() -> ! { panic!("You can't disable the name of a named field.") } #[inline] pub fn empty_parameter(parameter_name: &str) -> ! { panic!("You can't set the `{}` parameter to empty.", parameter_name) } #[inline] pub fn unit_struct_need_name() -> ! { panic!("A unit struct needs to have a name.") } #[inline] pub fn unit_enum_need_name() -> ! { panic!("A unit enum needs to have a name.") } #[inline] pub fn unit_variant_need_name() -> ! { panic!("A unit variant which doesn't use an enum name needs to have a name.") } #[inline] pub fn ignore_ranked_field() -> ! { panic!("You can't ignore a ranked field.") } #[inline] pub fn reuse_a_rank(rank: isize) -> ! { panic!("The rank `{}` is repeatedly used.", rank) } #[inline] pub fn reuse_a_value(value: isize) -> ! { panic!("The value `{}` is repeatedly used.", value) } // TODO patterns #[inline] pub fn educe_format_incorrect() -> ! { attribute_incorrect_format("educe", &[stringify!(#[educe(Trait1, Trait2, ..., TraitN)])]) } fn concat_string_slice_array(array: &[&str]) -> String { let len = array.len(); if len == 0 { String::new() } else { let mut string = String::from(" It needs to be formed into "); let mut iter = array.iter(); let first = iter.next().unwrap(); string.push('`'); string.push_str(&first.replace('\n', "")); string.push('`'); if len > 2 { for s in iter.take(len - 2) { string.push_str(", `"); string.push_str(&s.replace('\n', "")); string.push('`'); } } if len > 1 { string.push_str(", or `"); string.push_str(&array[len - 1].replace('\n', "")); string.push('`'); } string.push('.'); string } } educe-0.4.19/src/support_traits.rs000064400000000000000000000040070072674642500152740ustar 00000000000000#[cfg(not(any( feature = "Debug", feature = "PartialEq", feature = "Eq", feature = "PartialOrd", feature = "Ord", feature = "Hash", feature = "Default", feature = "Clone", feature = "Copy", feature = "Deref", feature = "DerefMut" )))] compile_error!("at least one of the trait features must be enabled"); #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Ordinalize)] #[cfg_attr(not(feature = "default"), allow(dead_code))] pub enum Trait { #[cfg(feature = "Debug")] Debug, #[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 = "Clone")] Clone, #[cfg(feature = "Copy")] Copy, #[cfg(feature = "Deref")] Deref, #[cfg(feature = "DerefMut")] DerefMut, } impl Trait { #[inline] pub fn from_str>(s: S) -> Trait { let s = s.as_ref(); match s { #[cfg(feature = "Debug")] "Debug" => Trait::Debug, #[cfg(feature = "PartialEq")] "PartialEq" => Trait::PartialEq, #[cfg(feature = "Eq")] "Eq" => Trait::Eq, #[cfg(feature = "PartialOrd")] "PartialOrd" => Trait::PartialOrd, #[cfg(feature = "Ord")] "Ord" => Trait::Ord, #[cfg(feature = "Hash")] "Hash" => Trait::Hash, #[cfg(feature = "Default")] "Default" => Trait::Default, #[cfg(feature = "Clone")] "Clone" => Trait::Clone, #[cfg(feature = "Copy")] "Copy" => Trait::Copy, #[cfg(feature = "Deref")] "Deref" => Trait::Deref, #[cfg(feature = "DerefMut")] "DerefMut" => Trait::DerefMut, _ => panic!("Unsupported trait `{}`. Available traits are {:?}", s, Trait::variants()), } } } educe-0.4.19/src/trait_handlers/clone/clone_enum.rs000064400000000000000000000523660072674642500204340ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Generics, Meta}; pub struct CloneEnumHandler; impl TraitHandler for CloneEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_clone_meta(meta); let mut bound = Punctuated::new(); let mut clone_tokens = TokenStream::new(); let mut clone_from_tokens = TokenStream::new(); if let Data::Enum(data) = &ast.data { let mut variant_idents = Vec::new(); let mut field_attributes_names: Vec<(bool, Vec, Vec)> = 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, } .from_attributes(&variant.attrs, traits); let mut field_attributes = Vec::new(); let mut field_names = Vec::new(); let mut is_tuple = true; match &variant.fields { Fields::Unit => (), Fields::Named(fields) => { // TODO Struct is_tuple = false; for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); #[cfg(feature = "Copy")] if field_attribute.clone_method.is_some() { has_custom_clone_method = true; } field_attributes.push(field_attribute); field_names.push(field_name); } } Fields::Unnamed(fields) => { // TODO Tuple for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = format!("_{}", index); #[cfg(feature = "Copy")] if field_attribute.clone_method.is_some() { has_custom_clone_method = true; } field_attributes.push(field_attribute); field_names.push(field_name); } } } variant_idents.push(variant.ident.to_string()); field_attributes_names.push((is_tuple, field_attributes, field_names)); } let enum_name = ast.ident.to_string(); #[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 { bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters_with_copy( &ast.generics.params, ); clone_tokens.extend(quote!(*self)); let mut match_tokens = String::from("match self {"); for (index, variant_ident) in variant_idents.iter().enumerate() { let field_attributes_names = &field_attributes_names[index]; let is_tuple = field_attributes_names.0; let field_attributes = &field_attributes_names.1; let field_names = &field_attributes_names.2; let is_unit = field_names.is_empty(); if is_unit { match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ if let {enum_name}::{variant_ident} = _source {{ done = true; }} }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } else { let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); if is_tuple { for field_name in field_names { pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "___{field_name},", field_name = field_name )) .unwrap(); } } else { for field_name in field_names { pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: ___{field_name},", field_name = field_name )) .unwrap(); } } let mut block_tokens = String::new(); for (index, field_attribute) in field_attributes.iter().enumerate() { let field_name = &field_names[index]; let clone_trait = &field_attribute.clone_trait; let clone_method = &field_attribute.clone_method; match clone_trait { Some(clone_trait) => { let clone_method = clone_method.as_ref().unwrap(); block_tokens.write_fmt(format_args!("*{field_name} = {clone_trait}::{clone_method}(___{field_name});", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); } None => { match clone_method { Some(clone_method) => { block_tokens.write_fmt(format_args!("*{field_name} = {clone_method}(___{field_name});", clone_method = clone_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("core::clone::Clone::clone_from({field_name}, ___{field_name});", field_name = field_name)).unwrap(); } } } } } if is_tuple { match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} ( {pattern_tokens} ) => {{ if let {enum_name}::{variant_ident} ( {pattern_2_tokens} ) = _source {{ {block_tokens} done = true; }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens)).unwrap(); } else { match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ if let {enum_name}::{variant_ident} {{ {pattern_2_tokens} }} = _source {{ {block_tokens} }} done = true; }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens)).unwrap(); } } } match_tokens.push('}'); clone_from_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); } else { bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut clone_match_tokens = String::from("match self {"); let mut clone_from_match_tokens = String::from("match self {"); for (index, variant_ident) in variant_idents.iter().enumerate() { let field_attributes_names = &field_attributes_names[index]; let is_tuple = field_attributes_names.0; let field_attributes = &field_attributes_names.1; let field_names = &field_attributes_names.2; let is_unit = field_names.is_empty(); if is_unit { clone_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ {enum_name}::{variant_ident} }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); clone_from_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ if let {enum_name}::{variant_ident} = _source {{ done = true; }} }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } else { let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut pattern_3_tokens = String::new(); if is_tuple { let mut clone = format!( "{enum_name}::{variant_ident}(", enum_name = enum_name, variant_ident = variant_ident ); let mut clone_from = String::new(); for field_name in field_names { pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_3_tokens .write_fmt(format_args!( "___{field_name},", field_name = field_name )) .unwrap(); } for (index, field_attribute) in field_attributes.iter().enumerate() { let field_name = &field_names[index]; let clone_trait = &field_attribute.clone_trait; let clone_method = &field_attribute.clone_method; match clone_trait { Some(clone_trait) => { let clone_method = clone_method.as_ref().unwrap(); clone .write_fmt(format_args!( "{clone_trait}::{clone_method}({field_name}),", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name )) .unwrap(); clone_from.write_fmt(format_args!("*{field_name} = {clone_trait}::{clone_method}(___{field_name});", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); } None => { match clone_method { Some(clone_method) => { clone .write_fmt(format_args!( "{clone_method}({field_name}),", clone_method = clone_method, field_name = field_name )) .unwrap(); clone_from.write_fmt(format_args!("*{field_name} = {clone_method}(___{field_name});", clone_method = clone_method, field_name = field_name)).unwrap(); } None => { clone .write_fmt(format_args!( "core::clone::Clone::clone({field_name}),", field_name = field_name )) .unwrap(); clone_from.write_fmt(format_args!("core::clone::Clone::clone_from({field_name}, ___{field_name});", field_name = field_name)).unwrap(); } } } } } clone.push(')'); clone_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} ( {pattern_tokens} ) => {{ {clone} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, clone = clone)).unwrap(); clone_from_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} ( {pattern_2_tokens} ) => {{ if let {enum_name}::{variant_ident} ( {pattern_3_tokens} ) = _source {{ {block_tokens} done = true; }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_2_tokens = pattern_2_tokens, pattern_3_tokens = pattern_3_tokens, block_tokens = clone_from)).unwrap(); } else { let mut clone = format!( "{enum_name}::{variant_ident} {{", enum_name = enum_name, variant_ident = variant_ident ); let mut clone_from = String::new(); for field_name in field_names { pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_3_tokens .write_fmt(format_args!( "{field_name}: ___{field_name},", field_name = field_name )) .unwrap(); } for (index, field_attribute) in field_attributes.iter().enumerate() { let field_name = &field_names[index]; let clone_trait = &field_attribute.clone_trait; let clone_method = &field_attribute.clone_method; match clone_trait { Some(clone_trait) => { let clone_method = clone_method.as_ref().unwrap(); clone.write_fmt(format_args!("{field_name}: {clone_trait}::{clone_method}({field_name}),", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("*{field_name} = {clone_trait}::{clone_method}(___{field_name});", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); } None => { match clone_method { Some(clone_method) => { clone.write_fmt(format_args!("{field_name}: {clone_method}({field_name}),", clone_method = clone_method, field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("*{field_name} = {clone_method}(___{field_name});", clone_method = clone_method, field_name = field_name)).unwrap(); } None => { clone.write_fmt(format_args!("{field_name}: core::clone::Clone::clone({field_name}),", field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("core::clone::Clone::clone_from({field_name}, ___{field_name});", field_name = field_name)).unwrap(); } } } } } clone.push('}'); clone_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ {clone} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, clone = clone)).unwrap(); clone_from_match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_2_tokens} }} => {{ if let {enum_name}::{variant_ident} {{ {pattern_3_tokens} }} = _source {{ {block_tokens} }} done = true; }}", enum_name = enum_name, variant_ident = variant_ident, pattern_2_tokens = pattern_2_tokens, pattern_3_tokens = pattern_3_tokens, block_tokens = clone_from)).unwrap(); } } } clone_match_tokens.push('}'); clone_from_match_tokens.push('}'); clone_tokens.extend(TokenStream::from_str(&clone_match_tokens).unwrap()); clone_from_tokens.extend(TokenStream::from_str(&clone_from_match_tokens).unwrap()); } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::clone::Clone for #ident #ty_generics #where_clause { #[inline] fn clone(&self) -> Self { #clone_tokens } #[inline] fn clone_from(&mut self, _source: &Self) { let mut done = false; #clone_from_tokens if !done { *self = core::clone::Clone::clone(_source); } } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/clone/clone_struct.rs000064400000000000000000000231050072674642500210010ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Generics, Meta}; pub struct CloneStructHandler; impl TraitHandler for CloneStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_clone_meta(meta); let mut bound = Punctuated::new(); let mut clone_tokens = TokenStream::new(); let mut clone_from_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { let mut field_attributes = Vec::new(); let mut field_names = Vec::new(); #[cfg(feature = "Copy")] let mut has_custom_clone_method = false; for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; #[cfg(feature = "Copy")] if field_attribute.clone_method.is_some() { has_custom_clone_method = true; } field_attributes.push(field_attribute); field_names.push(field_name); } #[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 { bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters_with_copy( &ast.generics.params, ); clone_tokens.extend(quote!(*self)); let mut clone_from = String::new(); for field_name in field_names { clone_from.write_fmt(format_args!("core::clone::Clone::clone_from(&mut self.{field_name}, &_source.{field_name});", field_name = field_name)).unwrap(); } clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap()); } else { bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); match &data.fields { Fields::Unit => { let ident = &ast.ident; clone_tokens.extend(quote!(#ident)); } Fields::Unnamed(_) => { let mut clone = ast.ident.to_string(); let mut clone_from = String::new(); clone.push('('); for (index, field_attribute) in field_attributes.into_iter().enumerate() { let field_name = &field_names[index]; let clone_trait = field_attribute.clone_trait; let clone_method = field_attribute.clone_method; match clone_trait { Some(clone_trait) => { let clone_method = clone_method.unwrap(); clone .write_fmt(format_args!( "{clone_trait}::{clone_method}(&self.{field_name}),", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name )) .unwrap(); clone_from.write_fmt(format_args!("self.{field_name} = {clone_trait}::{clone_method}(&_source.{field_name});", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); } None => { match clone_method { Some(clone_method) => { clone .write_fmt(format_args!( "{clone_method}(&self.{field_name}),", clone_method = clone_method, field_name = field_name )) .unwrap(); clone_from.write_fmt(format_args!("self.{field_name} = {clone_method}(&_source.{field_name});", clone_method = clone_method, field_name = field_name)).unwrap(); } None => { clone.write_fmt(format_args!("core::clone::Clone::clone(&self.{field_name}),", field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("core::clone::Clone::clone_from(&mut self.{field_name}, &_source.{field_name});", field_name = field_name)).unwrap(); } } } } } clone.push(')'); clone_tokens.extend(TokenStream::from_str(&clone).unwrap()); clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap()); } Fields::Named(_) => { let mut clone = ast.ident.to_string(); let mut clone_from = String::new(); clone.push('{'); for (index, field_attribute) in field_attributes.into_iter().enumerate() { let field_name = &field_names[index]; let clone_trait = field_attribute.clone_trait; let clone_method = field_attribute.clone_method; match clone_trait { Some(clone_trait) => { let clone_method = clone_method.unwrap(); clone.write_fmt(format_args!("{field_name}: {clone_trait}::{clone_method}(&self.{field_name}),", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("self.{field_name} = {clone_trait}::{clone_method}(&_source.{field_name});", clone_trait = clone_trait, clone_method = clone_method, field_name = field_name)).unwrap(); } None => { match clone_method { Some(clone_method) => { clone.write_fmt(format_args!("{field_name}: {clone_method}(&self.{field_name}),", clone_method = clone_method, field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("self.{field_name} = {clone_method}(&_source.{field_name});", clone_method = clone_method, field_name = field_name)).unwrap(); } None => { clone.write_fmt(format_args!("{field_name}: core::clone::Clone::clone(&self.{field_name}),", field_name = field_name)).unwrap(); clone_from.write_fmt(format_args!("core::clone::Clone::clone_from(&mut self.{field_name}, &_source.{field_name});", field_name = field_name)).unwrap(); } } } } } clone.push('}'); clone_tokens.extend(TokenStream::from_str(&clone).unwrap()); clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap()); } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::clone::Clone for #ident #ty_generics #where_clause { #[inline] fn clone(&self) -> Self { #clone_tokens } #[inline] fn clone_from(&mut self, _source: &Self) { #clone_from_tokens } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/clone/clone_union.rs000064400000000000000000000024030072674642500206030ustar 00000000000000use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Meta}; pub struct CloneUnionHandler; impl TraitHandler for CloneUnionHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let _ = TypeAttributeBuilder { enable_flag: true, enable_bound: false, } .from_clone_meta(meta); if let Data::Union(data) = &ast.data { for field in data.fields.named.iter() { let _ = FieldAttributeBuilder { enable_impl: false, } .from_attributes(&field.attrs, traits); } } let ident = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::clone::Clone for #ident #ty_generics #where_clause { #[inline] fn clone(&self) -> Self { *self } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/clone/mod.rs000064400000000000000000000016270072674642500170610ustar 00000000000000mod models; mod clone_enum; mod clone_struct; mod clone_union; use super::TraitHandler; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use clone_enum::CloneEnumHandler; use clone_struct::CloneStructHandler; use clone_union::CloneUnionHandler; pub struct CloneHandler; impl TraitHandler for CloneHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { CloneStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { CloneEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => { CloneUnionHandler::trait_meta_handler(ast, tokens, traits, meta); } } } } educe-0.4.19/src/trait_handlers/clone/models/field_attribute.rs000064400000000000000000000301400072674642500227230ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub struct FieldAttribute { pub clone_method: Option, pub clone_trait: Option, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_impl: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_clone_meta(&self, meta: &Meta) -> FieldAttribute { let mut clone_method = None; let mut clone_trait = None; let correct_usage_for_clone_attribute = { let usage = vec![]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(Clone(method = "path_to_method"))]), stringify!(#[educe(Clone(trait = "path_to_trait"))]), stringify!(#[educe(Clone(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(Clone(method("path_to_method")))]), stringify!(#[educe(Clone(trait("path_to_trait")))]), stringify!(#[educe(Clone(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "method" => { if !self.enable_impl { panic::unknown_parameter("Clone", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if clone_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { clone_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if clone_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { clone_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("Clone", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if clone_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { clone_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if clone_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { clone_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => panic::unknown_parameter("Clone", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Clone", &correct_usage_for_clone_attribute, ) } } } } _ => panic::attribute_incorrect_format("Clone", &correct_usage_for_clone_attribute), } if clone_trait.is_some() && clone_method.is_none() { clone_method = Some("clone".to_string()); } FieldAttribute { clone_method, clone_trait, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Clone { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_clone_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { clone_method: None, clone_trait: None, }) } } educe-0.4.19/src/trait_handlers/clone/models/mod.rs000064400000000000000000000001410072674642500203320ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/clone/models/type_attribute.rs000064400000000000000000000256260072674642500226360ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::clone::Clone)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } pub fn into_punctuated_where_predicates_by_generic_parameters_with_copy( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::marker::Copy)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_clone_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut bound = TypeAttributeBound::None; let correct_usage_for_clone_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Clone)])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Clone(bound))]), stringify!(#[educe(Clone(bound = "where_predicates"))]), stringify!(#[educe(Clone(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("Clone", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("Clone", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Clone", &correct_usage_for_clone_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Clone", &correct_usage_for_clone_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Clone", &correct_usage_for_clone_attribute); } flag = true; } } TypeAttribute { flag, bound, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Clone { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_clone_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, bound: TypeAttributeBound::None, }) } } educe-0.4.19/src/trait_handlers/copy/mod.rs000064400000000000000000000023000072674642500167200ustar 00000000000000mod models; use super::TraitHandler; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{DeriveInput, Generics, Meta}; use models::TypeAttributeBuilder; pub struct CopyHandler; impl TraitHandler for CopyHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, _traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_bound: true, } .from_copy_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let copy_impl = quote! { impl #impl_generics core::marker::Copy for #ident #ty_generics #where_clause { } }; tokens.extend(copy_impl); } } educe-0.4.19/src/trait_handlers/copy/models/mod.rs000064400000000000000000000000600072674642500202040ustar 00000000000000mod type_attribute; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/copy/models/type_attribute.rs000064400000000000000000000201510072674642500224740ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::marker::Copy)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_copy_meta(&self, meta: &Meta) -> TypeAttribute { let mut bound = TypeAttributeBound::None; let correct_usage_for_copy_attribute = { let usage = vec![stringify!(#[educe(Copy)])]; usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Copy(bound))]), stringify!(#[educe(Copy(bound = "where_predicates"))]), stringify!(#[educe(Copy(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("Copy", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("Copy", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Copy", &correct_usage_for_copy_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Copy", &correct_usage_for_copy_attribute) } Meta::Path(_) => (), } TypeAttribute { bound, } } } educe-0.4.19/src/trait_handlers/debug/debug_enum.rs000064400000000000000000000767730072674642500204200ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{ FieldAttributeBuilder, FieldAttributeName, TypeAttributeBuilder, TypeAttributeName, }; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct DebugEnumHandler; impl TraitHandler for DebugEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, name: TypeAttributeName::Disable, enable_name: true, named_field: false, enable_named_field: false, enable_bound: true, } .from_debug_meta(meta); let enum_name = ast.ident.to_string(); let name = type_attribute.name.into_string_by_ident(&ast.ident); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); let mut has_variants = false; let mut match_tokens = String::from("match self {"); if let Data::Enum(data) = &ast.data { for variant in data.variants.iter() { let type_attribute = TypeAttributeBuilder { enable_flag: false, name: TypeAttributeName::Default, enable_name: true, named_field: matches!(&variant.fields, Fields::Named(_)), enable_named_field: true, enable_bound: false, } .from_attributes(&variant.attrs, traits); let variant_name = type_attribute.name.into_string_by_ident(&variant.ident); let named_field = type_attribute.named_field; let variant_ident = variant.ident.to_string(); let name = combine_names(&name, variant_name); match &variant.fields { Fields::Unit => { // TODO Unit if name.is_empty() { panic::unit_variant_need_name(); } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ formatter.write_str({name:?}) }}", enum_name = enum_name, variant_ident = variant_ident, name = name)).unwrap(); has_variants = true; } Fields::Named(fields) => { // TODO Struct let mut has_fields = false; let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); if named_field { if name.is_empty() { block_tokens.push_str(" struct RawString(&'static str); impl core::fmt::Debug for RawString {{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{ f.write_str(self.0) }} }} "); block_tokens.push_str("let mut builder = formatter.debug_map();"); } else { block_tokens .write_fmt(format_args!( "let mut builder = formatter.debug_struct({name:?});", name = name )) .unwrap(); } for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: true, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } let rename = field_attribute.name.into_option_string(); let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; let key = rename.unwrap_or_else(|| field_name.clone()); pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug({field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap(); let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &arg);", key = key ) } else { format!("builder.field({key:?}, &arg);", key = key) }; block_tokens.push_str(&statement); } None => { match format_method { Some(format_method) => { let ty = field .ty .clone() .into_token_stream() .to_string(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug({field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap(); let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &arg);", key = key ) } else { format!( "builder.field({key:?}, &arg);", key = key ) }; block_tokens.push_str(&statement); } None => { let statement = if name.is_empty() { format!("builder.entry(&RawString({key:?}), {field_name});", key = key, field_name = field_name) } else { format!( "builder.field({key:?}, {field_name});", key = key, field_name = field_name ) }; block_tokens.push_str(&statement); } } } } has_fields = true; } } else { block_tokens .write_fmt(format_args!( "let mut builder = formatter.debug_tuple({name:?});", name = name )) .unwrap(); for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: false, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug({field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap(); block_tokens.push_str("builder.field(&arg);"); } None => { match format_method { Some(format_method) => { let ty = field .ty .clone() .into_token_stream() .to_string(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug({field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap(); block_tokens.push_str("builder.field(&arg);"); } None => { let statement = format!( "builder.field({field_name});", field_name = field_name ); block_tokens.push_str(&statement); } } } } has_fields = true; } } if name.is_empty() && !has_fields { panic::unit_struct_need_name(); } block_tokens.push_str("return builder.finish();"); match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}{{ {pattern_tokens} }} => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); has_variants = true; } Fields::Unnamed(fields) => { // TODO Tuple let mut has_fields = false; let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); if named_field { if name.is_empty() { block_tokens.push_str(" struct RawString(&'static str); impl core::fmt::Debug for RawString {{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{ f.write_str(self.0) }} }} "); block_tokens.push_str("let mut builder = formatter.debug_map();"); } else { block_tokens .write_fmt(format_args!( "let mut builder = formatter.debug_struct({name:?});", name = name )) .unwrap(); } for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: true, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { pattern_tokens.push_str("_,"); continue; } let rename = field_attribute.name.into_option_string(); let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; let (key, field_name) = match rename { Some(rename) => (rename, format!("{}", index)), None => (format!("_{}", index), format!("{}", index)), }; pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug(_{field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap(); let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &arg);", key = key ) } else { format!("builder.field({key:?}, &arg);", key = key) }; block_tokens.push_str(&statement); } None => { match format_method { Some(format_method) => { let ty = field .ty .clone() .into_token_stream() .to_string(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug(_{field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap(); let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &arg);", key = key ) } else { format!( "builder.field({key:?}, &arg);", key = key ) }; block_tokens.push_str(&statement); } None => { let statement = if name.is_empty() { format!("builder.entry(&RawString({key:?}), {field_name});", key = key, field_name = field_name) } else { format!( "builder.field({key:?}, _{field_name});", key = key, field_name = field_name ) }; block_tokens.push_str(&statement); } } } } has_fields = true; } } else { block_tokens .write_fmt(format_args!( "let mut builder = formatter.debug_tuple({name:?});", name = name )) .unwrap(); for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: false, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { pattern_tokens.push_str("_,"); continue; } let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; let field_name = format!("{}", index); pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug(_{field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap(); block_tokens.push_str("builder.field(&arg);"); } None => { match format_method { Some(format_method) => { let ty = field .ty .clone() .into_token_stream() .to_string(); block_tokens.write_fmt(format_args!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug(_{field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap(); block_tokens.push_str("builder.field(&arg);"); } None => { let statement = format!( "builder.field(_{field_name});", field_name = field_name ); block_tokens.push_str(&statement); } } } } has_fields = true; } } if name.is_empty() && !has_fields { panic::unit_struct_need_name(); } block_tokens.push_str("return builder.finish();"); match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); has_variants = true; } } } } match_tokens.push('}'); builder_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); if name.is_empty() && !has_variants { panic::unit_enum_need_name(); } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let debug_impl = quote! { impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause { #[inline] #[allow(clippy::unneeded_field_pattern)] fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { #builder_tokens } } }; tokens.extend(debug_impl); } } fn combine_names(name: &str, variant_name: String) -> String { if name.is_empty() { if variant_name.is_empty() { String::new() } else { variant_name } } else { let mut name = name.to_string(); if !variant_name.is_empty() { if !variant_name.starts_with("::") { name.push_str("::"); } name.push_str(&variant_name); } name } } educe-0.4.19/src/trait_handlers/debug/debug_struct.rs000064400000000000000000000312120072674642500207530ustar 00000000000000use std::str::FromStr; use super::super::TraitHandler; use super::models::{ FieldAttributeBuilder, FieldAttributeName, TypeAttributeBuilder, TypeAttributeName, }; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct DebugStructHandler; impl TraitHandler for DebugStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let is_tuple = { if let Data::Struct(data) = &ast.data { matches!(data.fields, Fields::Unnamed(_)) } else { true } }; let type_attribute = TypeAttributeBuilder { enable_flag: true, name: TypeAttributeName::Default, enable_name: true, named_field: !is_tuple, enable_named_field: true, enable_bound: true, } .from_debug_meta(meta); let name = type_attribute.name.into_string_by_ident(&ast.ident); let named_field = type_attribute.named_field; let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); let mut has_fields = false; if named_field { if name.is_empty() { builder_tokens.extend(quote!( struct RawString(&'static str); impl core::fmt::Debug for RawString { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str(self.0) } } )); builder_tokens.extend(quote!(let mut builder = formatter.debug_map();)); } else { builder_tokens.extend(quote!(let mut builder = formatter.debug_struct(#name);)); } if let Data::Struct(data) = &ast.data { for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: true, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let rename = field_attribute.name.into_option_string(); let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; let (key, field_name) = match rename { Some(rename) => { if let Some(ident) = field.ident.as_ref() { (rename, ident.to_string()) } else { (rename, format!("{}", index)) } } None => { if let Some(ident) = field.ident.as_ref() { (ident.to_string(), ident.to_string()) } else { (format!("_{}", index), format!("{}", index)) } } }; match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); builder_tokens.extend(TokenStream::from_str(&format!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug(&self.{field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap()); let statement = if name.is_empty() { format!("builder.entry(&RawString({key:?}), &arg);", key = key) } else { format!("builder.field({key:?}, &arg);", key = key) }; builder_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { match format_method { Some(format_method) => { let ty = field.ty.clone().into_token_stream().to_string(); builder_tokens.extend(TokenStream::from_str(&format!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug(&self.{field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap()); let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &arg);", key = key ) } else { format!("builder.field({key:?}, &arg);", key = key) }; builder_tokens .extend(TokenStream::from_str(&statement).unwrap()); } None => { let statement = if name.is_empty() { format!( "builder.entry(&RawString({key:?}), &self.{field_name});", key = key, field_name = field_name ) } else { format!( "builder.field({key:?}, &self.{field_name});", key = key, field_name = field_name ) }; builder_tokens .extend(TokenStream::from_str(&statement).unwrap()); } } } } has_fields = true; } } } else { builder_tokens.extend(quote!(let mut builder = formatter.debug_tuple(#name);)); if let Data::Struct(data) = &ast.data { for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: false, enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let format_trait = field_attribute.format_trait; let format_method = field_attribute.format_method; let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; match format_trait { Some(format_trait) => { let format_method = format_method.unwrap(); builder_tokens.extend(TokenStream::from_str(&format!(" let arg = {{ struct MyDebug<'a, T: {format_trait}>(&'a T); impl<'a, T: {format_trait}> core::fmt::Debug for MyDebug<'a, T> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_trait}::{format_method}(self.0, formatter) }} }} MyDebug(&self.{field_name}) }}; ", format_trait = format_trait, format_method = format_method, field_name = field_name)).unwrap()); builder_tokens .extend(TokenStream::from_str("builder.field(&arg);").unwrap()); } None => { match format_method { Some(format_method) => { let ty = field.ty.clone().into_token_stream().to_string(); builder_tokens.extend(TokenStream::from_str(&format!(" let arg = {{ struct MyDebug<'a>(&'a {ty}); impl<'a> core::fmt::Debug for MyDebug<'a> {{ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {{ {format_method}(self.0, formatter) }} }} MyDebug(&self.{field_name}) }}; ", ty = ty, format_method = format_method, field_name = field_name)).unwrap()); builder_tokens.extend( TokenStream::from_str("builder.field(&arg);").unwrap(), ); } None => { let statement = format!( "builder.field(&self.{field_name});", field_name = field_name ); builder_tokens .extend(TokenStream::from_str(&statement).unwrap()); } } } } has_fields = true; } } } if name.is_empty() && !has_fields { panic::unit_struct_need_name(); } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let debug_impl = quote! { impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause { #[inline] fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { #builder_tokens builder.finish() } } }; tokens.extend(debug_impl); } } educe-0.4.19/src/trait_handlers/debug/debug_union.rs000064400000000000000000000055520072674642500205670ustar 00000000000000use super::super::TraitHandler; use super::models::{ FieldAttributeBuilder, FieldAttributeName, TypeAttributeBuilder, TypeAttributeName, }; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Generics, Meta}; pub struct DebugUnionHandler; impl TraitHandler for DebugUnionHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, name: TypeAttributeName::Default, enable_name: true, named_field: false, enable_named_field: false, enable_bound: true, } .from_debug_meta(meta); let name = type_attribute.name.into_string_by_ident(&ast.ident); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); if let Data::Union(data) = &ast.data { for field in data.fields.named.iter() { let _ = FieldAttributeBuilder { name: FieldAttributeName::Default, enable_name: false, enable_ignore: false, enable_impl: false, } .from_attributes(&field.attrs, traits); } if name.is_empty() { builder_tokens.extend(quote!( let size = core::mem::size_of::(); let data = unsafe {{ core::slice::from_raw_parts(self as *const Self as *const u8, size) }}; core::fmt::Debug::fmt(data, formatter) )); } else { builder_tokens.extend(quote!( let mut builder = formatter.debug_tuple(#name); let size = core::mem::size_of::(); let data = unsafe {{ core::slice::from_raw_parts(self as *const Self as *const u8, size) }}; builder.field(&data); builder.finish() )); } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); where_clause.predicates.extend(bound.iter().cloned()); let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let debug_impl = quote! { impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause { #[inline] fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { #builder_tokens } } }; tokens.extend(debug_impl); } } educe-0.4.19/src/trait_handlers/debug/mod.rs000064400000000000000000000016270072674642500170470ustar 00000000000000mod models; mod debug_enum; mod debug_struct; mod debug_union; use super::TraitHandler; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use debug_enum::DebugEnumHandler; use debug_struct::DebugStructHandler; use debug_union::DebugUnionHandler; pub struct DebugHandler; impl TraitHandler for DebugHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { DebugStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { DebugEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => { DebugUnionHandler::trait_meta_handler(ast, tokens, traits, meta); } } } } educe-0.4.19/src/trait_handlers/debug/models/field_attribute.rs000064400000000000000000000620710072674642500227210ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub enum FieldAttributeName { Default, Custom(String), } impl FieldAttributeName { pub fn into_option_string(self) -> Option { match self { FieldAttributeName::Default => None, FieldAttributeName::Custom(s) => Some(s), } } } #[derive(Debug, Clone)] pub struct FieldAttribute { pub name: FieldAttributeName, pub ignore: bool, pub format_method: Option, pub format_trait: Option, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub name: FieldAttributeName, pub enable_name: bool, pub enable_ignore: bool, pub enable_impl: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_debug_meta(&self, meta: &Meta) -> FieldAttribute { let mut name = self.name.clone(); let mut ignore = false; let mut format_method = None; let mut format_trait = None; let correct_usage_for_debug_attribute = { let mut usage = vec![]; if self.enable_name { usage.push(stringify!(#[educe(Debug = "new_name")])); usage.push(stringify!(#[educe(Debug("new_name"))])); } if self.enable_ignore { usage.push(stringify!(#[educe(Debug = false)])); usage.push(stringify!(#[educe(Debug(false))])); } usage }; let correct_usage_for_name = { let usage = vec![ stringify!(#[educe(Debug(name = "new_name"))]), stringify!(#[educe(Debug(name("new_name")))]), ]; usage }; let correct_usage_for_ignore = { let usage = vec![stringify!(#[educe(Debug(ignore))])]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(Debug(method = "path_to_method"))]), stringify!(#[educe(Debug(trait = "path_to_trait"))]), stringify!(#[educe(Debug(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(Debug(method("path_to_method")))]), stringify!(#[educe(Debug(trait("path_to_trait")))]), stringify!(#[educe(Debug(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; match meta { Meta::List(list) => { let mut name_is_set = false; let mut ignore_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "name" | "rename" => { if !self.enable_name { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(lit) => { match lit { Lit::Str(s) => { if name_is_set { panic::reset_parameter( meta_name.as_str(), ); } name_is_set = true; let s = create_path_string_from_lit_str( s, ); name = match s { Some(s) => { FieldAttributeName::Custom(s) } None => { panic::disable_named_field_name( ) } }; } Lit::Bool(s) => { if name_is_set { panic::reset_parameter( meta_name.as_str(), ); } name_is_set = true; if s.value { name = FieldAttributeName::Default; } else { panic::disable_named_field_name( ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if name_is_set { panic::reset_parameter(meta_name.as_str()); } name_is_set = true; let s = create_path_string_from_lit_str(s); name = match s { Some(s) => FieldAttributeName::Custom(s), None => panic::disable_named_field_name(), }; } Lit::Bool(s) => { if name_is_set { panic::reset_parameter(meta_name.as_str()); } name_is_set = true; if s.value { name = FieldAttributeName::Default; } else { panic::disable_named_field_name(); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } "ignore" => { if !self.enable_ignore { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::Path(_) => { if ignore_is_set { panic::reset_parameter(meta_name.as_str()); } ignore_is_set = true; ignore = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_ignore, ) } } } "method" => { if !self.enable_impl { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if format_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { format_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if format_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { format_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if format_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { format_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if format_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { format_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => panic::unknown_parameter("Debug", meta_name.as_str()), } } NestedMeta::Lit(lit) => { match lit { Lit::Str(s) => { if !self.enable_name { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } if name_is_set { panic::reset_parameter("name"); } name_is_set = true; let s = create_path_string_from_lit_str(s); name = match s { Some(s) => FieldAttributeName::Custom(s), None => panic::disable_named_field_name(), }; } Lit::Bool(b) => { if !self.enable_ignore { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } if ignore_is_set { panic::reset_parameter("ignore"); } ignore_is_set = true; ignore = !b.value; } _ => { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } } } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if !self.enable_name { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } let s = create_path_string_from_lit_str(s); name = match s { Some(s) => FieldAttributeName::Custom(s), None => panic::disable_named_field_name(), }; } Lit::Bool(b) => { if !self.enable_ignore { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } ignore = !b.value; } _ => { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } } } _ => panic::attribute_incorrect_format("Debug", &correct_usage_for_debug_attribute), } if format_trait.is_some() && format_method.is_none() { format_method = Some("fmt".to_string()); } FieldAttribute { name, ignore, format_method, format_trait, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Debug { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_debug_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { name: self.name, ignore: false, format_method: None, format_trait: None, }) } } educe-0.4.19/src/trait_handlers/debug/models/mod.rs000064400000000000000000000001410072674642500203200ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/debug/models/type_attribute.rs000064400000000000000000000617660072674642500226310ustar 00000000000000use super::super::super::{ create_path_string_from_lit_str, create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Ident, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Debug, Clone)] pub enum TypeAttributeName { Disable, Default, Custom(String), } impl TypeAttributeName { pub fn into_string_by_ident(self, ident: &Ident) -> String { match self { TypeAttributeName::Disable => String::new(), TypeAttributeName::Default => ident.to_string(), TypeAttributeName::Custom(s) => s, } } } #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::fmt::Debug)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub name: TypeAttributeName, pub named_field: bool, pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub name: TypeAttributeName, pub enable_name: bool, pub named_field: bool, pub enable_named_field: bool, pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_debug_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut name = self.name.clone(); let mut named_field = self.named_field; let mut bound = TypeAttributeBound::None; let correct_usage_for_debug_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Default)])); } if self.enable_name { usage.push(stringify!(#[educe(Debug = "new_name")])); usage.push(stringify!(#[educe(Debug("new_name"))])); } if self.enable_bound { usage.push(stringify!(#[educe(Debug(ignore))])); } usage }; let correct_usage_for_name = { let mut usage = vec![ stringify!(#[educe(Debug(name = "new_name"))]), stringify!(#[educe(Debug(name("new_name")))]), ]; if let TypeAttributeName::Disable = &name { usage.push(stringify!(#[educe(Debug(name = true))])); usage.push(stringify!(#[educe(Debug(name(true)))])); } else { usage.push(stringify!(#[educe(Debug(name = false))])); usage.push(stringify!(#[educe(Debug(name(false)))])); } usage }; let correct_usage_for_named_field = { let mut usage = vec![]; if !self.named_field { usage.push(stringify!(#[educe(Debug(named_field = true))])); usage.push(stringify!(#[educe(Debug(named_field(true)))])); } else { usage.push(stringify!(#[educe(Debug(named_field = false))])); usage.push(stringify!(#[educe(Debug(named_field(false)))])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Debug(bound))]), stringify!(#[educe(Debug(bound = "where_predicates"))]), stringify!(#[educe(Debug(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut name_is_set = false; let mut named_field_is_set = false; let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "name" | "rename" => { if !self.enable_name { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(lit) => { match lit { Lit::Str(s) => { if name_is_set { panic::reset_parameter( meta_name.as_str(), ); } name_is_set = true; let s = create_path_string_from_lit_str( s, ); name = match s { Some(s) => { TypeAttributeName::Custom(s) } None => { TypeAttributeName::Disable } }; } Lit::Bool(s) => { if name_is_set { panic::reset_parameter( meta_name.as_str(), ); } name_is_set = true; if s.value { name = TypeAttributeName::Default; } else { name = TypeAttributeName::Disable; } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if name_is_set { panic::reset_parameter(meta_name.as_str()); } name_is_set = true; let s = create_path_string_from_lit_str(s); name = match s { Some(s) => TypeAttributeName::Custom(s), None => TypeAttributeName::Disable, }; } Lit::Bool(s) => { if name_is_set { panic::reset_parameter(meta_name.as_str()); } name_is_set = true; if s.value { name = TypeAttributeName::Default; } else { name = TypeAttributeName::Disable; } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_name, ) } } } "named_field" => { if !self.enable_named_field { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Bool(s)) => { if named_field_is_set { panic::reset_parameter( meta_name.as_str(), ); } named_field_is_set = true; named_field = s.value; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_named_field, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Bool(s) => { if named_field_is_set { panic::reset_parameter(meta_name.as_str()); } named_field_is_set = true; named_field = s.value; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_named_field, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_named_field, ) } } } "bound" => { if !self.enable_bound { panic::unknown_parameter("Debug", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("Debug", meta_name.as_str()), } } NestedMeta::Lit(lit) => { match lit { Lit::Str(s) => { if !self.enable_name { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } if name_is_set { panic::reset_parameter("name"); } name_is_set = true; let s = create_path_string_from_lit_str(s); name = match s { Some(s) => TypeAttributeName::Custom(s), None => TypeAttributeName::Disable, }; } _ => { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } } } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if !self.enable_name { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } let s = create_path_string_from_lit_str(s); name = match s { Some(s) => TypeAttributeName::Custom(s), None => TypeAttributeName::Disable, }; } _ => { panic::attribute_incorrect_format( "Debug", &correct_usage_for_debug_attribute, ) } } } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Debug", &correct_usage_for_debug_attribute); } flag = true; } } TypeAttribute { flag, name, named_field, bound, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Debug { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_debug_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, name: self.name, named_field: self.named_field, bound: TypeAttributeBound::None, }) } } educe-0.4.19/src/trait_handlers/default/default_enum.rs000064400000000000000000000331220072674642500212710ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Fields, Generics, Lit, Meta}; pub struct DefaultEnumHandler; impl TraitHandler for DefaultEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_new: true, enable_expression: true, enable_bound: true, } .from_default_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); if let Data::Enum(data) = &ast.data { match type_attribute.expression { Some(expression) => { for variant in data.variants.iter() { let _ = TypeAttributeBuilder { enable_flag: false, enable_new: false, enable_expression: false, enable_bound: false, } .from_attributes(&variant.attrs, traits); ensure_fields_no_attribute(&variant.fields, traits); } builder_tokens.extend(quote!(#expression)); } None => { 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, } .from_attributes(&variant.attrs, traits); variant } else { let mut variants_iter = variants.iter(); loop { let variant = variants_iter.next(); match variant { Some(variant) => { let variant_attribute = TypeAttributeBuilder { enable_flag: true, enable_new: false, enable_expression: false, enable_bound: false, } .from_attributes(&variant.attrs, traits); if variant_attribute.flag { loop { let variant = variants_iter.next(); match variant { Some(variant) => { let variant_attribute = TypeAttributeBuilder { enable_flag: true, enable_new: false, enable_expression: false, enable_bound: false, }.from_attributes(&variant.attrs, traits); if variant_attribute.flag { panic::multiple_default_variants(); } else { ensure_fields_no_attribute( &variant.fields, traits, ); } } None => break, } } break variant; } else { ensure_fields_no_attribute(&variant.fields, traits); } } None => panic::no_default_variant(), } } } }; let enum_name = ast.ident.to_string(); let variant_name = variant.ident.to_string(); let mut enum_tokens = format!( "{enum_name}::{variant_name}", enum_name = enum_name, variant_name = variant_name ); match &variant.fields { Fields::Unit => (), // TODO Unit Fields::Named(fields) => { // TODO Struct enum_tokens.push('{'); for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: false, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); enum_tokens .write_fmt(format_args!( "{field_name}: ", field_name = field_name )) .unwrap(); match field_attribute.literal { Some(value) => { match &value { Lit::Str(s) => { enum_tokens .write_fmt(format_args!( "core::convert::Into::into({s})", s = s.into_token_stream() )) .unwrap(); } _ => { enum_tokens.push_str( &value.into_token_stream().to_string(), ); } } } None => { match field_attribute.expression { Some(expression) => { enum_tokens.push_str(&expression); } None => { let typ = field .ty .clone() .into_token_stream() .to_string(); enum_tokens.write_fmt(format_args!("<{typ} as core::default::Default>::default()", typ = typ)).unwrap(); } } } } enum_tokens.push(','); } enum_tokens.push('}'); } Fields::Unnamed(fields) => { // TODO Tuple enum_tokens.push('('); for field in fields.unnamed.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: false, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); match field_attribute.literal { Some(value) => { match &value { Lit::Str(s) => { enum_tokens .write_fmt(format_args!( "core::convert::Into::into({s})", s = s.into_token_stream() )) .unwrap(); } _ => { enum_tokens.push_str( &value.into_token_stream().to_string(), ); } } } None => { match field_attribute.expression { Some(expression) => { enum_tokens.push_str(&expression); } None => { let typ = field .ty .clone() .into_token_stream() .to_string(); enum_tokens.write_fmt(format_args!("<{typ} as core::default::Default>::default()", typ = typ)).unwrap(); } } } } enum_tokens.push(','); } enum_tokens.push(')'); } } builder_tokens.extend(TokenStream::from_str(&enum_tokens).unwrap()); } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let default_impl = quote! { impl #impl_generics core::default::Default for #ident #ty_generics #where_clause { #[inline] fn default() -> Self { #builder_tokens } } }; tokens.extend(default_impl); if type_attribute.new { let new_impl = quote! { impl #impl_generics #ident #ty_generics #where_clause { /// Returns the "default value" for a type. #[inline] pub fn new() -> Self { ::default() } } }; tokens.extend(new_impl); } } } fn ensure_fields_no_attribute(fields: &Fields, traits: &[Trait]) { match fields { Fields::Unit => (), Fields::Named(fields) => { for field in fields.named.iter() { let _ = FieldAttributeBuilder { enable_flag: false, enable_literal: false, enable_expression: false, } .from_attributes(&field.attrs, traits); } } Fields::Unnamed(fields) => { for field in fields.unnamed.iter() { let _ = FieldAttributeBuilder { enable_flag: false, enable_literal: false, enable_expression: false, } .from_attributes(&field.attrs, traits); } } } } educe-0.4.19/src/trait_handlers/default/default_struct.rs000064400000000000000000000221710072674642500216530ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Fields, Generics, Lit, Meta}; pub struct DefaultStructHandler; impl TraitHandler for DefaultStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_new: true, enable_expression: true, enable_bound: true, } .from_default_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { match type_attribute.expression { Some(expression) => { for field in data.fields.iter() { let _ = FieldAttributeBuilder { enable_flag: false, enable_literal: false, enable_expression: false, } .from_attributes(&field.attrs, traits); } builder_tokens.extend(quote!(#expression)); } None => { match &data.fields { Fields::Unit => { let ident = &ast.ident; builder_tokens.extend(quote!(#ident)); } Fields::Unnamed(_) => { let mut struct_tokens = ast.ident.to_string(); struct_tokens.push('('); for field in data.fields.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: false, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); match field_attribute.literal { Some(value) => { match &value { Lit::Str(s) => { struct_tokens .write_fmt(format_args!( "core::convert::Into::into({s})", s = s.into_token_stream() )) .unwrap(); } _ => { struct_tokens.push_str( &value.into_token_stream().to_string(), ); } } } None => { match field_attribute.expression { Some(expression) => { struct_tokens.push_str(&expression); } None => { let typ = field .ty .clone() .into_token_stream() .to_string(); struct_tokens.write_fmt(format_args!("<{typ} as core::default::Default>::default()", typ = typ)).unwrap(); } } } } struct_tokens.push(','); } struct_tokens.push(')'); builder_tokens.extend(TokenStream::from_str(&struct_tokens).unwrap()); } Fields::Named(_) => { let mut struct_tokens = ast.ident.to_string(); struct_tokens.push('{'); for field in data.fields.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: false, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); struct_tokens .write_fmt(format_args!( "{field_name}: ", field_name = field_name )) .unwrap(); match field_attribute.literal { Some(value) => { match &value { Lit::Str(s) => { struct_tokens .write_fmt(format_args!( "core::convert::Into::into({s})", s = s.into_token_stream() )) .unwrap(); } _ => { struct_tokens.push_str( &value.into_token_stream().to_string(), ); } } } None => { match field_attribute.expression { Some(expression) => { struct_tokens.push_str(&expression); } None => { let typ = field .ty .clone() .into_token_stream() .to_string(); struct_tokens.write_fmt(format_args!("<{typ} as core::default::Default>::default()", typ = typ)).unwrap(); } } } } struct_tokens.push(','); } struct_tokens.push('}'); builder_tokens.extend(TokenStream::from_str(&struct_tokens).unwrap()); } } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let default_impl = quote! { impl #impl_generics core::default::Default for #ident #ty_generics #where_clause { #[inline] fn default() -> Self { #builder_tokens } } }; tokens.extend(default_impl); if type_attribute.new { let new_impl = quote! { impl #impl_generics #ident #ty_generics #where_clause { /// Returns the "default value" for a type. #[inline] pub fn new() -> Self { ::default() } } }; tokens.extend(new_impl); } } } educe-0.4.19/src/trait_handlers/default/default_union.rs000064400000000000000000000212370072674642500214610ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Generics, Lit, Meta}; pub struct DefaultUnionHandler; impl TraitHandler for DefaultUnionHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_new: true, enable_expression: true, enable_bound: true, } .from_default_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut builder_tokens = TokenStream::new(); if let Data::Union(data) = &ast.data { match type_attribute.expression { Some(expression) => { for field in data.fields.named.iter() { let _ = FieldAttributeBuilder { enable_flag: false, enable_literal: false, enable_expression: false, } .from_attributes(&field.attrs, traits); } builder_tokens.extend(quote!(#expression)); } None => { let ident = ast.ident.to_string(); let (field_name, field_attribute, typ) = { let fields = &data.fields.named; if fields.len() == 1 { let field = &fields[0]; let field_attribute = FieldAttributeBuilder { enable_flag: true, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); ( field_name, field_attribute, field.ty.clone().into_token_stream().to_string(), ) } else { let mut fields_iter = fields.iter(); loop { let field = fields_iter.next(); match field { Some(field) => { let field_attribute = FieldAttributeBuilder { enable_flag: true, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag || field_attribute.literal.is_some() || field_attribute.expression.is_some() { let field_name = field.ident.as_ref().unwrap().to_string(); loop { let field = fields_iter.next(); match field { Some(field) => { let field_attribute = FieldAttributeBuilder { enable_flag: true, enable_literal: true, enable_expression: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag || field_attribute.literal.is_some() || field_attribute.expression.is_some() { panic::multiple_default_fields(); } } None => break, } } break ( field_name, field_attribute, field.ty.clone().into_token_stream().to_string(), ); } } None => panic::no_default_field(), } } } }; let mut union_tokens = format!( "{ident} {{ {field_name}: ", ident = ident, field_name = field_name ); match field_attribute.literal { Some(value) => { match &value { Lit::Str(s) => { union_tokens .write_fmt(format_args!( "core::convert::Into::into({s})", s = s.into_token_stream() )) .unwrap(); } _ => { union_tokens.push_str(&value.into_token_stream().to_string()); } } } None => { match field_attribute.expression { Some(expression) => { union_tokens.push_str(&expression); } None => { union_tokens .write_fmt(format_args!( "<{typ} as core::default::Default>::default()", typ = typ )) .unwrap(); } } } } union_tokens.push('}'); builder_tokens.extend(TokenStream::from_str(&union_tokens).unwrap()); } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let default_impl = quote! { impl #impl_generics core::default::Default for #ident #ty_generics #where_clause { #[inline] fn default() -> Self { #builder_tokens } } }; tokens.extend(default_impl); if type_attribute.new { let new_impl = quote! { impl #impl_generics #ident #ty_generics #where_clause { /// Returns the "default value" for a type. #[inline] pub fn new() -> Self { ::default() } } }; tokens.extend(new_impl); } } } educe-0.4.19/src/trait_handlers/default/mod.rs000064400000000000000000000016630072674642500174050ustar 00000000000000mod models; mod default_enum; mod default_struct; mod default_union; use super::TraitHandler; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use default_enum::DefaultEnumHandler; use default_struct::DefaultStructHandler; use default_union::DefaultUnionHandler; pub struct DefaultHandler; impl TraitHandler for DefaultHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { DefaultStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { DefaultEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => { DefaultUnionHandler::trait_meta_handler(ast, tokens, traits, meta); } } } } educe-0.4.19/src/trait_handlers/default/models/field_attribute.rs000064400000000000000000000221370072674642500232560ustar 00000000000000use super::super::super::create_expr_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Clone)] pub struct FieldAttribute { pub flag: bool, pub literal: Option, pub expression: Option, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_flag: bool, pub enable_literal: bool, pub enable_expression: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_default_meta(&self, meta: &Meta) -> FieldAttribute { let mut flag = false; let mut value: Option = None; let mut expression: Option = None; let correct_usage_for_default_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Default)])); } if self.enable_literal { usage.push(stringify!(#[educe(Default = literal)])); usage.push(stringify!(#[educe(Default(literal))])); } usage }; let correct_usage_for_expression = { let usage = vec![ stringify!(#[educe(Default(expression = "expression"))]), stringify!(#[educe(Default(expression("expression")))]), ]; usage }; match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "expression" | "expr" => { if !self.enable_expression { panic::unknown_parameter("Default", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if expression.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_expr_string_from_lit_str(s); if s.is_some() { expression = s; } else { panic::empty_parameter( meta_name.as_str(), ) } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if expression.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_expr_string_from_lit_str(s); if s.is_some() { expression = s; } else { panic::empty_parameter(meta_name.as_str()) } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } _ => panic::unknown_parameter("Default", meta_name.as_str()), } } NestedMeta::Lit(lit) => { if !self.enable_literal { panic::attribute_incorrect_format( "Default", &correct_usage_for_default_attribute, ) } if value.is_some() { panic::reset_parameter("value"); } value = Some(lit.clone()); } } } } Meta::NameValue(named_value) => { if !self.enable_literal { panic::attribute_incorrect_format( "Default", &correct_usage_for_default_attribute, ) } let lit = &named_value.lit; value = Some(lit.clone()); } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "Default", &correct_usage_for_default_attribute, ); } flag = true; } } if value.is_some() && expression.is_some() { panic::set_value_expression(); } FieldAttribute { flag, literal: value, expression, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Default { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_default_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { flag: false, literal: None, expression: None, }) } } educe-0.4.19/src/trait_handlers/default/models/mod.rs000064400000000000000000000001410072674642500206560ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/default/models/type_attribute.rs000064400000000000000000000411100072674642500231440ustar 00000000000000use super::super::super::{ create_expr_from_lit_str, create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, Expr, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::default::Default)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub new: bool, pub expression: Option, pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_new: bool, pub enable_expression: bool, pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_default_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut new = false; let mut expression: Option = None; let mut bound = TypeAttributeBound::None; 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))])); } usage }; let correct_usage_for_new = { let usage = vec![stringify!(#[educe(Default(new))])]; usage }; let correct_usage_for_expression = { let usage = vec![ stringify!(#[educe(Default(expression = "expression"))]), stringify!(#[educe(Default(expression("expression")))]), ]; usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Default(bound))]), stringify!(#[educe(Default(bound = "where_predicates"))]), stringify!(#[educe(Default(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut new_is_set = false; let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "expression" | "expr" => { if !self.enable_expression { panic::unknown_parameter("Default", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if expression.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_expr_from_lit_str(s); if s.is_some() { expression = s; } else { panic::empty_parameter( meta_name.as_str(), ) } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if expression.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_expr_from_lit_str(s); if s.is_some() { expression = s; } else { panic::empty_parameter(meta_name.as_str()) } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_expression, ) } } } "bound" => { if !self.enable_bound { panic::unknown_parameter("Default", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } "new" => { if !self.enable_new { panic::unknown_parameter("Default", meta_name.as_str()); } match meta { Meta::Path(_) => { if new_is_set { panic::reset_parameter(meta_name.as_str()); } new_is_set = true; new = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_new, ) } } } _ => panic::unknown_parameter("Default", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Default", &correct_usage_for_default_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Default", &correct_usage_for_default_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "Default", &correct_usage_for_default_attribute, ); } flag = true; } } if expression.is_some() { if let TypeAttributeBound::None = &bound { } else { panic::set_expression_bound(); } } TypeAttribute { flag, new, expression, bound, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Default { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_default_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, new: false, expression: None, bound: TypeAttributeBound::None, }) } } educe-0.4.19/src/trait_handlers/deref/deref_enum.rs000064400000000000000000000220070072674642500203730ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Fields, Meta}; pub struct DerefEnumHandler; impl TraitHandler for DerefEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let _ = TypeAttributeBuilder { enable_flag: true, } .from_deref_meta(meta); let enum_name = ast.ident.to_string(); let mut ty_all = TokenStream::new(); let mut deref_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); if let Data::Enum(data) = &ast.data { for variant in data.variants.iter() { let _ = TypeAttributeBuilder { enable_flag: false, } .from_attributes(&variant.attrs, traits); let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit panic::deref_cannot_support_unit_variant(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); let mut ty = TokenStream::new(); let mut counter = 0; for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !ty.is_empty() { panic::multiple_deref_fields_of_variant(&variant_ident); } let field_name = field.ident.as_ref().unwrap().to_string(); ty.extend(field.ty.clone().into_token_stream()); if ty_all.is_empty() { ty_all.extend(field.ty.clone().into_token_stream()); } block_tokens .write_fmt(format_args!( "return {field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "{field_name}, ..", field_name = field_name )) .unwrap(); } counter += 1; } if ty.is_empty() { if counter == 1 { let field = fields.named.iter().next().unwrap(); let field_name = field.ident.as_ref().unwrap().to_string(); ty.extend(field.ty.clone().into_token_stream()); if ty_all.is_empty() { ty_all.extend(field.ty.clone().into_token_stream()); } block_tokens .write_fmt(format_args!( "return {field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "{field_name}, ..", field_name = field_name )) .unwrap(); } else { panic::no_deref_field_of_variant(&variant_ident); } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); let mut ty = TokenStream::new(); let mut counter = 0; for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !ty.is_empty() { panic::multiple_deref_fields_of_variant(&variant_ident); } let field_name = format!("{}", index); ty.extend(field.ty.clone().into_token_stream()); if ty_all.is_empty() { ty_all.extend(field.ty.clone().into_token_stream()); } block_tokens .write_fmt(format_args!( "return _{field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); } else { pattern_tokens.push_str("_,"); } counter += 1; } if ty.is_empty() { if counter == 1 { let field = fields.unnamed.iter().next().unwrap(); let field_name = String::from("0"); ty.extend(field.ty.clone().into_token_stream()); if ty_all.is_empty() { ty_all.extend(field.ty.clone().into_token_stream()); } block_tokens .write_fmt(format_args!( "return _{field_name};", field_name = field_name )) .unwrap(); pattern_tokens.clear(); pattern_tokens .write_fmt(format_args!( "_{field_name}", field_name = field_name )) .unwrap(); } else { panic::no_deref_field_of_variant(&variant_ident); } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } } } } match_tokens.push('}'); deref_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); let ident = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let deref_impl = quote! { impl #impl_generics core::ops::Deref for #ident #ty_generics #where_clause { type Target = #ty_all; #[inline] fn deref(&self) -> &Self::Target { #deref_tokens } } }; tokens.extend(deref_impl); } } educe-0.4.19/src/trait_handlers/deref/deref_struct.rs000064400000000000000000000060360072674642500207570ustar 00000000000000use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Meta}; pub struct DerefStructHandler; impl TraitHandler for DerefStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let _ = TypeAttributeBuilder { enable_flag: true, } .from_deref_meta(meta); let mut ty = TokenStream::new(); let mut deref_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { let mut counter = 0; for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !ty.is_empty() { panic::multiple_deref_fields(); } let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; ty.extend(field.ty.clone().into_token_stream()); deref_tokens.extend( TokenStream::from_str(&format!( "&self.{field_name}", field_name = field_name )) .unwrap(), ); } counter += 1; } if ty.is_empty() { if counter == 1 { let field = data.fields.iter().next().unwrap(); let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { String::from("0") }; ty.extend(field.ty.clone().into_token_stream()); deref_tokens.extend( TokenStream::from_str(&format!( "&self.{field_name}", field_name = field_name )) .unwrap(), ); } else { panic::no_deref_field(); } } } let ident = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let deref_impl = quote! { impl #impl_generics core::ops::Deref for #ident #ty_generics #where_clause { type Target = #ty; #[inline] fn deref(&self) -> &Self::Target { #deref_tokens } } }; tokens.extend(deref_impl); } } educe-0.4.19/src/trait_handlers/deref/mod.rs000064400000000000000000000015000072674642500170340ustar 00000000000000mod models; mod deref_enum; mod deref_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use deref_enum::DerefEnumHandler; use deref_struct::DerefStructHandler; pub struct DerefHandler; impl TraitHandler for DerefHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { DerefStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { DerefEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::Deref), } } } educe-0.4.19/src/trait_handlers/deref/models/field_attribute.rs000064400000000000000000000055740072674642500227250ustar 00000000000000use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Meta, NestedMeta}; #[derive(Clone)] pub struct FieldAttribute { pub flag: bool, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_flag: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_deref_meta(&self, meta: &Meta) -> FieldAttribute { let flag; let correct_usage_for_deref_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Deref)])); } usage }; match meta { Meta::List(_) => { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute) } Meta::NameValue(_) => { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute); } flag = true; } } FieldAttribute { flag, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Deref { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_deref_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { flag: false, }) } } educe-0.4.19/src/trait_handlers/deref/models/mod.rs000064400000000000000000000001410072674642500203170ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/deref/models/type_attribute.rs000064400000000000000000000057360072674642500226230ustar 00000000000000use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Meta, NestedMeta}; #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_deref_meta(&self, meta: &Meta) -> TypeAttribute { let flag; let correct_usage_for_deref_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Deref)])); } usage }; match meta { Meta::List(_) => { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute) } Meta::NameValue(_) => { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Deref", &correct_usage_for_deref_attribute); } flag = true; } } TypeAttribute { flag, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Deref { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_deref_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, }) } } educe-0.4.19/src/trait_handlers/deref_mut/deref_mut_enum.rs000064400000000000000000000174630072674642500221570ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Meta}; pub struct DerefMutEnumHandler; impl TraitHandler for DerefMutEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let _ = TypeAttributeBuilder { enable_flag: true, } .from_deref_mut_meta(meta); let enum_name = ast.ident.to_string(); let mut deref_mut_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); if let Data::Enum(data) = &ast.data { for variant in data.variants.iter() { let _ = TypeAttributeBuilder { enable_flag: false, } .from_attributes(&variant.attrs, traits); let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit panic::deref_mut_cannot_support_unit_variant(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); let mut counter = 0; for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !block_tokens.is_empty() { panic::multiple_deref_mut_fields_of_variant(&variant_ident); } let field_name = field.ident.as_ref().unwrap().to_string(); block_tokens .write_fmt(format_args!( "return {field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "{field_name}, ..", field_name = field_name )) .unwrap(); } counter += 1; } if block_tokens.is_empty() { if counter == 1 { let field = fields.named.iter().next().unwrap(); let field_name = field.ident.as_ref().unwrap().to_string(); block_tokens .write_fmt(format_args!( "return {field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "{field_name}, ..", field_name = field_name )) .unwrap(); } else { panic::no_deref_mut_field_of_variant(&variant_ident); } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); let mut counter = 0; for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !block_tokens.is_empty() { panic::multiple_deref_mut_fields_of_variant(&variant_ident); } let field_name = format!("{}", index); block_tokens .write_fmt(format_args!( "return _{field_name};", field_name = field_name )) .unwrap(); pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); } else { pattern_tokens.push_str("_,"); } counter += 1; } if block_tokens.is_empty() { if counter == 1 { let field_name = String::from("0"); block_tokens .write_fmt(format_args!( "return _{field_name};", field_name = field_name )) .unwrap(); pattern_tokens.clear(); pattern_tokens .write_fmt(format_args!( "_{field_name}", field_name = field_name )) .unwrap(); } else { panic::no_deref_mut_field_of_variant(&variant_ident); } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } } } } match_tokens.push('}'); deref_mut_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); let ident = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let deref_mut_impl = quote! { impl #impl_generics core::ops::DerefMut for #ident #ty_generics #where_clause { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { #deref_mut_tokens } } }; tokens.extend(deref_mut_impl); } } educe-0.4.19/src/trait_handlers/deref_mut/deref_mut_struct.rs000064400000000000000000000056300072674642500225300ustar 00000000000000use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Meta}; pub struct DerefMutStructHandler; impl TraitHandler for DerefMutStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let _ = TypeAttributeBuilder { enable_flag: true, } .from_deref_mut_meta(meta); let mut deref_mut_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { let mut counter = 0; for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_flag: true, } .from_attributes(&field.attrs, traits); if field_attribute.flag { if !deref_mut_tokens.is_empty() { panic::multiple_deref_mut_fields(); } let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; deref_mut_tokens.extend( TokenStream::from_str(&format!( "&mut self.{field_name}", field_name = field_name )) .unwrap(), ); } counter += 1; } if deref_mut_tokens.is_empty() { if counter == 1 { let field = data.fields.iter().next().unwrap(); let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { String::from("0") }; deref_mut_tokens.extend( TokenStream::from_str(&format!( "&mut self.{field_name}", field_name = field_name )) .unwrap(), ); } else { panic::no_deref_mut_field(); } } } let ident = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let deref_mut_impl = quote! { impl #impl_generics core::ops::DerefMut for #ident #ty_generics #where_clause { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { #deref_mut_tokens } } }; tokens.extend(deref_mut_impl); } } educe-0.4.19/src/trait_handlers/deref_mut/mod.rs000064400000000000000000000015450072674642500177320ustar 00000000000000mod models; mod deref_mut_enum; mod deref_mut_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use deref_mut_enum::DerefMutEnumHandler; use deref_mut_struct::DerefMutStructHandler; pub struct DerefMutHandler; impl TraitHandler for DerefMutHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { DerefMutStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { DerefMutEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::DerefMut), } } } educe-0.4.19/src/trait_handlers/deref_mut/models/field_attribute.rs000064400000000000000000000061400072674642500236000ustar 00000000000000use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Meta, NestedMeta}; #[derive(Clone)] pub struct FieldAttribute { pub flag: bool, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_flag: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_deref_mut_meta(&self, meta: &Meta) -> FieldAttribute { let flag; let correct_usage_for_deref_mut_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(DerefMut)])); } usage }; match meta { Meta::List(_) => { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ) } Meta::NameValue(_) => { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ); } flag = true; } } FieldAttribute { flag, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::DerefMut { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_deref_mut_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { flag: false, }) } } educe-0.4.19/src/trait_handlers/deref_mut/models/mod.rs000064400000000000000000000001410072674642500212040ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/deref_mut/models/type_attribute.rs000064400000000000000000000063020072674642500234760ustar 00000000000000use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Meta, NestedMeta}; #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_deref_mut_meta(&self, meta: &Meta) -> TypeAttribute { let flag; let correct_usage_for_deref_mut_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(DerefMut)])); } usage }; match meta { Meta::List(_) => { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ) } Meta::NameValue(_) => { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "DerefMut", &correct_usage_for_deref_mut_attribute, ); } flag = true; } } TypeAttribute { flag, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::DerefMut { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_deref_mut_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, }) } } educe-0.4.19/src/trait_handlers/eq/mod.rs000064400000000000000000000022740072674642500163650ustar 00000000000000mod models; use super::TraitHandler; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{DeriveInput, Generics, Meta}; use models::TypeAttributeBuilder; pub struct EqHandler; impl TraitHandler for EqHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, _traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_bound: true, } .from_eq_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::Eq for #ident #ty_generics #where_clause { } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/eq/models/mod.rs000064400000000000000000000000600072674642500176370ustar 00000000000000mod type_attribute; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/eq/models/type_attribute.rs000064400000000000000000000177550072674642500221470ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::cmp::Eq)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_eq_meta(&self, meta: &Meta) -> TypeAttribute { let mut bound = TypeAttributeBound::None; let correct_usage_for_eq_attribute = { let usage = vec![stringify!(#[educe(Eq)])]; usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Eq(bound))]), stringify!(#[educe(Eq(bound = "where_predicates"))]), stringify!(#[educe(Eq(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("Eq", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("Eq", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format("Eq", &correct_usage_for_eq_attribute) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Eq", &correct_usage_for_eq_attribute) } Meta::Path(_) => (), } TypeAttribute { bound, } } } educe-0.4.19/src/trait_handlers/hash/hash_enum.rs000064400000000000000000000260260072674642500200740ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct HashEnumHandler; impl TraitHandler for HashEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_hash_meta(meta); let enum_name = ast.ident.to_string(); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut hasher_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); if let Data::Enum(data) = &ast.data { let has_non_unit = { let mut non_unit = false; for variant in data.variants.iter() { let _ = TypeAttributeBuilder { enable_flag: false, enable_bound: false, } .from_attributes(&variant.attrs, traits); match &variant.fields { Fields::Named(_) | Fields::Unnamed(_) => { non_unit = true; break; } _ => (), } } non_unit }; if has_non_unit { for (index, variant) in data.variants.iter().enumerate() { let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ core::hash::Hash::hash(&{index}, state); }}", enum_name = enum_name, variant_ident = variant_ident, index = index)).unwrap(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); block_tokens .write_fmt(format_args!( "core::hash::Hash::hash(&{index}, state);", index = index )) .unwrap(); for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } let hash_trait = field_attribute.hash_trait; let hash_method = field_attribute.hash_method; pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); match hash_trait { Some(hash_trait) => { let hash_method = hash_method.unwrap(); block_tokens .write_fmt(format_args!( "{hash_trait}::{hash_method}({field_name}, state);", hash_trait = hash_trait, hash_method = hash_method, field_name = field_name )) .unwrap(); } None => { match hash_method { Some(hash_method) => { block_tokens .write_fmt(format_args!( "{hash_method}({field_name}, state);", hash_method = hash_method, field_name = field_name )) .unwrap(); } None => { block_tokens.write_fmt(format_args!("core::hash::Hash::hash({field_name}, state);", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut block_tokens = String::new(); block_tokens .write_fmt(format_args!( "core::hash::Hash::hash(&{index}, state);", index = index )) .unwrap(); for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { pattern_tokens.push_str("_,"); continue; } let hash_trait = field_attribute.hash_trait; let hash_method = field_attribute.hash_method; let field_name = format!("{}", index); pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); match hash_trait { Some(hash_trait) => { let hash_method = hash_method.unwrap(); block_tokens.write_fmt(format_args!("{hash_trait}::{hash_method}(_{field_name}, state);", hash_trait = hash_trait, hash_method = hash_method, field_name = field_name)).unwrap(); } None => { match hash_method { Some(hash_method) => { block_tokens .write_fmt(format_args!( "{hash_method}(_{field_name}, state);", hash_method = hash_method, field_name = field_name )) .unwrap(); } None => { block_tokens.write_fmt(format_args!("core::hash::Hash::hash(_{field_name}, state);", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ {block_tokens} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, block_tokens = block_tokens)).unwrap(); } } } } else { for variant in data.variants.iter() { let variant_ident = variant.ident.to_string(); match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ core::hash::Hash::hash(&({enum_name}::{variant_ident} as isize), state); }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } } } match_tokens.push('}'); hasher_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let hash_impl = quote! { impl #impl_generics core::hash::Hash for #ident #ty_generics #where_clause { #[inline] #[allow(clippy::unneeded_field_pattern)] fn hash(&self, state: &mut H) { #hasher_tokens } } }; tokens.extend(hash_impl); } } educe-0.4.19/src/trait_handlers/hash/hash_struct.rs000064400000000000000000000074660072674642500204630ustar 00000000000000use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Generics, Meta}; pub struct HashStructHandler; impl TraitHandler for HashStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_hash_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut hasher_tokens = 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_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let hash_trait = field_attribute.hash_trait; let hash_method = field_attribute.hash_method; let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; match hash_trait { Some(hash_trait) => { let hash_method = hash_method.unwrap(); let statement = format!( "{hash_trait}::{hash_method}(&self.{field_name}, state);", hash_trait = hash_trait, hash_method = hash_method, field_name = field_name ); hasher_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { match hash_method { Some(hash_method) => { let statement = format!( "{hash_method}(&self.{field_name}, state);", hash_method = hash_method, field_name = field_name ); hasher_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { let statement = format!( "core::hash::Hash::hash(&self.{field_name}, state);", field_name = field_name ); hasher_tokens.extend(TokenStream::from_str(&statement).unwrap()); } } } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let hash_impl = quote! { impl #impl_generics core::hash::Hash for #ident #ty_generics #where_clause { #[inline] fn hash(&self, state: &mut H) { #hasher_tokens } } }; tokens.extend(hash_impl); } } educe-0.4.19/src/trait_handlers/hash/mod.rs000064400000000000000000000014650072674642500167040ustar 00000000000000mod models; mod hash_enum; mod hash_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use hash_enum::HashEnumHandler; use hash_struct::HashStructHandler; pub struct HashHandler; impl TraitHandler for HashHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { HashStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { HashEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::Hash), } } } educe-0.4.19/src/trait_handlers/hash/models/field_attribute.rs000064400000000000000000000332510072674642500225540ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub struct FieldAttribute { pub ignore: bool, pub hash_method: Option, pub hash_trait: Option, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_ignore: bool, pub enable_impl: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_hash_meta(&self, meta: &Meta) -> FieldAttribute { let mut ignore = false; let mut hash_method = None; let mut hash_trait = None; let correct_usage_for_hash_attribute = { let mut usage = vec![]; if self.enable_ignore { usage.push(stringify!(#[educe(Hash = false)])); usage.push(stringify!(#[educe(Hash(false))])); } usage }; let correct_usage_for_ignore = { let usage = vec![stringify!(#[educe(Hash(ignore))])]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(Hash(method = "path_to_method"))]), stringify!(#[educe(Hash(trait = "path_to_trait"))]), stringify!(#[educe(Hash(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(Hash(method("path_to_method")))]), stringify!(#[educe(Hash(trait("path_to_trait")))]), stringify!(#[educe(Hash(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; match meta { Meta::List(list) => { let mut ignore_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "ignore" => { if !self.enable_ignore { panic::unknown_parameter("Hash", meta_name.as_str()); } match meta { Meta::Path(_) => { if ignore_is_set { panic::reset_parameter(meta_name.as_str()); } ignore_is_set = true; ignore = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_ignore, ) } } } "method" => { if !self.enable_impl { panic::unknown_parameter("Hash", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if hash_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { hash_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if hash_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { hash_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("Hash", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if hash_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { hash_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if hash_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { hash_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => panic::unknown_parameter("Hash", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Hash", &correct_usage_for_hash_attribute, ) } } } } _ => panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute), } if hash_trait.is_some() && hash_method.is_none() { hash_method = Some("hash".to_string()); } FieldAttribute { ignore, hash_method, hash_trait, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Hash { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_hash_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { ignore: false, hash_method: None, hash_trait: None, }) } } educe-0.4.19/src/trait_handlers/hash/models/mod.rs000064400000000000000000000001410072674642500201550ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/hash/models/type_attribute.rs000064400000000000000000000244550072674642500224600ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::hash::Hash)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_hash_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut bound = TypeAttributeBound::None; let correct_usage_for_hash_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Hash)])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Hash(bound))]), stringify!(#[educe(Hash(bound = "where_predicates"))]), stringify!(#[educe(Hash(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("Hash", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("Hash", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Hash", &correct_usage_for_hash_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute); } flag = true; } } TypeAttribute { flag, bound, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Hash { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_hash_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, bound: TypeAttributeBound::None, }) } } educe-0.4.19/src/trait_handlers/mod.rs000064400000000000000000000053140072674642500157560ustar 00000000000000#![cfg_attr(not(feature = "default"), allow(dead_code))] #[cfg(feature = "Clone")] pub mod clone; #[cfg(feature = "Copy")] pub mod copy; #[cfg(feature = "Debug")] pub mod debug; #[cfg(feature = "Default")] pub mod default; #[cfg(feature = "Deref")] pub mod deref; #[cfg(feature = "DerefMut")] pub mod deref_mut; #[cfg(feature = "Eq")] pub mod eq; #[cfg(feature = "Hash")] pub mod hash; #[cfg(feature = "Ord")] pub mod ord; #[cfg(feature = "PartialEq")] pub mod partial_eq; #[cfg(feature = "PartialOrd")] pub mod partial_ord; use std::str::FromStr; use crate::Trait; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{ self, punctuated::Punctuated, token::Comma, DeriveInput, Expr, GenericParam, LitStr, Meta, Path, WhereClause, WherePredicate, }; pub trait TraitHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ); } #[inline] pub fn create_path_from_lit_str(s: &LitStr) -> Option { let s = s.value(); let s = s.trim(); if s.is_empty() { None } else { let tokens = TokenStream::from_str(s).unwrap(); Some(syn::parse2(tokens).unwrap()) } } #[inline] pub fn create_path_string_from_lit_str(s: &LitStr) -> Option { create_path_from_lit_str(s).map(|path| path.into_token_stream().to_string().replace(' ', "")) } #[inline] pub fn create_expr_from_lit_str(s: &LitStr) -> Option { let s = s.value(); let s = s.trim(); if s.is_empty() { None } else { let tokens = TokenStream::from_str(s).unwrap(); Some(syn::parse2(tokens).unwrap()) } } #[inline] pub fn create_expr_string_from_lit_str(s: &LitStr) -> Option { create_expr_from_lit_str(s).map(|expr| expr.into_token_stream().to_string().replace(' ', "")) } #[inline] pub fn create_where_predicates_from_lit_str( s: &LitStr, ) -> Option> { let s = s.value(); let s = s.trim(); if s.is_empty() { None } else { let s = format!("where {}", s); let tokens = TokenStream::from_str(&s).unwrap(); let where_clause: WhereClause = syn::parse2(tokens).unwrap(); Some(where_clause.predicates) } } #[inline] pub fn create_where_predicates_from_generic_parameters( p: &Punctuated, bound_trait: &Path, ) -> Punctuated { let mut where_predicates = Punctuated::new(); for param in p.iter() { if let GenericParam::Type(typ) = param { let ident = &typ.ident; where_predicates.push(syn::parse2(quote! { #ident: #bound_trait }).unwrap()); } } where_predicates } educe-0.4.19/src/trait_handlers/ord/mod.rs000064400000000000000000000014520072674642500165410ustar 00000000000000mod models; mod ord_enum; mod ord_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use ord_enum::OrdEnumHandler; use ord_struct::OrdStructHandler; pub struct OrdHandler; impl TraitHandler for OrdHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { OrdStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { OrdEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::Ord), } } } educe-0.4.19/src/trait_handlers/ord/models/field_attribute.rs000064400000000000000000000427330072674642500224220ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub struct FieldAttribute { pub ignore: bool, pub compare_method: Option, pub compare_trait: Option, pub rank: isize, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_ignore: bool, pub enable_impl: bool, pub rank: isize, pub enable_rank: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_ord_meta(&self, meta: &Meta) -> FieldAttribute { let mut ignore = false; let mut compare_method = None; let mut compare_trait = None; let mut rank = self.rank; let correct_usage_for_ord_attribute = { let mut usage = vec![]; if self.enable_ignore { usage.push(stringify!(#[educe(Ord = false)])); usage.push(stringify!(#[educe(Ord(false))])); } usage }; let correct_usage_for_ignore = { let usage = vec![stringify!(#[educe(Ord(ignore))])]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(Ord(method = "path_to_method"))]), stringify!(#[educe(Ord(trait = "path_to_trait"))]), stringify!(#[educe(Ord(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(Ord(method("path_to_method")))]), stringify!(#[educe(Ord(trait("path_to_trait")))]), stringify!(#[educe(Ord(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; let correct_usage_for_rank = { let usage = vec![ stringify!(#[educe(Ord(rank = priority_value))]), stringify!(#[educe(Ord(rank(priority_value)))]), ]; usage }; let mut rank_is_set = false; match meta { Meta::List(list) => { let mut ignore_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "ignore" => { if !self.enable_ignore { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::Path(_) => { if ignore_is_set { panic::reset_parameter(meta_name.as_str()); } ignore_is_set = true; ignore = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_ignore, ) } } } "method" => { if !self.enable_impl { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "rank" => { if !self.enable_rank { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Int(i)) => { if rank_is_set { panic::reset_parameter( meta_name.as_str(), ); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Int(i) => { if rank_is_set { panic::reset_parameter(meta_name.as_str()); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => panic::unknown_parameter("Ord", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Ord", &correct_usage_for_ord_attribute, ) } } } } _ => panic::attribute_incorrect_format("Ord", &correct_usage_for_ord_attribute), } if compare_trait.is_some() && compare_method.is_none() { compare_method = Some("cmp".to_string()); } if ignore && rank_is_set { panic::ignore_ranked_field(); } FieldAttribute { ignore, compare_method, compare_trait, rank, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Ord { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_ord_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { ignore: false, compare_method: None, compare_trait: None, rank: self.rank, }) } } educe-0.4.19/src/trait_handlers/ord/models/mod.rs000064400000000000000000000001410072674642500200160ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/ord/models/type_attribute.rs000064400000000000000000000337340072674642500223210ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::cmp::Ord)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub bound: TypeAttributeBound, pub rank: isize, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_bound: bool, pub rank: isize, pub enable_rank: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_ord_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut bound = TypeAttributeBound::None; let mut rank = self.rank; let correct_usage_for_ord_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(Ord)])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(Ord(bound))]), stringify!(#[educe(Ord(bound = "where_predicates"))]), stringify!(#[educe(Ord(bound("where_predicates")))]), ]; usage }; let correct_usage_for_rank = { let usage = vec![ stringify!(#[educe(Ord(rank = comparison_value))]), stringify!(#[educe(Ord(rank(comparison_value)))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; let mut rank_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } "rank" => { if !self.enable_rank { panic::unknown_parameter("Ord", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Int(i)) => { if rank_is_set { panic::reset_parameter( meta_name.as_str(), ); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Int(i) => { if rank_is_set { panic::reset_parameter(meta_name.as_str()); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => panic::unknown_parameter("Ord", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "Ord", &correct_usage_for_ord_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format("Ord", &correct_usage_for_ord_attribute) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format("Ord", &correct_usage_for_ord_attribute); } flag = true; } } TypeAttribute { flag, bound, rank, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::Ord { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_ord_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, bound: TypeAttributeBound::None, rank: self.rank, }) } } educe-0.4.19/src/trait_handlers/ord/ord_enum.rs000064400000000000000000000432260072674642500175770ustar 00000000000000use std::collections::BTreeMap; use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct OrdEnumHandler; impl TraitHandler for OrdEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, rank: 0, enable_rank: false, } .from_ord_meta(meta); let enum_name = ast.ident.to_string(); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); let mut has_non_unit_or_custom_value = false; if let Data::Enum(data) = &ast.data { let mut variant_values = Vec::new(); let mut variant_idents = Vec::new(); let mut variants = Vec::new(); let mut variant_to_integer = String::from("let variant_to_integer = |other: &Self| match other {"); let mut unit_to_integer = String::from("let unit_to_integer = |other: &Self| match other {"); for (index, variant) in data.variants.iter().enumerate() { let variant_attribute = TypeAttributeBuilder { enable_flag: false, enable_bound: false, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&variant.attrs, traits); let value = variant_attribute.rank; if variant_values.contains(&value) { panic::reuse_a_value(value); } if value >= 0 { has_non_unit_or_custom_value = true; } let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit unit_to_integer.write_fmt(format_args!("{enum_name}::{variant_ident} => {enum_name}::{variant_ident} as isize,", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident} => {value},", enum_name = enum_name, variant_ident = variant_ident, value = value )) .unwrap(); } Fields::Named(_) => { // TODO Struct has_non_unit_or_custom_value = true; variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident} {{ .. }} => {value},", enum_name = enum_name, variant_ident = variant_ident, value = value )) .unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple has_non_unit_or_custom_value = true; let mut pattern_tokens = String::new(); for _ in fields.unnamed.iter() { pattern_tokens.push_str("_,"); } variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident}( {pattern_tokens} ) => {value},", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, value = value )) .unwrap(); } } variant_values.push(value); variant_idents.push(variant_ident); variants.push(variant); } if has_non_unit_or_custom_value { variant_to_integer.push_str("};"); comparer_tokens.extend(TokenStream::from_str(&variant_to_integer).unwrap()); for (index, variant) in variants.into_iter().enumerate() { let variant_value = variant_values[index]; let variant_ident = &variant_idents[index]; match &variant.fields { Fields::Unit => { // TODO Unit match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ let other_value = variant_to_integer(other); return core::cmp::Ord::cmp(&{variant_value}, &other_value); }}", enum_name = enum_name, variant_ident = variant_ident, variant_value = variant_value)).unwrap(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in fields.named.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: ___{field_name},", field_name = field_name )) .unwrap(); field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("match {compare_trait}::{compare_method}({field_name}, ___{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("match {compare_method}({field_name}, ___{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("match core::cmp::Ord::cmp({field_name}, ___{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}{{ {pattern_tokens} }} => {{ if let {enum_name}::{variant_ident} {{ {pattern_2_tokens} }} = other {{ {block_tokens} }} else {{ let other_value = variant_to_integer(other); return core::cmp::Ord::cmp(&{variant_value}, &other_value); }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens, variant_value = variant_value)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); let field_name = format!("{}", index); if field_attribute.ignore { pattern_tokens.push_str("_,"); pattern_2_tokens.push_str("_,"); continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "__{field_name},", field_name = field_name )) .unwrap(); field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("match {compare_trait}::{compare_method}(_{field_name}, __{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("match {compare_method}(_{field_name}, __{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("match core::cmp::Ord::cmp(_{field_name}, __{field_name}) {{ core::cmp::Ordering::Equal => (), core::cmp::Ordering::Greater => {{ return core::cmp::Ordering::Greater; }}, core::cmp::Ordering::Less => {{ return core::cmp::Ordering::Less; }} }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ if let {enum_name}::{variant_ident} ( {pattern_2_tokens} ) = other {{ {block_tokens} }} else {{ let other_value = variant_to_integer(other); return core::cmp::Ord::cmp(&{variant_value}, &other_value); }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens, variant_value = variant_value)).unwrap(); } } } } else { unit_to_integer.push_str("};"); comparer_tokens.extend(TokenStream::from_str(&unit_to_integer).unwrap()); for (index, _) in variants.into_iter().enumerate() { let variant_ident = &variant_idents[index]; match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ let other_value = unit_to_integer(other); return core::cmp::Ord::cmp(&({enum_name}::{variant_ident} as isize), &other_value); }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } } } match_tokens.push('}'); comparer_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); if has_non_unit_or_custom_value { comparer_tokens.extend(quote!(core::cmp::Ordering::Equal)); } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::Ord for #ident #ty_generics #where_clause { #[inline] #[allow(unreachable_code, clippy::unneeded_field_pattern)] fn cmp(&self, other: &Self) -> core::cmp::Ordering { #comparer_tokens } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/ord/ord_struct.rs000064400000000000000000000115660072674642500201610ustar 00000000000000use std::collections::BTreeMap; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Generics, Meta}; pub struct OrdStructHandler; impl TraitHandler for OrdStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, rank: 0, enable_rank: false, } .from_ord_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); let statement = format!("match {compare_trait}::{compare_method}(&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; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { match compare_method { Some(compare_method) => { let statement = format!("match {compare_method}(&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; }} }}", compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { let statement = format!("match core::cmp::Ord::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; }} }}", field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } } } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::Ord for #ident #ty_generics #where_clause { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { #comparer_tokens core::cmp::Ordering::Equal } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/partial_eq/mod.rs000064400000000000000000000015600072674642500200760ustar 00000000000000mod models; mod partial_eq_enum; mod partial_eq_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use partial_eq_enum::PartialEqEnumHandler; use partial_eq_struct::PartialEqStructHandler; pub struct PartialEqHandler; impl TraitHandler for PartialEqHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { PartialEqStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { PartialEqEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::PartialEq), } } } educe-0.4.19/src/trait_handlers/partial_eq/models/field_attribute.rs000064400000000000000000000336500072674642500237550ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub struct FieldAttribute { pub ignore: bool, pub compare_method: Option, pub compare_trait: Option, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_ignore: bool, pub enable_impl: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_partial_eq_meta(&self, meta: &Meta) -> FieldAttribute { let mut ignore = false; let mut compare_method = None; let mut compare_trait = 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(false))])); } usage }; let correct_usage_for_ignore = { let usage = vec![stringify!(#[educe(PartialEq(ignore))])]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(PartialEq(method = "path_to_method"))]), stringify!(#[educe(PartialEq(trait = "path_to_trait"))]), stringify!(#[educe(PartialEq(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(PartialEq(method("path_to_method")))]), stringify!(#[educe(PartialEq(trait("path_to_trait")))]), stringify!(#[educe(PartialEq(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; match meta { Meta::List(list) => { let mut ignore_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "ignore" => { if !self.enable_ignore { panic::unknown_parameter("PartialEq", meta_name.as_str()); } match meta { Meta::Path(_) => { if ignore_is_set { panic::reset_parameter(meta_name.as_str()); } ignore_is_set = true; ignore = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_ignore, ) } } } "method" => { if !self.enable_impl { panic::unknown_parameter("PartialEq", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("PartialEq", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => panic::unknown_parameter("PartialEq", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "PartialEq", &correct_usage_for_partial_eq_attribute, ) } } } } _ => { panic::attribute_incorrect_format( "PartialEq", &correct_usage_for_partial_eq_attribute, ) } } if compare_trait.is_some() && compare_method.is_none() { compare_method = Some("eq".to_string()); } FieldAttribute { ignore, compare_method, compare_trait, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::PartialEq { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_partial_eq_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { ignore: false, compare_method: None, compare_trait: None, }) } } educe-0.4.19/src/trait_handlers/partial_eq/models/mod.rs000064400000000000000000000001410072674642500213530ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/partial_eq/models/type_attribute.rs000064400000000000000000000250110072674642500236430ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::cmp::PartialEq)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub bound: TypeAttributeBound, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_bound: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_partial_eq_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut bound = TypeAttributeBound::None; let correct_usage_for_partial_eq_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(PartialEq)])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(PartialEq(bound))]), stringify!(#[educe(PartialEq(bound = "where_predicates"))]), stringify!(#[educe(PartialEq(bound("where_predicates")))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("PartialEq", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } _ => panic::unknown_parameter("PartialEq", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "PartialEq", &correct_usage_for_partial_eq_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format( "PartialEq", &correct_usage_for_partial_eq_attribute, ) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "PartialEq", &correct_usage_for_partial_eq_attribute, ); } flag = true; } } TypeAttribute { flag, bound, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::PartialEq { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_partial_eq_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, bound: TypeAttributeBound::None, }) } } educe-0.4.19/src/trait_handlers/partial_eq/partial_eq_enum.rs000064400000000000000000000246670072674642500225010ustar 00000000000000use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct PartialEqEnumHandler; impl TraitHandler for PartialEqEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_partial_eq_meta(meta); let enum_name = ast.ident.to_string(); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); if let Data::Enum(data) = &ast.data { for variant in data.variants.iter() { let _ = TypeAttributeBuilder { enable_flag: false, enable_bound: false, } .from_attributes(&variant.attrs, traits); let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ if let {enum_name}::{variant_ident} = other {{ }} else {{ return false; }} }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = Vec::new(); let mut field_names = Vec::new(); for field in fields.named.iter() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } pattern_tokens .write_fmt(format_args!("{field_name},", field_name = field_name)) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: ___{field_name},", field_name = field_name )) .unwrap(); field_attributes.push(field_attribute); field_names.push(field_name); } for (index, field_attribute) in field_attributes.into_iter().enumerate() { let field_name = &field_names[index]; let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("if !{compare_trait}::{compare_method}({field_name}, ___{field_name}) {{ return false; }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("if !{compare_method}({field_name}, ___{field_name}) {{ return false; }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("if core::cmp::PartialEq::ne({field_name}, ___{field_name}) {{ return false; }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}{{ {pattern_tokens} }} => {{ if let {enum_name}::{variant_ident} {{ {pattern_2_tokens} }} = other {{ {block_tokens} }} else {{ return false; }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = Vec::new(); let mut field_names = Vec::new(); for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, } .from_attributes(&field.attrs, traits); let field_name = format!("{}", index); if field_attribute.ignore { pattern_tokens.push_str("_,"); pattern_2_tokens.push_str("_,"); continue; } pattern_tokens .write_fmt(format_args!("_{field_name},", field_name = field_name)) .unwrap(); pattern_2_tokens .write_fmt(format_args!("__{field_name},", field_name = field_name)) .unwrap(); field_attributes.push(field_attribute); field_names.push(field_name); } for (index, field_attribute) in field_attributes.into_iter().enumerate() { let field_name = &field_names[index]; let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("if !{compare_trait}::{compare_method}(_{field_name}, __{field_name}) {{ return false; }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("if !{compare_method}(_{field_name}, __{field_name}) {{ return false; }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("if core::cmp::PartialEq::ne(_{field_name}, __{field_name}) {{ return false; }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ if let {enum_name}::{variant_ident} ( {pattern_2_tokens} ) = other {{ {block_tokens} }} else {{ return false; }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens)).unwrap(); } } } } match_tokens.push('}'); comparer_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::PartialEq for #ident #ty_generics #where_clause { #[inline] #[allow(clippy::unneeded_field_pattern)] fn eq(&self, other: &Self) -> bool { #comparer_tokens true } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/partial_eq/partial_eq_struct.rs000064400000000000000000000072100072674642500230420ustar 00000000000000use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Generics, Meta}; pub struct PartialEqStructHandler; impl TraitHandler for PartialEqStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, } .from_partial_eq_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = 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_impl: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); let statement = format!("if !{compare_trait}::{compare_method}(&self.{field_name}, &other.{field_name}) {{ return false }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { match compare_method { Some(compare_method) => { let statement = format!("if !{compare_method}(&self.{field_name}, &other.{field_name}) {{ return false; }}", compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { let statement = format!("if core::cmp::PartialEq::ne(&self.{field_name}, &other.{field_name}) {{ return false; }}", field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } } } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::PartialEq for #ident #ty_generics #where_clause { #[inline] fn eq(&self, other: &Self) -> bool { #comparer_tokens true } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/partial_ord/mod.rs000064400000000000000000000015730072674642500202610ustar 00000000000000mod models; mod partial_ord_enum; mod partial_ord_struct; use super::TraitHandler; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use syn::{Data, DeriveInput, Meta}; use partial_ord_enum::PartialOrdEnumHandler; use partial_ord_struct::PartialOrdStructHandler; pub struct PartialOrdHandler; impl TraitHandler for PartialOrdHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { match ast.data { Data::Struct(_) => { PartialOrdStructHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Enum(_) => { PartialOrdEnumHandler::trait_meta_handler(ast, tokens, traits, meta); } Data::Union(_) => panic::trait_not_support_union(Trait::PartialOrd), } } } educe-0.4.19/src/trait_handlers/partial_ord/models/field_attribute.rs000064400000000000000000000433520072674642500241340ustar 00000000000000use super::super::super::create_path_string_from_lit_str; use crate::panic; use crate::Trait; use quote::ToTokens; use syn::{Attribute, Lit, Meta, NestedMeta}; #[derive(Debug, Clone)] pub struct FieldAttribute { pub ignore: bool, pub compare_method: Option, pub compare_trait: Option, pub rank: isize, } #[derive(Debug, Clone)] pub struct FieldAttributeBuilder { pub enable_ignore: bool, pub enable_impl: bool, pub rank: isize, pub enable_rank: bool, } impl FieldAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_partial_ord_meta(&self, meta: &Meta) -> FieldAttribute { let mut ignore = false; let mut compare_method = None; let mut compare_trait = None; let mut rank = self.rank; let correct_usage_for_partial_ord_attribute = { let mut usage = vec![]; if self.enable_ignore { usage.push(stringify!(#[educe(PartialOrd = false)])); usage.push(stringify!(#[educe(PartialOrd(false))])); } usage }; let correct_usage_for_ignore = { let usage = vec![stringify!(#[educe(PartialOrd(ignore))])]; usage }; let correct_usage_for_impl = { let usage = vec![ stringify!(#[educe(PartialOrd(method = "path_to_method"))]), stringify!(#[educe(PartialOrd(trait = "path_to_trait"))]), stringify!(#[educe(PartialOrd(trait = "path_to_trait", method = "path_to_method_in_trait"))]), stringify!(#[educe(PartialOrd(method("path_to_method")))]), stringify!(#[educe(PartialOrd(trait("path_to_trait")))]), stringify!(#[educe(PartialOrd(trait("path_to_trait"), method("path_to_method_in_trait")))]), ]; usage }; let correct_usage_for_rank = { let usage = vec![ stringify!(#[educe(PartialOrd(rank = priority_value))]), stringify!(#[educe(PartialOrd(rank(priority_value)))]), ]; usage }; let mut rank_is_set = false; match meta { Meta::List(list) => { let mut ignore_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "ignore" => { if !self.enable_ignore { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::Path(_) => { if ignore_is_set { panic::reset_parameter(meta_name.as_str()); } ignore_is_set = true; ignore = true; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_ignore, ) } } } "method" => { if !self.enable_impl { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_method.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_method.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_method = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "trait" => { if !self.enable_impl { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if compare_trait.is_some() { panic::reset_parameter( meta_name.as_str(), ); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter( meta_name.as_str(), ); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if compare_trait.is_some() { panic::reset_parameter(meta_name.as_str()); } let s = create_path_string_from_lit_str(s); if let Some(s) = s { compare_trait = Some(s); } else { panic::empty_parameter(meta_name.as_str()); } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_impl, ) } } } "rank" => { if !self.enable_rank { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Int(i)) => { if rank_is_set { panic::reset_parameter( meta_name.as_str(), ); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Int(i) => { if rank_is_set { panic::reset_parameter(meta_name.as_str()); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => panic::unknown_parameter("PartialOrd", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "PartialOrd", &correct_usage_for_partial_ord_attribute, ) } } } } _ => { panic::attribute_incorrect_format( "PartialOrd", &correct_usage_for_partial_ord_attribute, ) } } if compare_trait.is_some() && compare_method.is_none() { compare_method = Some("partial_cmp".to_string()); } if ignore && rank_is_set { panic::ignore_ranked_field(); } FieldAttribute { ignore, compare_method, compare_trait, rank, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute { let mut result = None; for attribute in attributes.iter() { let meta = attribute.parse_meta().unwrap(); let meta_name = meta.path().into_token_stream().to_string(); if meta_name.as_str() == "educe" { match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::PartialOrd { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_partial_ord_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } result.unwrap_or(FieldAttribute { ignore: false, compare_method: None, compare_trait: None, rank: self.rank, }) } } educe-0.4.19/src/trait_handlers/partial_ord/models/mod.rs000064400000000000000000000001410072674642500215320ustar 00000000000000mod field_attribute; mod type_attribute; pub use field_attribute::*; pub use type_attribute::*; educe-0.4.19/src/trait_handlers/partial_ord/models/type_attribute.rs000064400000000000000000000343600072674642500240310ustar 00000000000000use super::super::super::{ create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str, }; use crate::panic; use crate::Trait; use quote::{quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta, WherePredicate, }; #[derive(Clone)] pub enum TypeAttributeBound { None, Auto, Custom(Punctuated), } impl TypeAttributeBound { pub fn into_punctuated_where_predicates_by_generic_parameters( self, params: &Punctuated, ) -> Punctuated { match self { TypeAttributeBound::None => Punctuated::new(), TypeAttributeBound::Auto => { create_where_predicates_from_generic_parameters( params, &syn::parse2(quote!(core::cmp::PartialOrd)).unwrap(), ) } TypeAttributeBound::Custom(where_predicates) => where_predicates, } } } #[derive(Clone)] pub struct TypeAttribute { pub flag: bool, pub bound: TypeAttributeBound, pub rank: isize, } #[derive(Debug, Clone)] pub struct TypeAttributeBuilder { pub enable_flag: bool, pub enable_bound: bool, pub rank: isize, pub enable_rank: bool, } impl TypeAttributeBuilder { #[allow(clippy::wrong_self_convention)] pub fn from_partial_ord_meta(&self, meta: &Meta) -> TypeAttribute { let mut flag = false; let mut bound = TypeAttributeBound::None; let mut rank = self.rank; let correct_usage_for_partial_ord_attribute = { let mut usage = vec![]; if self.enable_flag { usage.push(stringify!(#[educe(PartialOrd)])); } usage }; let correct_usage_for_bound = { let usage = vec![ stringify!(#[educe(PartialOrd(bound))]), stringify!(#[educe(PartialOrd(bound = "where_predicates"))]), stringify!(#[educe(PartialOrd(bound("where_predicates")))]), ]; usage }; let correct_usage_for_rank = { let usage = vec![ stringify!(#[educe(PartialOrd(rank = comparison_value))]), stringify!(#[educe(PartialOrd(rank(comparison_value)))]), ]; usage }; match meta { Meta::List(list) => { let mut bound_is_set = false; let mut rank_is_set = false; for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); match meta_name.as_str() { "bound" => { if !self.enable_bound { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Str(s)) => { if bound_is_set { panic::reset_parameter( meta_name.as_str(), ); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Str(s) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; let where_predicates = create_where_predicates_from_lit_str(s); bound = match where_predicates { Some(where_predicates) => { TypeAttributeBound::Custom( where_predicates, ) } None => { panic::empty_parameter( meta_name.as_str(), ) } }; } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_bound, ) } } } Meta::Path(_) => { if bound_is_set { panic::reset_parameter(meta_name.as_str()); } bound_is_set = true; bound = TypeAttributeBound::Auto; } } } "rank" => { if !self.enable_rank { panic::unknown_parameter("PartialOrd", meta_name.as_str()); } match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Lit(Lit::Int(i)) => { if rank_is_set { panic::reset_parameter( meta_name.as_str(), ); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } } Meta::NameValue(named_value) => { let lit = &named_value.lit; match lit { Lit::Int(i) => { if rank_is_set { panic::reset_parameter(meta_name.as_str()); } rank_is_set = true; rank = i.base10_parse().unwrap(); } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => { panic::parameter_incorrect_format( meta_name.as_str(), &correct_usage_for_rank, ) } } } _ => panic::unknown_parameter("PartialOrd", meta_name.as_str()), } } _ => { panic::attribute_incorrect_format( "PartialOrd", &correct_usage_for_partial_ord_attribute, ) } } } } Meta::NameValue(_) => { panic::attribute_incorrect_format( "PartialOrd", &correct_usage_for_partial_ord_attribute, ) } Meta::Path(_) => { if !self.enable_flag { panic::attribute_incorrect_format( "PartialOrd", &correct_usage_for_partial_ord_attribute, ); } flag = true; } } TypeAttribute { flag, bound, rank, } } #[allow(clippy::wrong_self_convention)] pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute { let mut result = None; for attribute in attributes.iter() { if let Some(meta_name) = attribute.path.get_ident() { if meta_name == "educe" { let meta = attribute.parse_meta().unwrap(); match meta { Meta::List(list) => { for p in list.nested.iter() { match p { NestedMeta::Meta(meta) => { let meta_name = meta.path().into_token_stream().to_string(); let t = Trait::from_str(meta_name); if traits.binary_search(&t).is_err() { panic::trait_not_used(t); } if t == Trait::PartialOrd { if result.is_some() { panic::reuse_a_trait(t); } result = Some(self.from_partial_ord_meta(meta)); } } _ => panic::educe_format_incorrect(), } } } _ => panic::educe_format_incorrect(), } } } } result.unwrap_or(TypeAttribute { flag: false, bound: TypeAttributeBound::None, rank: self.rank, }) } } educe-0.4.19/src/trait_handlers/partial_ord/partial_ord_enum.rs000064400000000000000000000441770072674642500230350ustar 00000000000000use std::collections::BTreeMap; use std::fmt::Write; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Generics, Meta}; pub struct PartialOrdEnumHandler; impl TraitHandler for PartialOrdEnumHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, rank: 0, enable_rank: false, } .from_partial_ord_meta(meta); let enum_name = ast.ident.to_string(); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = TokenStream::new(); let mut match_tokens = String::from("match self {"); let mut has_non_unit_or_custom_value = false; if let Data::Enum(data) = &ast.data { let mut variant_values = Vec::new(); let mut variant_idents = Vec::new(); let mut variants = Vec::new(); let mut variant_to_integer = String::from("let variant_to_integer = |other: &Self| match other {"); let mut unit_to_integer = String::from("let unit_to_integer = |other: &Self| match other {"); for (index, variant) in data.variants.iter().enumerate() { let variant_attribute = TypeAttributeBuilder { enable_flag: false, enable_bound: false, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&variant.attrs, traits); let value = variant_attribute.rank; if variant_values.contains(&value) { panic::reuse_a_value(value); } if value >= 0 { has_non_unit_or_custom_value = true; } let variant_ident = variant.ident.to_string(); match &variant.fields { Fields::Unit => { // TODO Unit unit_to_integer.write_fmt(format_args!("{enum_name}::{variant_ident} => {enum_name}::{variant_ident} as isize,", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident} => {value},", enum_name = enum_name, variant_ident = variant_ident, value = value )) .unwrap(); } Fields::Named(_) => { // TODO Struct has_non_unit_or_custom_value = true; variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident} {{ .. }} => {value},", enum_name = enum_name, variant_ident = variant_ident, value = value )) .unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple has_non_unit_or_custom_value = true; let mut pattern_tokens = String::new(); for _ in fields.unnamed.iter() { pattern_tokens.push_str("_,"); } variant_to_integer .write_fmt(format_args!( "{enum_name}::{variant_ident}( {pattern_tokens} ) => {value},", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, value = value )) .unwrap(); } } variant_values.push(value); variant_idents.push(variant_ident); variants.push(variant); } if has_non_unit_or_custom_value { variant_to_integer.push_str("};"); comparer_tokens.extend(TokenStream::from_str(&variant_to_integer).unwrap()); for (index, variant) in variants.into_iter().enumerate() { let variant_value = variant_values[index]; let variant_ident = &variant_idents[index]; match &variant.fields { Fields::Unit => { // TODO Unit match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ let other_value = variant_to_integer(other); return core::cmp::PartialOrd::partial_cmp(&{variant_value}, &other_value); }}", enum_name = enum_name, variant_ident = variant_ident, variant_value = variant_value)).unwrap(); } Fields::Named(fields) => { // TODO Struct let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in fields.named.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); let field_name = field.ident.as_ref().unwrap().to_string(); if field_attribute.ignore { pattern_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: _,", field_name = field_name )) .unwrap(); continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } pattern_tokens .write_fmt(format_args!( "{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "{field_name}: ___{field_name},", field_name = field_name )) .unwrap(); field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("match {compare_trait}::{compare_method}({field_name}, ___{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("match {compare_method}({field_name}, ___{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("match core::cmp::PartialOrd::partial_cmp({field_name}, ___{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}{{ {pattern_tokens} }} => {{ if let {enum_name}::{variant_ident} {{ {pattern_2_tokens} }} = other {{ {block_tokens} }} else {{ let other_value = variant_to_integer(other); return core::cmp::PartialOrd::partial_cmp(&{variant_value}, &other_value); }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens, variant_value = variant_value)).unwrap(); } Fields::Unnamed(fields) => { // TODO Tuple let mut pattern_tokens = String::new(); let mut pattern_2_tokens = String::new(); let mut block_tokens = String::new(); let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in fields.unnamed.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); let field_name = format!("{}", index); if field_attribute.ignore { pattern_tokens.push_str("_,"); pattern_2_tokens.push_str("_,"); continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } pattern_tokens .write_fmt(format_args!( "_{field_name},", field_name = field_name )) .unwrap(); pattern_2_tokens .write_fmt(format_args!( "__{field_name},", field_name = field_name )) .unwrap(); field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); block_tokens.write_fmt(format_args!("match {compare_trait}::{compare_method}(_{field_name}, __{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name)).unwrap(); } None => { match compare_method { Some(compare_method) => { block_tokens.write_fmt(format_args!("match {compare_method}(_{field_name}, __{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_method = compare_method, field_name = field_name)).unwrap(); } None => { block_tokens.write_fmt(format_args!("match core::cmp::PartialOrd::partial_cmp(_{field_name}, __{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", field_name = field_name)).unwrap(); } } } } } match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ if let {enum_name}::{variant_ident} ( {pattern_2_tokens} ) = other {{ {block_tokens} }} else {{ let other_value = variant_to_integer(other); return core::cmp::PartialOrd::partial_cmp(&{variant_value}, &other_value); }} }}", enum_name = enum_name, variant_ident = variant_ident, pattern_tokens = pattern_tokens, pattern_2_tokens = pattern_2_tokens, block_tokens = block_tokens, variant_value = variant_value)).unwrap(); } } } } else { unit_to_integer.push_str("};"); comparer_tokens.extend(TokenStream::from_str(&unit_to_integer).unwrap()); for (index, _) in variants.into_iter().enumerate() { let variant_ident = &variant_idents[index]; match_tokens.write_fmt(format_args!("{enum_name}::{variant_ident} => {{ let other_value = unit_to_integer(other); return core::cmp::PartialOrd::partial_cmp(&({enum_name}::{variant_ident} as isize), &other_value); }}", enum_name = enum_name, variant_ident = variant_ident)).unwrap(); } } } match_tokens.push('}'); comparer_tokens.extend(TokenStream::from_str(&match_tokens).unwrap()); if has_non_unit_or_custom_value { comparer_tokens.extend(quote!(Some(core::cmp::Ordering::Equal))); } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::PartialOrd for #ident #ty_generics #where_clause { #[inline] #[allow(unreachable_code, clippy::unneeded_field_pattern)] fn partial_cmp(&self, other: &Self) -> Option { #comparer_tokens } } }; tokens.extend(compare_impl); } } educe-0.4.19/src/trait_handlers/partial_ord/partial_ord_struct.rs000064400000000000000000000121460072674642500234040ustar 00000000000000use std::collections::BTreeMap; use std::str::FromStr; use super::super::TraitHandler; use super::models::{FieldAttributeBuilder, TypeAttributeBuilder}; use crate::panic; use crate::Trait; use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Generics, Meta}; pub struct PartialOrdStructHandler; impl TraitHandler for PartialOrdStructHandler { fn trait_meta_handler( ast: &DeriveInput, tokens: &mut TokenStream, traits: &[Trait], meta: &Meta, ) { let type_attribute = TypeAttributeBuilder { enable_flag: true, enable_bound: true, rank: 0, enable_rank: false, } .from_partial_ord_meta(meta); let bound = type_attribute .bound .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params); let mut comparer_tokens = TokenStream::new(); if let Data::Struct(data) = &ast.data { let mut field_attributes = BTreeMap::new(); let mut field_names = BTreeMap::new(); for (index, field) in data.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_ignore: true, enable_impl: true, rank: isize::min_value() + index as isize, enable_rank: true, } .from_attributes(&field.attrs, traits); if field_attribute.ignore { continue; } let rank = field_attribute.rank; if field_attributes.contains_key(&rank) { panic::reuse_a_rank(rank); } let field_name = if let Some(ident) = field.ident.as_ref() { ident.to_string() } else { format!("{}", index) }; field_attributes.insert(rank, field_attribute); field_names.insert(rank, field_name); } for (index, field_attribute) in field_attributes { let field_name = field_names.get(&index).unwrap(); let compare_trait = field_attribute.compare_trait; let compare_method = field_attribute.compare_method; match compare_trait { Some(compare_trait) => { let compare_method = compare_method.unwrap(); let statement = format!("match {compare_trait}::{compare_method}(&self.{field_name}, &other.{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_trait = compare_trait, compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { match compare_method { Some(compare_method) => { let statement = format!("match {compare_method}(&self.{field_name}, &other.{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", compare_method = compare_method, field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } None => { let statement = format!("match core::cmp::PartialOrd::partial_cmp(&self.{field_name}, &other.{field_name}) {{ Some(core::cmp::Ordering::Equal) => (), Some(core::cmp::Ordering::Greater) => {{ return Some(core::cmp::Ordering::Greater); }}, Some(core::cmp::Ordering::Less) => {{ return Some(core::cmp::Ordering::Less); }}, None => {{ return None; }} }}", field_name = field_name); comparer_tokens.extend(TokenStream::from_str(&statement).unwrap()); } } } } } } let ident = &ast.ident; let mut generics_cloned: Generics = ast.generics.clone(); let where_clause = generics_cloned.make_where_clause(); for where_predicate in bound { where_clause.predicates.push(where_predicate); } let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl(); let compare_impl = quote! { impl #impl_generics core::cmp::PartialOrd for #ident #ty_generics #where_clause { #[inline] fn partial_cmp(&self, other: &Self) -> Option { #comparer_tokens Some(core::cmp::Ordering::Equal) } } }; tokens.extend(compare_impl); } }