kinded 0.5.0

Generate enums with same variants, but without data.
Documentation
//! # 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)

#![no_std]

mod errors;
mod traits;

pub use errors::ParseKindError;
pub use kinded_macros::Kinded;
pub use traits::{Kind, Kinded};