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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
//! # Kinded
//!
//! Generate Rust enum kind types without boilerplate.
//!
//! ## Get Started
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! enum Drink {
//! Mate,
//! Coffee(String),
//! Tea { variety: String, caffeine: bool }
//! }
//!
//! let drink = Drink::Coffee("Espresso".to_owned());
//! assert_eq!(drink.kind(), DrinkKind::Coffee);
//! ```
//!
//! Note, the definition of `DrinkKind` enum is generated automatically as well as `Drink::kind()` method.
//! To put it simply you get something similar to the following:
//!
//! ```ignore
//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
//! enum DrinkKind {
//! Mate,
//! Coffee,
//! Tea
//! }
//!
//! impl Drink {
//! const fn kind(&self) -> DrinkKind {
//! match self {
//! Drink::Mate => DrinkKind::Mate,
//! Drink::Coffee(..) => DrinkKind::Coffee,
//! Drink::Tea { .. } => DrinkKind::Tea,
//! }
//! }
//! }
//! ```
//!
//! ## Const context
//!
//! The `kind()` method is a `const fn`, so it can be used in const contexts:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! enum Status {
//! Active,
//! Inactive,
//! }
//!
//! const ACTIVE_KIND: StatusKind = Status::Active.kind();
//! ```
//!
//! ## Kinded trait
//!
//! The library provides `Kinded` trait:
//!
//! ```ignore
//! pub trait Kinded {
//! type Kind: PartialEq + Eq + Debug + Clone + Copy;
//!
//! fn kind(&self) -> Self::Kind;
//! }
//! ```
//!
//! From the example above, the derived implementation of `Kinded` for `Drink` resembles the following:
//!
//! ```ignore
//! impl Kinded for Drink {
//! type Kind = DrinkKind;
//!
//! fn kind(&self) -> DrinkKind { /* implementation */ }
//! }
//! ```
//!
//! The `Kinded` trait allows to build abstract functions that can be used with different enum types.
//!
//! ## Get all kind variants
//!
//! The kind type gets implementation of `::all()` associated function, which returns a vector with all kind variants:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! enum Drink {
//! Mate,
//! Coffee(String),
//! Tea { variety: String, caffeine: bool }
//! }
//!
//! assert_eq!(DrinkKind::all(), [DrinkKind::Mate, DrinkKind::Coffee, DrinkKind::Tea]);
//! ```
//!
//! ## Attributes
//!
//! ### Custom kind type name
//!
//! By default the kind type name is generated by adding postfix `Kind` to the original enum name.
//! This can be customized with `kind = ` attribute:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(kind = SimpleDrink)]
//! enum Drink {
//! Mate,
//! Coffee(String),
//! Tea { variety: String, caffeine: bool }
//! }
//!
//! assert_eq!(Drink::Mate.kind(), SimpleDrink::Mate);
//! ```
//!
//! ### Derive traits
//!
//! By default the kind type implements the following traits: `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Display`, `FromStr`, `From<T>`, `From<&T>`.
//!
//! Extra traits can be derived with `derive(..)` attribute:
//!
//! ```
//! use kinded::Kinded;
//! use std::collections::HashSet;
//!
//! #[derive(Kinded)]
//! #[kinded(derive(Hash))]
//! enum Drink {
//! Mate,
//! Coffee(String),
//! Tea { variety: String, caffeine: bool }
//! }
//!
//! let mut drink_kinds = HashSet::new();
//! drink_kinds.insert(DrinkKind::Mate);
//! ```
//!
//! ### Skip default traits
//!
//! In some cases you may need to opt out of default trait implementations.
//! For example, when using `kinded` with crates like `enumset` that provide their own trait implementations,
//! you can use `skip_derive(..)` to avoid conflicts:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(skip_derive(Display, FromStr))]
//! enum Task {
//! Download { url: String },
//! Process(Vec<u8>),
//! }
//!
//! // Display and FromStr are not implemented for TaskKind
//! // You can provide your own custom implementations if needed
//! ```
//!
//! The following traits can be skipped:
//! - Derived traits: `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`
//! - Implemented traits: `Display`, `FromStr`, `From`
//!
//! You can combine `skip_derive` with `derive` to replace default traits:
//!
//! ```
//! #[derive(kinded::Kinded)]
//! #[kinded(skip_derive(Display), derive(Hash))]
//! enum Expr {
//! Literal(i64),
//! Variable(String),
//! }
//! ```
//!
//! ### Generic attributes
//!
//! If you're using derive traits from other libraries like Serde or Sqlx, you might want to add
//! extra attributes specific to those libraries. You can add these by using the `attrs` attribute:
//!
//! ```ignore
//! use kinded::Kinded;
//! use serde::Serialize;
//! use serde_json::json;
//!
//! #[derive(Kinded, Serialize)]
//! #[kinded(display = "snake_case", attrs(serde(rename_all = "snake_case")))]
//! enum Drink {
//! VeryHotBlackTea,
//! Milk { fat: f64 },
//! }
//!
//! let json_value = serde_json::to_value(&DrinkKind::VeryHotBlackTea);
//! assert_eq!(json_value, json!("very_hot_black_tea"));
//! ```
//!
//! ### Variant attributes
//!
//! You can also apply attributes to individual variants of the generated kind enum:
//!
//! ```ignore
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(derive(Default))]
//! enum Priority {
//! Low,
//! #[kinded(attrs(default))]
//! Medium,
//! High,
//! }
//!
//! // Medium is the default
//! assert_eq!(PriorityKind::default(), PriorityKind::Medium);
//! ```
//!
//! You can combine `attrs` with `rename` on the same variant:
//!
//! ```ignore
//! #[derive(Kinded)]
//! #[kinded(derive(Default))]
//! enum Level {
//! #[kinded(rename = "low_level", attrs(default))]
//! Low,
//! Medium,
//! }
//! ```
//!
//! ### Customize Display trait
//!
//! Implementation of `Display` trait can be customized in the `serde` fashion:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(display = "snake_case")]
//! enum Drink {
//! VeryHotBlackTea,
//! Milk { fat: f64 },
//! }
//!
//! let tea = DrinkKind::VeryHotBlackTea;
//! assert_eq!(tea.to_string(), "very_hot_black_tea");
//! ```
//!
//! ### FromStr trait
//!
//! The kind type implements `FromStr` trait. The implementation tries it's best to parse, checking all the possible cases mentioned above.
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(display = "snake_case")]
//! enum Drink {
//! VeryHotBlackTea,
//! Milk { fat: f64 },
//! }
//!
//! assert_eq!(
//! "VERY_HOT_BLACK_TEA".parse::<DrinkKind>().unwrap(),
//! DrinkKind::VeryHotBlackTea
//! );
//!
//! assert_eq!(
//! "veryhotblacktea".parse::<DrinkKind>().unwrap(),
//! DrinkKind::VeryHotBlackTea
//! );
//! ```
//!
//! The possible values are `"snake_case"`, `"camelCase"`, `"PascalCase"`, `"SCREAMING_SNAKE_CASE"`, `"kebab-case"`, `"SCREAMING-KEBAB-CASE"`, `"Title Case"`, `"lowercase"`, `"UPPERCASE"`.
//!
//! ### Rename variants
//!
//! Individual variants can have custom display/parse names using the `rename` attribute.
//! This is useful when the automatic case conversion doesn't produce the desired result:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! #[kinded(display = "snake_case")]
//! enum Validator {
//! NotEmpty,
//! // Without rename, this would display as "len_utf_16_min" (with extra underscore)
//! #[kinded(rename = "len_utf16_min")]
//! LenUtf16Min,
//! #[kinded(rename = "len_utf16_max")]
//! LenUtf16Max,
//! }
//!
//! assert_eq!(ValidatorKind::NotEmpty.to_string(), "not_empty");
//! assert_eq!(ValidatorKind::LenUtf16Min.to_string(), "len_utf16_min");
//! assert_eq!(ValidatorKind::LenUtf16Max.to_string(), "len_utf16_max");
//!
//! // Parsing also works with the renamed values
//! assert_eq!("len_utf16_min".parse::<ValidatorKind>().unwrap(), ValidatorKind::LenUtf16Min);
//! ```
//!
//! Note: The original variant name and its case alternatives can still be parsed (e.g., `"LenUtf16Min"`, `"len_utf_16_min"`).
//!
//! ## A note about the war in Ukraine 🇺🇦
//!
//! Today I live in Berlin, I have the luxury to live a physically safe life.
//! But I am Ukrainian. The first 25 years of my life I spent in [Kharkiv](https://en.wikipedia.org/wiki/Kharkiv),
//! the second-largest city in Ukraine, 60km away from the border with russia. Today about [a third of my home city is destroyed](https://www.youtube.com/watch?v=ihoufBFSZds) by russians.
//! My parents, my relatives and my friends had to survive the artillery and air attack, living for over a month in basements.
//!
//! Some of them have managed to evacuate to EU. Some others are trying to live "normal lives" in Kharkiv, doing there daily duties.
//! And some are at the front line right now, risking their lives every second to protect the rest.
//!
//! I encourage you to donate to [Charity foundation of Serhiy Prytula](https://prytulafoundation.org/en).
//! Just pick the project you like and donate. This is one of the best-known foundations, you can watch a [little documentary](https://www.youtube.com/watch?v=VlmWqoeub1Q) about it.
//! Your contribution to the Ukrainian military force is a contribution to my calmness, so I can spend more time developing the project.
//!
//! Thank you.
//!
//!
//! ## License
//!
//! MIT © [Serhii Potapov](https://www.greyblake.com)
pub use ParseKindError;
pub use Kinded;
pub use ;