1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! # partial-cmp-derive
//!
//! A procedural macro crate for deriving `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash`
//! with fine-grained control over field comparison and hashing behavior.
//!
//! ## Features
//!
//! - **Skip fields**: Use `#[ord(skip)]` to exclude fields from all comparisons and hashing.
//! Skipped fields are ignored in equality, ordering, and hash computations.
//! - **Sort order**: Use `#[ord(order = "asc")]` or `#[ord(order = "desc")]` per field
//! - **Explicit ordering**: Use `#[ord(by = [field1(desc), field2(asc)])]` at struct level
//! - **Field priority**: Use `#[ord(priority = N)]` for implicit ordering (lower = first)
//! - **Key extraction**: Use `#[ord(key = "path::to::fn")]` to extract a comparable key
//! - **Reverse all**: Use `#[ord(reverse)]` at struct level to reverse entire comparison
//! - **Enum ranking**: Use `#[ord(rank = N)]` to control variant ordering
//! - **Option handling**: Use `#[ord(none_order = "first")]` or `"last"`
//! - **Trait selection**: Control which traits are generated with skip flags
//!
//! ## Trait Generation
//!
//! By default, all five traits are generated: `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash`.
//! The `Hash` implementation is consistent with `Eq`, ensuring the invariant
//! `a == b -> hash(a) == hash(b)` holds. You can opt out of specific traits:
//!
//! - `#[ord(skip_partial_eq)]` — Don't generate `PartialEq` (implies no other traits)
//! - `#[ord(skip_eq)]` — Don't generate `Eq` (also disables `Ord`)
//! - `#[ord(skip_partial_ord)]` — Don't generate `PartialOrd` (also disables `Ord`)
//! - `#[ord(skip_ord)]` — Don't generate `Ord`
//! - `#[ord(skip_hash)]` — Don't generate `Hash`
//!
//! ## Example
//!
//! ```rust
//! use partial_cmp_derive::PartialCmp;
//!
//! // Generates PartialEq, Eq, PartialOrd, Ord, and Hash
//! #[derive(PartialCmp)]
//! struct Point {
//! x: i32,
//! y: i32,
//! }
//!
//! // Skipped fields are excluded from all comparisons and hashing
//! // Use skip_eq, skip_ord, and skip_hash when non-skipped fields don't implement those traits
//! #[derive(Debug, PartialCmp)]
//! #[ord(skip_eq, skip_ord, skip_hash)]
//! struct Measurement {
//! #[ord(skip)]
//! raw_value: f32, // Ignored in eq, cmp, and hash
//! timestamp: u64,
//! }
//!
//! // Compare by absolute value using a key function
//! #[derive(Debug, PartialCmp)]
//! struct AbsValue {
//! #[ord(key = "abs_key")]
//! value: i32,
//! }
//!
//! fn abs_key(v: &i32) -> i32 {
//! v.abs()
//! }
//! ```
//!
//! This generates consistent implementations where skipped fields are ignored
//! in equality, ordering, and hash computations.
//!
//! ## Key Extraction
//!
//! The `key` attribute allows you to specify a function that extracts a comparable
//! value from a field. This single function is used for `Eq`, `Ord`, and `Hash`,
//! ensuring consistency across all three traits automatically:
//!
//! ```rust
//! use partial_cmp_derive::PartialCmp;
//!
//! fn abs_key(v: &i32) -> i32 {
//! v.abs()
//! }
//!
//! #[derive(Debug, PartialCmp)]
//! struct AbsValue {
//! #[ord(key = "abs_key")]
//! value: i32,
//! }
//!
//! let a = AbsValue { value: -5 };
//! let b = AbsValue { value: 5 };
//!
//! // Both are equal because abs(-5) == abs(5)
//! assert_eq!(a, b);
//! // And their hashes are equal too (Hash/Eq invariant maintained)
//! ```
//!
//! The key function signature should be `fn(&T) -> U` where `U: Ord + Hash`.
use FromDeriveInput;
use TokenStream;
use ;
use expand_derive;
use OrdDerive;
/// Derives comparison and hash traits with customizable field behavior.
///
/// By default, this macro generates implementations for `PartialEq`, `Eq`,
/// `PartialOrd`, `Ord`, and `Hash`. All traits respect the same field configuration,
/// ensuring consistent behavior.
///
/// # Struct-Level Attributes
///
/// - `#[ord(reverse)]` — Reverses the final comparison result
/// - `#[ord(by = [field1(asc), field2(desc)])]` — Explicit field comparison order
/// - `#[ord(skip_partial_eq)]` — Don't generate `PartialEq` (disables all other traits too)
/// - `#[ord(skip_eq)]` — Don't generate `Eq` (also disables `Ord`)
/// - `#[ord(skip_partial_ord)]` — Don't generate `PartialOrd` (also disables `Ord`)
/// - `#[ord(skip_ord)]` — Don't generate `Ord`
/// - `#[ord(skip_hash)]` — Don't generate `Hash`
///
/// # Field-Level Attributes
///
/// - `#[ord(skip)]` — Exclude this field from all comparisons and hashing
/// - `#[ord(order = "asc")]` or `#[ord(order = "desc")]` — Sort direction
/// - `#[ord(priority = N)]` — Comparison priority (lower = compared first)
/// - `#[ord(key = "path::to::fn")]` — Key extraction function (signature: `fn(&T) -> U`)
/// - `#[ord(none_order = "first")]` or `#[ord(none_order = "last")]` — Option handling
///
/// # Variant-Level Attributes (Enums)
///
/// - `#[ord(rank = N)]` — Explicit variant ranking (lower = less than)
///
/// # Key Extraction
///
/// The `key` attribute specifies a function that extracts a comparable/hashable value
/// from a field. The extracted key is used consistently for `Eq`, `Ord`, and `Hash`,
/// ensuring the invariant `a == b -> hash(a) == hash(b)` is maintained.
///
/// ```rust
/// use partial_cmp_derive::PartialCmp;
///
/// fn abs_key(v: &i32) -> i32 {
/// v.abs()
/// }
///
/// #[derive(Debug, PartialCmp)]
/// struct AbsValue {
/// #[ord(key = "abs_key")]
/// value: i32,
/// }
///
/// let a = AbsValue { value: -5 };
/// let b = AbsValue { value: 5 };
///
/// assert_eq!(a, b); // Equal because abs(-5) == abs(5)
/// ```
///
/// # Example
///
/// ```rust
/// use partial_cmp_derive::PartialCmp;
///
/// #[derive(Debug, PartialCmp)]
/// #[ord(by = [score(desc), name(asc)])]
/// struct Player {
/// id: u64, // Not compared (not in `by` list)
/// name: String,
/// score: u32,
/// }
/// ```